본문으로 건너뛰기

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 등 한글 포함 고정폭 사용

구분 내용
장점 한글도 고정폭이라 디렉터리 구조와 한글 주석이 섞인 코드 블록에서 열이 정확히 맞습니다
단점 라틴 자형이 최근 코딩 폰트보다 둔하고 0O, 1l 의 구분이 덜 뚜렷합니다
기각 사유 코드 블록에서 더 자주 읽는 것이 라틴 식별자이므로 라틴 가독성을 우선했습니다

결과

렌더링이 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 패키지를 그대로 쓰는 방식을 버리고 빌드에 폰트 가공 단계를 들여야 합니다. 로고 하나를 위해 파이프라인을 늘리지 않는 쪽을 택했으며, 폰트 총량이 문제가 되면 이 항목이 첫 번째 검토 대상입니다.

© 2026 dev.goraebap