폰트비 블로그
폰트/타이포그래피/디자인

한글 웹폰트 적용 완전 정리 — @font-face·서브셋·font-display 실전 코드

글쓴이 · 폰트비
종이에 인쇄된 다양한 서체 스타일을 가까이서 촬영한 사진

한글 웹폰트 적용 완전 정리 — @font-face·서브셋·font-display 실전 코드

종이에 인쇄된 다양한 서체 스타일을 가까이서 촬영한 사진

디자인을 잡고 폰트도 골랐는데, 막상 웹에 올리면 무거워지거나 깜빡이거나 라이선스 문제가 생긴다. 한글 폰트는 알파벳과 구조 자체가 달라서, 조합 가능한 글자가 11,172자에 달한다. 그래서 파일 하나가 수십 MB까지 커지고, 아무 준비 없이 올리면 페이지 로딩이 눈에 띄게 느려진다. 폰트가 다 받아지기 전까지 텍스트가 통째로 안 보이는 FOIT 현상을 겪고 나서야 "그냥 올리면 안 되는구나"를 깨닫는 경우도 많다. 라이선스도 마찬가지다. 무료 폰트라고 해서 무조건 웹 임베딩이 허용되는 건 아니어서, 모르고 올렸다가 뒤늦게 수정하는 상황이 생기기도 한다.

CSS에서 font-family: 'Pretendard' 한 줄로 해결되면 좋겠지만, 실제로는 파일 형식 선택 → 서브셋 생성 → @font-face 선언 → font-display 전략 → fallback 스택까지 다섯 단계를 제대로 밟아야 브라우저 어디서든 의도한 대로 보인다.

이 글에서는 그 다섯 단계를 실전 코드와 함께 하나씩 정리한다.


웹폰트 적용 5단계

1단계 — 폰트 파일 형식 선택

웹에서 쓰는 폰트 형식은 크게 세 가지다.

  • woff2 — 현재 표준. 압축률이 가장 높고, 모든 모던 브라우저가 지원한다.
  • woff — woff2 이전 표준. 구형 브라우저 대응이 필요할 때만 쓴다.
  • ttf/otf — 데스크톱용 원본 파일. 웹에 그대로 올리면 용량이 크다.

한글 ttf 파일은 평균 수 MB, 많게는 16 MB까지 나간다. 같은 폰트를 woff2 서브셋으로 만들면 200~400 KB대로 줄어든다. Noto Sans CJK KR Regular 원본(16 MB)을 KS X 1001 기준 2,350자로 서브셋하고 woff2로 변환하면 379 KB까지 감소한다는 실측 결과가 있다. (출처: 44BITS) woff2는 Brotli 압축을 기반으로 하기 때문에 ttf 대비 파일 크기가 확연히 작고, 같은 폰트라도 형식 하나만 바꿔도 로딩 시간이 체감할 만큼 단축된다.

자체 호스팅이라면 woff2만 선언해도 충분하다. 2020년 이후 출시된 브라우저는 전부 지원한다. 구형 IE 대응이 필요하지 않다면 woff 파일을 별도로 준비할 이유가 없다.

2단계 — 서브셋 만들기

한글 웹폰트에서 서브셋은 선택이 아니라 필수다. 11,172자를 전부 싣지 않고 실제로 쓰이는 2,350자(KS X 1001 기준)만 추려내는 것이다. 일반 텍스트 콘텐츠에서 실제로 등장하는 한글 자모는 이 범위 안에 거의 다 포함되기 때문에, 서브셋을 써도 실사용에서 빠지는 글자는 거의 없다. Python 환경에서 fonttoolspyftsubset으로 직접 만들 수 있다.

# fonttools 설치
pip install fonttools brotli

# KS X 1001 유니코드 범위로 서브셋 생성 후 woff2 변환
pyftsubset NotoSansKR-Regular.ttf \
  --unicodes="U+AC00-D7A3,U+0020-007E,U+3130-318F" \
  --flavor=woff2 \
  --output-file=NotoSansKR-subset.woff2

서브셋 파일이 여러 개라면 unicode-range를 함께 선언해 브라우저가 필요한 파일만 내려받도록 한다. 예를 들어 한글 전용 파일과 라틴 전용 파일을 분리해두면, 라틴 문자만 쓰이는 페이지에서는 한글 파일이 아예 요청되지 않아 불필요한 네트워크 낭비를 줄일 수 있다.

3단계 — @font-face 선언

서브셋 파일이 준비됐다면 CSS에 @font-face를 선언한다. @font-face는 폰트 파일의 위치, 이름, 굵기, 표시 방식을 한꺼번에 지정하는 블록으로, 여기서 이름을 정해두면 나중에 어떤 요소에든 font-family로 참조해 쓸 수 있다.

