ADR-0014: 본문에 Pretendard, 코드에 JetBrains Mono 를 쓰고 자체 호스팅한다
상태
Accepted
날짜
2026-08-12
맥락
폰트를 지정하지 않은 상태였습니다. 전역 스타일의 --font-sans 는 Spartan 생성기가 넣은 시스템 폰트 스택이며 한글 글리프를 가진 폰트가 하나도 명시되어 있지 않습니다.
그 결과 한글은 브라우저 폴백으로 렌더링됩니다. Windows 는 맑은 고딕, macOS 는 Apple SD 산돌고딕, 그 외 환경은 시스템 설정을 따릅니다. 같은 화면이 기기마다 다른 글꼴로 보이며, 자간과 행간이 달라 줄바꿈 위치까지 갈립니다.
ADR-0007에서 네이티브 위임을 기각한 사유가 "같은 화면이 기기마다 다르게 보이는 것을 허용할 수 없다"였습니다. 폰트를 OS 에 맡기는 것은 같은 문제를 타이포그래피에서 되풀이하는 것입니다.
--font-mono 는 값 자체가 없어 코드 블록이 브라우저 기본 고정폭 폰트로 표시됩니다.
결정
| 용도 | 폰트 | 비고 |
|---|---|---|
| 본문 | Pretendard Variable | 한글과 라틴을 모두 포함합니다 |
| 코드 | JetBrains Mono Variable | 한글은 Pretendard 로 폴백됩니다 |
본문에 영문 전용 폰트를 겹치지 않습니다. Pretendard 는 라틴 글리프를 Inter 에서 가져와 본고딕 기반 한글과 어울리도록 다듬은 폰트이므로, 다른 영문 폰트를 얹으면 그 조정이 무효가 되고 한 문장 안에서 x-height 와 획 두께가 어긋납니다.
두 폰트 모두 npm 패키지로 설치해 자체 호스팅합니다. 빌드가 CSS 의 url() 을 따라 자산을 산출물에 포함시키므로 별도 복사 설정이 필요 없습니다.
로딩 방식은 다음과 같습니다.
| 항목 | 선택 | 사유 |
|---|---|---|
| 분할 | unicode-range 동적 서브셋 |
실제로 쓰는 범위만 내려받습니다 |
| 두께 | 가변 폰트 | 두께마다 파일을 두지 않습니다 |
| 표시 정책 | font-display: swap |
폰트 대기 중 텍스트가 보이지 않는 구간을 없앱니다 |
preload |
적용하지 않음 | 아래에 사유를 기술합니다 |
preload 를 적용하지 않는 이유는 동적 서브셋과 맞지 않기 때문입니다. Pretendard 는 한글을 92 개 범위로 쪼개어 배포하며 어느 조각이 필요한지는 화면에 실제로 나타난 글자에 따라 달라집니다. 특정 조각을 미리 지정하면 그 화면에 쓰이지 않는 파일을 받게 되고, 반대로 필요한 조각은 여전히 늦게 요청됩니다.
검토한 대안
시스템 폰트 스택 유지
| 구분 | 내용 |
|---|---|
| 장점 | 내려받을 파일이 없어 첫 렌더가 가장 빠릅니다. 산출물 크기가 늘지 않습니다 |
| 단점 | 렌더링 결과가 OS 에 종속됩니다. 디자인 검토와 실제 사용 환경이 어긋납니다 |
| 기각 사유 | 화면 통제를 포기하는 것이며 ADR-0007 에서 같은 이유로 네이티브 위임을 기각했습니다 |
CDN 로드 (구글 폰트 등)
| 구분 | 내용 |
|---|---|
| 장점 | 설정이 짧고 산출물이 커지지 않습니다. 방문자가 이미 캐시를 가졌을 가능성이 있습니다 |
| 단점 | 추가 연결이 생기고 CSP 허용 목록이 넓어집니다. 서드파티 가용성에 렌더링이 묶입니다. 브라우저의 캐시 파티셔닝으로 도메인 간 폰트 캐시 공유는 더 이상 동작하지 않습니다 |
| 기각 사유 | 성능 4절이 이미 자체 호스팅을 규정하고 있으며, 그 규칙의 근거를 본 ADR 이 기록합니다 |
통짜 가변 폰트 한 파일
| 구분 | 내용 |
|---|---|
| 장점 | @font-face 선언이 하나여서 CSS 가 작아집니다. 요청 수가 적습니다 |
| 단점 | Pretendard 통짜 가변 폰트가 2.0MB 입니다. 첫 방문에 전량을 내려받습니다 |
| 기각 사유 | 한글 문서가 실제로 쓰는 글자는 전체 자모 조합의 일부입니다. 쓰지 않는 글리프를 받는 비용이 @font-face 선언 92 개의 CSS 증가분보다 큽니다 |
코드 폰트로 D2Coding 등 한글 포함 고정폭 사용
| 구분 | 내용 |
|---|---|
| 장점 | 한글도 고정폭이라 디렉터리 구조와 한글 주석이 섞인 코드 블록에서 열이 정확히 맞습니다 |
| 단점 | 라틴 자형이 최근 코딩 폰트보다 둔하고 0 과 O, 1 과 l 의 구분이 덜 뚜렷합니다 |
| 기각 사유 | 코드 블록에서 더 자주 읽는 것이 라틴 식별자이므로 라틴 가독성을 우선했습니다 |
결과
렌더링이 OS 와 무관하게 동일해지고 외부 연결이 하나도 생기지 않습니다. 디자인 검토에서 본 화면이 사용자 화면과 같아집니다.
감수하는 사항은 다음과 같습니다.
- 한글이 섞인 코드 블록에서 열 정렬이 어긋납니다. JetBrains Mono 가 한글을 담지 않아 한글만 Pretendard 로 떨어지며 폭이 고정폭 배수와 맞지 않습니다. 본 저장소 문서 기준으로 코드 블록 439 줄 중 109 줄(25%)이 해당합니다. 대응은 아래 개정 절에 있습니다.
- 전역 스타일이 커집니다.
@font-face선언 98 개가 추가되어 CSS 원본이 27kB 에서 78kB 로 늘었습니다. 압축 전송 기준으로는 5kB 에서 14kB 입니다. - 산출물에 폰트 3.2MB 가 포함됩니다. 배포 용량이 늘어나며, 방문자가 실제로 받는 양은 그중 일부입니다.
- 첫 렌더에 폰트 교체가 보입니다.
font-display: swap의 직접적 결과이며, 텍스트가 보이지 않는 구간을 없애는 대가로 받아들입니다.
개정
2026-08-12 — 정렬이 의미를 갖는 블록의 대응 확정
검토 대상으로 남겨 두었던 항목을 처리했습니다. 디렉터리 구조 블록 11 개가 항목 뒤에 공백을 넣어 설명을 정렬하고 있었고, 전부 화면에서 어긋난 상태였습니다.
세 가지로 정리했습니다.
| 대상 | 처리 |
|---|---|
| 계층 표현 | tree 기호(├──·└──·│)로 바꿉니다. 고정폭 글꼴이 담는 문자이므로 왼쪽 구조는 정확히 그려집니다 |
| 항목 설명 셋 이상 | 블록 밖 표로 분리합니다 |
| 항목 설명 둘 이하 | 블록 밖 본문 문장으로 서술합니다 |
코드 블록 안에서 공백으로 열을 맞추는 것을 금지하는 규칙은 개발 환경 5.3절이 원본입니다. 폰트 선택 자체는 바뀌지 않으므로 신규 ADR 을 발행하지 않습니다.
2026-08-13 — 로고 전용 서체를 예외로 추가
상단 바의 사이트 이름 하나에 Caveat 500 을 씁니다. 본문과 코드 두 종으로 고정한다는 결정의 예외이며 사용처를 이 한 곳으로 한정합니다.
예외를 두는 이유는 이 이름이 옮겨 오기 전 블로그의 시각 정체성이고 서체가 곧 그 내용의 일부이기 때문입니다. 본문 서체로 대체하면 같은 글자가 다른 것을 가리키게 됩니다. 본문·제목·코드 어디에도 쓰지 않으므로 두 종 고정이 노리던 것, 즉 화면마다 서체가 갈리는 상황은 발생하지 않습니다.
설치와 로드 방식은 기존과 같습니다. npm 패키지로 자체 호스팅하고 unicode-range 동적 서브셋을 따릅니다. 굵기는 로고가 쓰는 500 하나만 가져옵니다.
| 항목 | 값 |
|---|---|
| 방문자가 받는 양 | 50.9kB (라틴 woff2 하나) |
| 산출물 증가 | 313kB (4 개 서브셋 × woff·woff2) |
로고에 실제로 필요한 글자는 10 자뿐이므로 받는 양의 대부분이 쓰이지 않습니다. 글자 단위로 서브셋을 만들면 수 kB 로 줄지만, 그러려면 npm 패키지를 그대로 쓰는 방식을 버리고 빌드에 폰트 가공 단계를 들여야 합니다. 로고 하나를 위해 파이프라인을 늘리지 않는 쪽을 택했으며, 폰트 총량이 문제가 되면 이 항목이 첫 번째 검토 대상입니다.