문제The Problem
한글 웹폰트가 무거운 이유는 단순합니다. 담아야 할 글자가 너무 많습니다. 완성형 한글은 11,172자이고, 여기에 로마자와 심벌을 더하면 글꼴 하나가 12,000자 넘는 글리프를 갖습니다. 로마자 전용 글꼴이 100자 안쪽으로 끝나는 것과는 규모가 다릅니다.
아리따 원본 파일로 확인해 보면 AritaDotumKR-Medium.ttf가 6.1MB, 본문용인 AritaBuriKR-SemiBold.ttf는 11.1MB입니다. 이를 웹 전송용 woff2로 압축해도 돋움 한 굵기가 769KB, 부리 한 굵기가 1.5MB에 머무릅니다.
문제는 굵기를 여러 개 쓸 때 커집니다. 제목에 Bold, 본문에 Medium, 부가 설명에 Light를 쓰는 흔한 구성이면 글꼴만 2.3MB입니다. 이미지보다 무거운 자원이 되고, 글꼴이 도착할 때까지 글자가 대체 서체로 보이거나 아예 비어 있어 첫 화면이 늦게 완성됩니다.
같은 글꼴인데 파일 크기가 다른 이유
글리프를 저장하는 방식이 두 가지라 같은 서체도 파일 크기가 달라집니다. 아리따 원본은 glyf(TrueType 곡선) 방식이고 화면 표시를 다듬는 힌팅 테이블(cvt, fpgm, prep)까지 품고 있습니다. 반면 CFF(PostScript 곡선) 방식으로 다시 만든 파일은 같은 글자 수를 더 적은 바이트로 표현합니다. 그래서 배포처에 따라 같은 아리따 돋움이 490KB일 수도, 770KB일 수도 있습니다.
다만 이 차이는 어떻게 저장했느냐의 문제입니다. 아래에서 다룰 절감은 얼마나 전송하느냐의 문제고, 둘은 별개입니다.
원리How It Works
글자 단위 분할(dynamic subset)은 글꼴을 유니코드 구간별로 잘게 나눠 두고, 브라우저가 화면에 실제로 나타난 글자가 속한 조각만내려받게 하는 방식입니다. CSS의 unicode-range가 그 판단 근거를 제공합니다.
/* 조각 하나하나가 담당 구간을 unicode-range로 선언한다 */
@font-face {
font-family: "Arita Dotum KR";
font-weight: 500;
font-display: swap;
src: url("./AritaDotumKR-Medium/xxxx.woff2") format("woff2");
unicode-range: U+AC00-AD8B, U+AD8D-AE00; /* '가'~'대' 부근 구간 */
}이런 @font-face 선언이 조각마다 하나씩 들어 있습니다. 아리따 돋움 Medium은 61개 조각으로 나뉘어 있고 조각 하나가 평균 27KB입니다. 브라우저는 CSS 전체를 읽은 뒤 페이지에 쓰인 글자의 코드포인트를 확인하고, 해당 구간을 담당하는 조각에만 요청을 보냅니다. 나머지 조각의 URL은 무시합니다.
개발자가 따로 설정할 것은 없습니다. 어떤 글자를 쓸지 미리 신고하거나 빌드 단계에서 텍스트를 추출할 필요도 없습니다. 브라우저가 렌더링 시점에 판단하기 때문에 내용이 바뀌어도 자동으로 맞춰집니다. 링크 한 줄이면 끝입니다.
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@bepyan/arita@1.0.1/dist/dynamic-subset/arita-dotum-dynamic-subset.css"
/>실측Measured
실제로 얼마나 줄어드는지 측정했습니다. 방법은 이렇습니다. 표본 텍스트의 고유 문자를 모아 코드포인트로 바꾸고, 배포된 CSS의unicode-range를 파싱해 어떤 조각이 필요한지 계산한 뒤, 그 조각들의 실제 파일 크기를 합했습니다.
| 시나리오 | 고유 글자 | 통짜 | 조각 | 분할 | 절감 |
|---|---|---|---|---|---|
| 돋움 Medium 1굵기 · 랜딩 페이지 | 39자 | 769.0KB | 8개 | 213.1KB | 72% |
| 돋움 Medium 1굵기 · 블로그 본문 | 157자 | 769.0KB | 9개 | 261.8KB | 66% |
| 돋움 3굵기(300·500·700) · 랜딩 페이지 | 39자 | 2,378.6KB | 24개 | 553.2KB | 77% |
| 돋움 3굵기(300·500·700) · 블로그 본문 | 157자 | 2,378.6KB | 27개 | 674.7KB | 72% |
| 부리 Medium 1굵기 · 랜딩 페이지 | 39자 | 1,515.4KB | 9개 | 279.6KB | 82% |
| 부리 Medium 1굵기 · 블로그 본문 | 157자 | 1,515.4KB | 15개 | 498.0KB | 67% |
측정 시점 2026년 7월 30일, @bepyan/arita@1.0.1 빌드 산출물 기준. 랜딩 페이지는 짧은 안내 문구(고유 39자), 블로그 본문은 네 단락 분량의 한국어 산문(고유 157자)을 표본으로 삼았습니다. 통짜 값은 같은 굵기의woff2 파일 크기입니다.
전송량은 66~82% 줄어듭니다. 절감 폭이 가장 큰 쪽은 원본이 무거운 부리이고, 글자 수가 적은 랜딩 페이지일수록 유리합니다. 본문이 길어지면 필요한 조각이 늘어나 절감 폭은 서서히 줄어듭니다.
절감이 무한정 커지지 않는 이유
고유 39자로 이미 8개 조각(213KB)을 받습니다. 39자면 조각 한두 개로 충분할 것 같은데 8개인 이유는, 한글 음절이 유니코드 표에서 초성·중성·종성 순서로 배열되어 실제 문장의 글자들이 여러 구간에 흩어지기 때문입니다. '아'와 '리'와 '따'는 표에서 서로 멀리 떨어져 있습니다. 그래서 글자 수가 적어도 조각 수는 일정 수준 이상으로 유지됩니다.
비교Alternatives
아리따를 웹폰트로 제공하는 다른 경로와도 비교했습니다. 널리 쓰이는 눈누(Project Noonnu)의 jsDelivr 주소를 실제로 받아 측정한 결과입니다.
| 제공 방식 | 형식 | 전송량 |
|---|---|---|
| 눈누 · 돋움 Medium (전량) | woff · CFF | 491.6KB |
| 이 CDN · 돋움 Medium (블로그 본문) | woff2 분할 | 261.8KB |
| 눈누 · 부리 SemiBold (전량) | woff · glyf | 3,157.3KB |
| 이 CDN · 부리 Medium (블로그 본문) | woff2 분할 | 498.0KB |
두 눈누 파일 모두 완성형 11,172자를 온전히 담고 있어 글자 수 차이는 없습니다. 돋움 파일이 상대적으로 작은 것은 CFF 방식으로 다시 만들었기 때문이고, 부리 파일이 큰 것은 glyf 방식에 압축률이 낮은 woff를 쓴 결과입니다.
하나는 분명히 해둬야 합니다. 파일 하나만 놓고 보면 눈누의 돋움이 이 CDN의 통짜 파일보다 작습니다(491.6KB 대 769.0KB).CFF 방식이 그만큼 효율적이니까요. 이 CDN이 나은 건 압축을 더 잘해서가 아닙니다. 필요 없는 부분을 애초에 안 보내기때문입니다.
그 밖의 차이로는, 눈누는 서체별로 특정 굵기만 제공하지만 이 CDN은 돋움·부리·산스의 다섯 굵기와 흑체의 세 굵기를 모두 제공합니다. npm 버전으로 고정할 수 있어 배포 후 파일이 바뀌는 일도 없습니다.
한계Trade-offs
분할 방식이 언제나 유리한 것은 아닙니다. 아래 경우에는 통짜 파일이 낫습니다.
조각 전체를 합치면 통짜보다 크다
조각마다 글꼴의 공통 테이블이 중복으로 들어가기 때문에, 돋움 Medium의 61개 조각을 모두 합치면 1,792KB로 통짜 769KB의 두 배가 넘습니다. 한 방문자가 사이트의 페이지를 아주 많이 돌아다녀 대부분의 조각을 결국 받게 된다면 누적 전송량이 통짜보다 커질 수 있습니다. 글자 종류가 매우 많은 사전이나 대형 문서 사이트가 여기에 해당합니다.
요청 수가 늘어난다
한 페이지에서 8~27개의 추가 요청이 발생합니다. HTTP/2 이상에서는 한 연결로 다중 전송하므로 부담이 크지 않지만, HTTP/1.1 환경이라면 연결 수 제한 때문에 오히려 느려질 수 있습니다.
셀프 호스팅·오프라인
외부 CDN을 쓸 수 없는 환경이라면 조각 수백 개를 직접 배포하는 편보다 통짜 파일 몇 개를 두는 편이 관리하기 쉽습니다.
이런 경우에는 static 경로를 쓰면 됩니다. 같은 패키지에 함께 들어 있습니다.
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@bepyan/arita@1.0.1/dist/static/arita-dotum.css"
/>더 보기Related
- Next.js에 적용하기App Router · Tailwind · next/font와 함께 쓰는 방법
- 워드프레스에 적용하기자식 테마 · functions.php · 블록 에디터에 적용하는 방법