release: v0.11.0

This commit is contained in:
Yun Chan 2026-09-12 15:28:46 +09:00
parent 3c39a1731d
commit 45ba92ea82
45 changed files with 1459 additions and 1887 deletions

View file

@ -22,9 +22,7 @@
### 1-1. 먼저 개수를 정한다
**두 벌이면 충분하다.** 디스플레이 하나, 본문 하나. 코드나 수치가 있으면 모노 하나를 더한다.
세 번째 텍스트 폰트를 넣고 싶다면 **위계를 웨이트로 못 만들고 있다는 신호다.** 가변 폰트 한 벌의 300~700 구간이 서로 다른 폰트 두 벌보다 넓은 표현을 준다.
디스플레이 하나와 본문 하나, 필요할 때 모노 하나는 관리하기 쉬운 시작점이다. 추가 패밀리는 언어 범위·역할·전송량·라이선스와 실제 위계 효과를 근거로 제한한다. 가변 폰트가 정적 파일보다 항상 작거나 모든 필요한 축을 제공하는 것은 아니므로 실제 subset과 로딩 결과를 비교한다.
### 1-2. 성격을 브리프에서 끌어낸다
@ -35,13 +33,13 @@
| 정확함, 계기, 데이터 | 좁은 폭, 낮은 대비, 열린 카운터, 모노와 잘 붙는 것 |
| 편집, 읽는 시간, 권위 | 세리프 또는 고대비 산세, 넉넉한 x-height |
| 도구, 중립, 신뢰 | 그로테스크. 성격은 웨이트와 여백이 만든다 |
| 인상, 기억 | 디스플레이 전용 서체. **본문에는 절대 쓰지 않는다** |
| 인상, 기억 | 디스플레이 전용 서체를 우선 검토한다. 본문 사용은 긴 읽기·언어 범위·확대 상태에서 검증한 경우에만 채택한다 |
### 1-3. 후보를 실제 값으로 검증한다
이름과 인상으로 고르지 마라. 다음 넷을 본다.
- **x-height 비율** — 소문자 높이 ÷ 대문자 높이. 0.52 미만이면 작은 크기에서 흐려진다
- **x-height 비율** — 소문자 높이와 본문 크기의 관계. 단일 임계값으로 가독성을 판정하지 말고 실제 크기·언어·렌더링에서 확인한다
- **대비** — 획 굵기 차이. 큰 크기에서만 살아나는 폰트가 있다
- **웨이트 범위** — 필요한 웨이트가 실제로 있는가. 없는 웨이트를 브라우저가 합성하면 뭉개진다
- **숫자 형태** — 라이닝인가 올드스타일인가, tabular 가 있는가
@ -66,7 +64,7 @@ document.fonts.check('16px "Pretendard Variable"');
## 2. 가변 폰트
웨이트를 **두 개 이상** 쓰면 거의 항상 가변이 유리하다. 정적 폰트 두 벌보다 가변 한 벌이 작고, 중간 웨이트를 공짜로 얻는다.
여러 웨이트가 필요하면 가변 폰트를 후보로 둔다. 필요한 glyph·축·subset·캐시 조건에 따라 정적 파일 묶음이 더 작거나 호환성이 나을 수 있으므로 전송량과 실제 렌더를 비교한다.
### 축
@ -144,13 +142,13 @@ h1 { font-optical-sizing: auto; }
| 셀프 호스팅 | 요청 도메인 하나, 캐시 통제, 서드파티 장애 무관 | 업데이트를 직접 한다 |
| CDN | 설치가 한 줄 | 도메인이 늘고(각각 DNS+TLS), 그 서비스가 죽으면 글자가 죽는다 |
CDN 을 쓴다면 `preconnect` 를 반드시 건다. 셋 이상의 폰트 도메인은 그 자체가 결함이다.
CDN을 쓸 때 `preconnect`는 실제 첫 화면 요청·연결 비용을 측정한 뒤 선택한다. 도메인 수만으로 결함을 판정하지 말고, 권한·캐시·장애 경로·전송 waterfall을 함께 검토한다.
---
## 4. 폴백 메트릭 — 가장 자주 빠뜨리는 곳
폰트가 늦게 오면 브라우저는 폴백으로 먼저 그리고 나중에 교체한다. 두 폰트의 **행 높이와 글자 폭이 다르면 그 순간 레이아웃이 튄다.** 이것이 CLS 의 흔한 원인이고, `font-display: swap` 을 쓰는 한 반드시 일어난다.
폰트가 늦게 오면 표시 전략에 따라 폴백과 웹폰트가 교체될 수 있다. 두 폰트의 행 높이·폭이 다르면 레이아웃 이동의 원인이 될 수 있지만, `swap`이 반드시 CLS를 만든다고 단정하지 않는다. 실제 네트워크 조건에서 측정한다.
해결은 **폴백 폰트를 실제 폰트의 치수에 맞추는 것**이다.
@ -196,7 +194,7 @@ const measure = (family) => {
`ascent-override` / `descent-override` 는 실제 폰트의 `hhea` 또는 `OS/2` 값을 `unitsPerEm` 으로 나눈 백분율이다. 도구가 없으면 **행 높이가 눈에 띄게 튀지 않을 때까지 조정**하고 그 값을 `design.md` 에 적어라.
> `font-display: optional` 을 쓰면 이 문제가 사라진다. 대신 첫 방문자가 브랜드 서체를 못 볼 수 있다. **둘 중 하나는 골라야 한다.** 아무것도 안 하는 것이 최악이다.
> `font-display: optional`은 교체 가능성을 줄이는 한 선택지다. 폴백 메트릭 보정, 표시 전략, preload 여부는 실제 첫 방문·재방문·느린 망에서 조합해 검증한다.
---
@ -240,7 +238,7 @@ body { font-kerning: normal; }
### 6-1. 서브셋은 선택이 아니다
한글 완성형은 11,172자다. 전체 폰트는 수 MB 다. 반드시 쪼갠다.
한글 완성형은 11,172자다. 전체 폰트가 클 수 있으므로 subset 전략을 검토한다. 모든 프로젝트가 글자 빈도 기반의 동적 subset을 반드시 써야 하는 것은 아니며, 콘텐츠 범위·cache·운영 복잡도와 실제 전송량으로 결정한다.
**쪼개는 단위가 라틴과 다르다.** 라틴은 `unicode-range` 로 블록을 나누면 되지만, 한글은 자주 쓰는 글자가 블록에 흩어져 있다. 그래서 **글자 빈도로 나눈 수십 개 조각**을 만들고 각각에 `unicode-range` 를 건다. 브라우저가 페이지에 실제로 쓰인 조각만 받는다.
@ -313,13 +311,13 @@ font-family: "Switzer", "Pretendard Variable", system-ui, sans-serif;
## 8. 배포 전 체크리스트
- [ ] 폰트 패밀리 수가 예산 안이다(`tokens.md` §5). 텍스트 두 벌 + 모노 하나가 상한
- [ ] 폰트 패밀리 수와 실제 첫 화면 전송량이 프로젝트 예산 안이다(`tokens.md` §5)
- [ ] **"왜 이 폰트인가"에 한 문장으로 답할 수 있다**
- [ ] 웨이트를 두 개 이상 쓰는데 정적 폰트를 여러 벌 받고 있지 않다
- [ ] 필요한 웨이트의 가변/정적 후보를 실제 subset 전송량·지원 범위로 비교했다
- [ ] 없는 웨이트를 브라우저가 합성하고 있지 않다(`font-synthesis-weight: none` 으로 확인)
- [ ] `font-display` 를 의식적으로 골랐다
- [ ] preload 한 파일이 첫 화면에 실제로 쓰인다. `crossorigin` 이 있다
- [ ] **폴백 메트릭을 보정했거나, `optional` 을 골랐다.** 둘 중 하나는 했다
- [ ] 실제 첫 방문·재방문·느린 망에서 폴백 메트릭과 `font-display` 선택이 의도한 줄바꿈·레이아웃을 유지한다
- [ ] 수치를 나열하는 곳에 `tabular-nums` 가 있다
- [ ] 한글이 있으면 서브셋을 쓴다. 파일 개수가 아니라 전송량으로 쟀다
- [ ] 라틴 폰트가 폴백 스택 앞에 있다