대부분의 본문은 웹폰트 없이 시스템 폰트 스택(-apple-system, Segoe UI, Pretendard, Noto Sans KR …)만으로 0바이트·즉시 렌더가 가능합니다. 꼭 웹폰트를 써야 할 때만 self-host + woff2 서브셋 + font-display:swap + preload로 비용을 최소화합니다.
요약
- 웹폰트의 진짜 비용은 용량만이 아니다 — FOUT/FOIT 깜빡임, CLS(레이아웃 흔들림), 외부 도메인 연결까지 따라온다.
- 시스템 폰트 스택은 다운로드 0바이트, 추가 요청 0개, 깜빡임 0 — 사용자 기기의 가장 좋은 글꼴을 그대로 쓴다.
- 한글 웹폰트는 글리프가 많아 무게당 1~3MB를 넘기기도 한다. 쓸 거면 서브셋·woff2가 필수다.
- 브랜드 글꼴이 꼭 필요하면 로고·제목에만 self-host + swap + preload + size-adjust로 가볍게.
예전에 한 사이트의 로딩이 느리다는 제보를 받고 네트워크 탭을 열었습니다. 이미지도 적고 스크립트도 가벼운데, 폰트 파일 네 개가 4MB 가까이 받아지고 있었습니다. Regular·Medium·Bold·SemiBold 한글 글꼴을 통째로요. 정작 그 사이트가 쓰는 글자는 수백 자 남짓이었습니다. 그날 저는 ‘글꼴은 디자인 결정이기 전에 성능 결정’이라는 걸 다시 배웠습니다.
참고로, 시스템 폰트 스택은 브라우저가 ‘이미 깔린 글꼴이 있는지’만 확인하면 끝입니다. 저는 종종 터미널에서 글꼴 식별자만 확인해 두는데, 결과는 늘 같습니다 — 받을 파일이 0개라는 것.
$ fc-match "-apple-system" → San Francisco (macOS 내장) $ fc-match "Segoe UI" → Segoe UI (Windows 내장) $ fc-match "Apple SD Gothic Neo" → Apple SD Gothic Neo (한글 · macOS 내장) $ 다운로드 필요: 0 파일 · 0바이트 $ ✓ FOUT/FOIT 깜빡임 없음 · CLS 흔들림 없음 · 외부 요청 0
꼭 브랜드 글꼴을 써야 할 때만, 저는 self-host한 woff2 서브셋을 이렇게 한 곳에 묶어 둡니다 — 본문은 시스템 폰트로 0바이트, 포인트 글자에만 가볍게.
:root{
/* 본문: 받을 게 없는 시스템 폰트 스택 */
--sans: -apple-system, "Segoe UI", "Pretendard",
"Apple SD Gothic Neo", "Malgun Gothic", sans-serif;
}
body{ font-family: var(--sans); }
/* 제목·로고에만 — self-host 서브셋 woff2 */
@font-face{
font-family: "BrandSans";
src: url("/assets/fonts/brand-subset.woff2") format("woff2");
font-weight: 700;
font-display: swap; /* FOIT 방지: 먼저 시스템 폰트로 읽히게 */
size-adjust: 96%; /* 교체 순간 폭 차이(CLS) 제거 */
}
h1{ font-family: "BrandSans", var(--sans); }이렇게 두면 사용자가 가장 오래 보는 본문은 즉시 뜨고, 브랜드는 큰 글자 몇 곳에서만 ‘꼭 필요한 만큼’ 삽니다.
웹폰트는 정확히 무엇을 비싸게 만드나요?
단순히 ‘파일이 무겁다’가 전부가 아닙니다. 웹폰트 하나를 넣으면 네 가지 비용이 한꺼번에 따라옵니다. 첫째, 용량 — 한글은 무게(weight) 하나당 1~3MB를 넘기기도 합니다. 둘째, 깜빡임(FOUT·FOIT) — 폰트가 도착하기 전 글자가 안 보이거나(FOIT) 대체 글꼴로 떴다가 바뀝니다(FOUT). 셋째, CLS — 대체 글꼴과 웹폰트의 글자 폭이 달라 글자가 바뀌는 순간 줄바꿈과 레이아웃이 흔들립니다. 넷째, 외부 요청 — 구글 폰트를 link로 부르면 외부 도메인 두 곳에 추가로 연결됩니다.
그래서 늦으면 화면이 어떻게 되나요?
말로는 잘 안 와닿습니다. 직접 눌러보세요. 아래 ‘웹폰트 로딩 시뮬레이션’을 누르면, 늦게 도착한 웹폰트가 적용되는 순간 글자가 깜빡이며 자리가 살짝 흔들립니다. 이게 FOUT와 CLS입니다.
웹폰트가 늦으면 이렇게 됩니다
'웹폰트 로딩 시뮬레이션'을 누르면 글자가 깜빡이며 자리가 흔들립니다(FOUT·CLS). 시스템 폰트는 이게 없습니다.
검색과 AI 답변에 찾아지는 홈페이지
그럼 글꼴을 포기하라는 말인가요?
아닙니다. 사용자 기기에는 이미 잘 만든 글꼴이 깔려 있습니다. 그걸 OS별로 가장 좋은 것부터 골라 쓰는 게 ‘시스템 폰트 스택’입니다. 다운로드 0바이트, 추가 요청 0개, 깜빡임 0. 제가 한글 사이트에 기본으로 까는 스택은 이렇습니다.
:root {
--sans:
-apple-system, BlinkMacSystemFont, /* macOS · iOS */
"Segoe UI", /* Windows */
"Pretendard", /* 깔려 있으면 사용 (self-host도 가능) */
"Noto Sans KR", "Apple SD Gothic Neo",
"Malgun Gothic", "맑은 고딕", /* Windows 한글 */
Roboto, "Helvetica Neue", Arial, sans-serif,
"Apple Color Emoji", "Segoe UI Emoji"; /* 이모지 폴백 */
}
body { font-family: var(--sans); }
맨 앞 -apple-system은 애플 기기의 산세리프(San Francisco)를, Segoe UI는 윈도우 기본을 가리킵니다. 각 OS는 이미 자기 글꼴을 갖고 있으니, 받을 게 없습니다. 한글은 Apple SD Gothic Neo(맥)·Malgun Gothic(윈도우)이 받쳐주고, 원한다면 Pretendard를 self-host해 우선순위에 끼워 넣습니다.
그래도 브랜드 글꼴을 꼭 써야 한다면?
로고나 큰 제목처럼 ‘브랜드가 사는’ 한두 곳에만 웹폰트를 쓰는 게 현실적인 절충입니다. 본문은 시스템 폰트로 0바이트로 즉시 띄우고, 포인트 글자에만 가볍게. 이때 저는 네 가지를 반드시 챙깁니다.
@font-face {
font-family: "BrandSans";
src: url("/assets/fonts/brand-subset.woff2") format("woff2");
font-weight: 700;
font-display: swap; /* 먼저 대체 글꼴로 보여주고 나중에 교체(FOUT) */
size-adjust: 96%; /* 대체 글꼴 폭을 맞춰 CLS(흔들림) 제거 */
}
/* preload: 제목 글꼴을 빨리 받게 (HTML <head>에) */
<link rel="preload" href="/assets/fonts/brand-subset.woff2"
as="font" type="font/woff2" crossorigin>
하나씩 풀면 이렇습니다. font-display:swap — 폰트를 기다리며 글자를 숨기지 말고(FOIT 방지) 일단 대체 글꼴로 읽히게 합니다. preload — 진짜 필요한 글꼴 파일만 우선순위로 당겨 교체 시점을 앞당깁니다. 서브셋 — 실제 쓰는 글자만 남겨 파일을 줄입니다(아래에서 다룹니다). size-adjust — 대체 글꼴과 웹폰트의 크기를 맞춰 교체 순간의 흔들림(CLS)을 없앱니다. 그리고 CSP 때문에 외부 폰트는 막혀 있으니 파일은 우리 서버에 두고 self-host합니다.
한글 웹폰트는 왜 그렇게 무거운가요?
라틴 알파벳은 글리프가 수십~수백 개입니다. 한글은 자모 조합으로 이론상 1만1172자가 만들어지고, 글꼴 파일에는 보통 완성형 2천350자 안팎이 들어갑니다. 글리프가 많으니 파일이 클 수밖에 없고, 무게(Regular·Bold 등)마다 별도 파일이라 금세 수 MB가 됩니다. 그래서 한글은 ‘쓰는 글자만 남기는’ 서브셋과 woff2 압축이 거의 필수입니다.
# 실제로 쓰는 글자만 남겨 woff2로 (fonttools pyftsubset)
pyftsubset NotoSansKR.ttf \
--text-file=used-characters.txt \
--flavor=woff2 \
--layout-features='*' \
--output-file=notosanskr-subset.woff2
# 2~3MB 글꼴이 수십~수백 KB로 줄어듭니다.
제목에만 쓸 큰 글자라면 더 공격적으로 줄일 수 있습니다. 페이지에서 실제 등장하는 글자만 추려 서브셋하면, MB 단위 글꼴이 수십 KB로 떨어지는 일이 흔합니다.
그래서 무엇을 기본값으로 삼나요?
저의 기본값은 단순합니다. 본문은 시스템 폰트 스택, 브랜드는 꼭 필요한 곳에만 self-host 서브셋. 가장 오래 읽히는 본문이 0바이트로 즉시 뜨면 체감 속도가 가장 크게 좋아지고, 깜빡임과 흔들림이 사라집니다. 두 표를 나란히 두면 차이가 분명합니다.
| 항목 | 무거운 웹폰트(통짜) | 시스템 폰트 스택 |
|---|---|---|
| 다운로드 용량 | 한글 무게당 1~3MB(여러 무게면 수 MB) | 0바이트 — 받을 게 없음 |
| 첫 글자 표시 속도 | 폰트 도착까지 지연(FOIT) 또는 깜빡임(FOUT) | 즉시 렌더 — 깜빡임 0 |
| CLS(레이아웃 흔들림) | 대체→웹폰트 교체 시 폭 변화로 흔들림 | 교체가 없으니 흔들림 0 |
| 외부 요청 | 구글 폰트면 외부 도메인 2곳 추가 연결 | 추가 요청 0개 |
다른 담당자와의 연결
글꼴은 혼자 있는 결정이 아닙니다. 글자의 ‘모양’은 타이포 담당자와, 받아오는 ‘속도’는 성능 담당자와, 그리고 함께 무거워지는 이미지 최적화와 맞물립니다.
타이포그래피 — 어떤 글꼴을 어떻게 배치해 읽기 좋게 만드는지는 타이포 담당자의 노트에서 다룹니다. 성능 — 폰트를 포함해 첫 화면을 빠르게 띄우는 전체 전략은 성능 담당자의 노트에 있습니다. 이미지 최적화 — 폰트와 함께 페이지를 무겁게 만드는 또 다른 주범인 이미지는 이미지 최적화 노트에서 줄입니다.
시스템 폰트만 쓰면 브랜드 느낌이 안 사는 것 아닌가요?
한글 웹폰트는 왜 그렇게 무겁나요?
FOUT와 FOIT 중 무엇이 더 낫나요?
font-display:swap만 넣으면 CLS가 해결되나요?
구글 폰트는 그냥 link 태그로 불러오면 안 되나요?
타이포 담당자의 노트
읽기 좋은 글자 배치의 실무.
성능 담당자의 노트
첫 화면을 빠르게 띄우는 전략.
웹 성능 최적화
Core Web Vitals와 체감 속도.
웹폰트가 무조건 나쁜 건 아닙니다 — 브랜드가 사는 제목·로고에는 self-host + 서브셋 + swap + preload로 ‘꼭 필요한 만큼만’ 쓰면 됩니다. 이 글의 라이브 위젯은 FOUT·CLS의 개념을 보여주는 시뮬레이션이며, 실제 성능은 네트워크·기기·글꼴에 따라 달라집니다. 날조된 사례·수치는 사용하지 않았습니다.