/* 기본 단일 파일 방식 */
@font-face {
  font-family: 'MyFont';
  src: url('/fonts/myfont.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

/* unicode-range로 한글 파일만 지정 */
@font-face {
  font-family: 'MyFont';
  src: url('/fonts/myfont-kr.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+AC00-D7A3, U+0020-007E, U+3130-318F;
}

font-family에 지정한 이름은 이후 font-family: 'MyFont', sans-serif; 형태로 참조한다. 굵기별로 파일이 다를 경우 font-weight 값만 달리해서 @font-face 블록을 여러 개 선언하면 되고, 브라우저는 적용 시점에 맞는 굵기 파일만 골라 내려받는다.

4단계 — font-display 전략 선택 🎯

font-display는 폰트 파일이 로딩되는 동안 텍스트를 어떻게 보여줄지 결정한다. 이 값 하나가 사용자가 페이지를 열었을 때 텍스트가 보이냐 안 보이냐를 가르기 때문에, 한글 콘텐츠 중심 사이트에서는 특히 신경 써야 한다.

차단 구간교체 구간동작 요약
auto브라우저 기본브라우저 기본브라우저가 알아서 결정
block짧음(~3초)무한폰트 로딩 중 텍스트 숨김 → 로드 후 표시
swap극소(~100ms)무한즉시 fallback 표시 → 로드 완료 시 교체
fallback극소(~100ms)약 3초swap과 유사하나 3초 초과 시 fallback 고정
optional극소(~100ms)없음캐시에 있으면 사용, 없으면 fallback 고정

한글 본문에는 swap이 적합하다. 텍스트가 아예 안 보이는 FOIT보다 FOUT(교체 시 깜빡임)이 낫기 때문이다. 독자가 내용을 읽으러 들어왔는데 텍스트가 수 초간 안 보이는 것은 bounce를 유발하기 쉽다. 로고나 헤드라인처럼 브랜드 일관성이 중요한 자리라면 fallback을 쓴다.

5단계 — fallback 스택 지정

웹폰트가 로딩되지 않았을 때 어떤 폰트로 대신 보여줄지도 챙겨야 한다. 네트워크 상황이 나쁘거나 캐시가 없는 첫 방문 시에는 fallback 폰트가 실제로 화면에 그려지는 시간이 생기기 때문에, 이 단계를 생략하면 시스템 기본 폰트가 들쑥날쑥 적용되어 보기 어색해진다.

body {
  font-family: 'MyFont', 'Apple SD Gothic Neo', 'Noto Sans KR',
               'Malgun Gothic', sans-serif;
}
  • macOS/iOS: Apple SD Gothic Neo
  • Windows: Malgun Gothic (맑은 고딕)
  • Android: Noto Sans KR (시스템 내장)
  • 최후 fallback: sans-serif

fallback 폰트는 실제로 화면에 보이는 시간이 있다. 웹폰트와 자간·행간이 많이 다른 폰트를 fallback으로 쓰면 로딩 직후 레이아웃이 밀릴 수 있어서, 최대한 비슷한 계열로 맞춰두는 게 좋다. CSS size-adjustascent-override 속성을 이용해 fallback 폰트의 메트릭을 웹폰트에 맞게 조정하는 방법도 있지만, 우선은 비슷한 굵기와 너비의 시스템 폰트를 순서대로 나열하는 것만으로도 시각적 안정감이 충분히 나온다.


주요 한글 폰트 웹 임베딩 라이선스 비교

폰트를 서버에 올려 배포하는 건 단순 사용과 다르다. 로컬 컴퓨터에서 문서 작업에 쓰는 것과 달리, 서버에 올려 불특정 다수에게 파일을 전송하는 행위는 배포에 해당하기 때문이다. 예쁜 폰트를 골라서 woff2로 변환하기 전에, 웹 임베딩 허용 여부를 먼저 확인해야 한다.

폰트명제작사라이선스웹 임베딩비고
PretendardorioncactusOFL 1.1허용수정·재배포 허용, 폰트 단독 판매만 금지
나눔고딕 / 나눔바른고딕네이버OFL 1.1허용상업용 포함 자유 사용
Noto Sans KRGoogle / AdobeOFL 1.1허용Google Fonts CDN 또는 자체 호스팅 모두 가능
본고딕 (Source Han Sans KR)AdobeOFL 1.1허용Noto Sans와 동일 글리프 세트
KoPub 바탕 / 돋움한국출판인회의자체 라이선스별도 승인 필요서버 탑재 후 웹서비스 임베딩은 사전 승인 필수
KoPubWorld한국출판인회의자체 라이선스별도 승인 필요홈페이지 등록 후 사용, 임베딩 승인 별도

OFL(SIL Open Font License)은 웹폰트로 삽입해 배포하는 행위를 명시적으로 허용한다. 수정이나 재배포도 가능하고, 폰트 파일을 단독으로 판매하는 경우에만 제한이 붙는다. KoPub 계열은 인쇄나 온라인 콘텐츠 결과물은 자유롭지만, 폰트 파일 자체를 서버에 올려 서비스하는 임베딩은 한국출판인회의의 별도 승인이 필요하다. 라이선스 조건은 언제든 바뀔 수 있으니, 적용 전에 공식 페이지에서 최신 내용을 직접 확인하자.


Google Fonts CDN vs 자체 호스팅

Noto Sans KR이나 나눔고딕처럼 Google Fonts에 올라간 폰트라면 CDN을 그냥 쓸 수도 있다. 태그 한 줄만 넣으면 되니까 설정이 빠르고, Google이 브라우저와 언어 환경에 맞게 자동으로 최적화된 파일을 내려준다는 장점이 있다. 다만 성능과 제어 측면에서는 차이가 있다. 두 방법의 차이는 이렇다.

항목Google Fonts CDN자체 호스팅
설정 편의성 태그 한 줄파일 업로드 + CSS 작성
로딩 성능DNS + TCP 연결 추가 발생추가 외부 연결 없음
개인정보Google에 접속 IP 전달없음
제어권버전 자동 변경 가능완전 제어
GDPR별도 고지 필요할 수 있음문제 없음

속도를 중시한다면 자체 호스팅이 유리하다. 브라우저 캐시 공유 효과는 최신 브라우저 프라이버시 정책(파티션 캐시)으로 사실상 사라진 상태라, CDN의 성능 이점도 이전만큼 크지 않다. GDPR 등 개인정보 보호 규정이 엄격한 환경이라면 외부 CDN 연결 자체를 줄이는 쪽이 유리하고, 폰트 버전이 예고 없이 바뀌는 리스크도 자체 호스팅에서는 없다.


폰트비에서 @font-face 코드 바로 복사하기

어떤 폰트를 쓸지 정했다면, 라이선스 확인과 코드 작성을 한 번에 해결할 수 있다. 폰트비의 각 폰트 상세 페이지(/fonts/)에는 웹 임베딩을 포함한 사용 범위 표시(인쇄·웹사이트·영상·포장지·임베딩·BI/CI·OFL 가능 여부)와 함께 @font-face 코드 복사 버튼이 있다. 글자체를 고른 뒤 복사 버튼을 누르면 CDN URL이 포함된 완성 코드가 클립보드에 담기고, CSS에 붙여넣기만 하면 된다. 같은 페이지에서 라이선스 안내 원문도 확인할 수 있어, 웹 임베딩이 가능한지 따로 검색할 필요 없이 한 화면에서 확인이 끝난다.

폰트비는 파일 배포처가 아니라 디렉터리다. 폰트 파일 자체는 각 제작사 공식 배포처에서 받고, 폰트비는 라이선스 확인과 코드 복사 편의를 제공한다. 서체를 고르는 단계부터 시작하는 경우라면 AI 폰트 찾기(/find)에서 용도나 분위기를 입력해 후보를 좁힐 수 있고, 카테고리별 추천(/recommend)이나 최근 등록된 폰트(/new)도 둘러볼 수 있다. 수백 종을 일일이 미리보기하지 않아도 원하는 방향의 폰트를 빠르게 찾을 수 있다는 게 편리하다.

다섯 단계가 처음엔 복잡해 보여도, 한 번 세팅해두면 이후엔 서브셋 파일과 CSS 몇 줄만 바꾸면 된다. woff2 서브셋 생성 → @font-face 선언 → font-display: swap 지정 → fallback 스택 정리, 이 순서를 한 번 손에 익혀두면 어떤 한글 폰트를 가져다 써도 같은 방식으로 적용할 수 있다. 라이선스 확인은 귀찮더라도 빠뜨리지 말자. 나중에 문제가 생기면 폰트를 통째로 교체해야 하는 상황이 될 수 있다. 처음 폰트를 고르는 단계부터 배포까지 한 흐름으로 정리해두면 다음 프로젝트에서 같은 작업을 훨씬 빠르게 반복할 수 있다.


참고자료

이 글이 도움이 됐나요?

여러분의 반응이 다음 글의 방향이 돼요

이어서 읽기 좋은 글