웹 폰트 용량 줄이기

배준형·2023년 9월 11일
0

웹 폰트란?

웹 폰트는 사용자가 폰트를 설치하지 않아도 디자이너가 원하는 타이포그래피를 웹 페이지에 구현할 수 있게 하는 기술이다. 웹 폰트는 폰트 파일을 서버에서 다운로드하여 사용하는 방식으로 동작하고, 여러 가지 형식이 존재한다.

용량 순서대로 EOT > TTF/OTF > WOFF > WOFF2 형식의 폰트가 존재한다. 운영 체제에서 사용하는 TTF 또는 OTF 형식의 폰트는 파일 크기가 매우 커서 웹 환경에선 적절하지 않은데, 그래서 나온 것이 WOFF 형식의 폰트이다. WOFF(Web Open Font Format)는 이름 그대로 웹을 위한 폰트이고 TTF 폰트를 압축하여 웹에서 더욱 빠르게 로드할 수 있도록 만들어졌다. WOFF2는 WOFF의 개선 버전으로, 압축 알고리즘이 개선되었고 WOFF2 형식으로 폰트를 가져오면 더 적은 용량으로 폰트를 사용할 수 있다.


웹 폰트 용량을 줄이려는 이유

  • 개발하고 있는 서비스 프로덕션 환경에 접속하면 다운로드 받는 폰트이다.

우리 팀은 Pretendard 폰트를 CDN 형태로 사용하고 있는데, Git Commit Log 상 지금으로부터 20개월 전에 추가된 폰트였고, 약 750kB가 넘는 폰트 5개를 최초 접속 시에 받아오는데, 각각이 용량이 너무 크고 약 300ms 의 시간이 소요되고 있었다.

그런데, 우리 서비스는 i18n 같은 라이브러리는 전혀 사용하지 않고 있어서 대부분 한글만 사용하는데, 한글이 용량이 크긴 하지만 이 정도로 용량이 큰 폰트는 불필요하다.


폰트 용량이 클 때 발생할 수 있는 문제점

사용성 측면

FOIT, FOUT 현상으로 기획/디자이너/개발자가 의도한 화면을 제대로 보지 못할 수 있다.

사용자가 웹 사이트에 접속했을 때 텍스트가 보이는 시점에 폰트 다운로드가 완료되지 않았다면 텍스트가 보이지 않거나(FOIT) 기본 폰트로 보이다가(FOUT) 의도한 화면이 보이면서 텍스트가 변하게 된다. 이러한 현상은 페이지가 느리게 느껴진다거나 요소를 밀어낼 수 있는 등 사용성에 영향을 준다.

위 현상의 원인은 다음과 같다.

출처: https://web.dev/optimize-webfont-loading/

브라우저 렌더링 과정이 이루어지면서 레이아웃을 수행하고 콘텐츠를 화면에 그리는데,

  • 폰트를 아직 사용할 수 없는 경우 브라우저에서 텍스트 픽셀을 렌더링하지 못할 수 있다.
  • 폰트를 사용할 수 있게 되면 브라우저는 텍스트 픽셀을 그린다.

이 과정에서 외부 웹 폰트 링크로 정의된 부분을 만나고 해당 폰트 파일을 다운로드하기 시작한다. 하지만 그리기(paint) 단계에서 웹 폰트 파일처럼 외부 링크로 연결된 파일의 다운로드가 완료되지 않았으면 브라우저는 해당 자원을 사용하는 콘텐츠의 렌더링을 차단한다.


비용 측면

AWS 서비스를 사용하고 있는데, 사용하는 서비스나 과금 방식에 따라 다를 수는 있지만, 전송량 기준 과금 방식이라면 사용하지도 않는 용량 만큼의 폰트를 다운로드하면서 불필요한 요금을 추가로 지출해야한다.


폰트 형식 바꾸기

출처: https://d2.naver.com/helloworld/4969726

나눔 스퀘어 라운드 폰트 형식에 따른 용량 분포는 위 이미지와 같다. 가장 적은 용량을 갖는 형식은 woff2로, 만약 woff 폰트를 쓰고 있다면 woff2 폰트로 변환하기만 해도 꽤 많은 부분을 최적화할 수 있다.

여기선 이미 woff2 폰트를 쓰고 있기 때문에 정리만 하고 넘어간다.


서브셋 폰트 사용하기

서브셋(subset) 폰트는 전체 폰트 중 필요한 문자만 선택하여 사용하는 것을 말한다. 서브셋 폰트를 사용하면, 필요한 문자만 포함된 폰트 파일을 다운로드하기 때문에 전체 폰트를 다운로드하는 것보다 더 적은 용량으로 폰트를 사용할 수 있고 로딩 속도도 더 빠르다.

서브셋 폰트를 직접 만들 수도 있지만 대부분의 대부분의 웹 폰트 서비스에서는 폰트의 subset을 선택하여 다운로드할 수 있는 기능을 제공하고 있다.

우리 팀은 Pretendard 폰트를 CDN 사용 중인데, 코드를 확인해보면 단순히 pretendard.css 파일을 받아와 사용하고 있었다. 그래서 700kB 이상이 되는 폰트를 5개나 다운로드 받은 것이고, 각각의 폰트에는 모든 한글 글자가 포함되어 있을 것이다.

출처: https://d2.naver.com/helloworld/4969726

위 이미지에서 형광색으로 칠해진 갂, 갃 같은 글자들은 실생활에서 거의 사용하지 않는 글자들이다. 그런데 사용하지 않을 뿐이지 존재하지 않는 글자는 아니기에 서브셋 폰트가 아닌 일반 폰트라면 위의 글자들도 모두 포함된 글자일 것이다.

현대 한글의 모든 글자 수는 총 11,172 글자인데, 11,172개의 글자 중 자주 사용되는 글자는 매우 적다. 갂, 갃, 갅, 갆 등 거의 사용되지 않는 글자가 절대다수를 차지한다. 이 중 KS X 1001(한국 산업 규격으로 지정된 한국어 문자 집합으로서, 정식 규격명은 ‘정보 교환용 부호계 (한글 및 한자)’)에 포함된 현대 한글 글자 수는 2,350개 이다.

이 완성형 한글 중에서도 사용하지 않을 '붤', '싻', '쏀' 등 일상 생활에서 쓸 일이 없는 글자도 포함하고 있지만, 그럼에도 모든 글자보다 훨씬 적은 글자를 갖고 있으므로 완성형 한글만 사용하는 서브셋 폰트를 만들어 사용하면 용량을 꽤 많이 줄일 수 있다.


Pretendard subset 폰트 사용하기

우리 팀은 폰트를 CSS에서 @import 키워드를 사용해서 CDN으로 불러오고 있다.

수정 전

@import url('https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.8/dist/web/static/pretendard.css');

수정 후

@import url('https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.8/dist/web/static/pretendard-dynamic-subset.css');

단순히 Pretendard 에서 제공하는 다이나믹 서브셋을 활용하는 것으로 수정하기만 했는데, 그 결과는 아래와 같다.

폰트가 필요한 글자에 대해서만 서브셋 폰트를 불러오고 있고, 기존 700kB가 넘는 폰트 5개를 다 받아와서 약 3,500 kB 이상의 용량을 사용하던 것에서 10~20 kB의 서브셋 폰트를 필요한 것만 받아와서 사용하는 것으로 개선되었다. 불러오는 항목은 더 많아졌지만, 다 합쳐서 400~500 kB 만 다운받으면 되니 결과적으로 80% 이상 감소된 것이다.

물론 페이지를 이동하면서 더 필요한 폰트에 대한 다운로드는 있을 수 있지만 최초 랜딩 시에 감소된 수치는 꽤 크다.


Pretendard 서브셋 폰트는 어떻게 필요한 문자만 다운로드가 가능할까?

서브셋 폰트로 바꾸자마자 따로 설정하지 않아도 필요한 문자만 다운로드 받는 모습을 확인할 수 있다. 이것은 어떻게 가능한지 알아보자.

pretendard/dist/web/static/pretendard-dynamic-subset.css (Github)

/* [0] */
@font-face {
  font-family: 'Pretendard';
  font-style: normal;
  font-display: swap;
  font-weight: 100;
  src: url(../../../packages/pretendard/dist/web/static/woff2-dynamic-subset/Pretendard-Thin.subset.0.woff2) format('woff2'),
    url(../../../packages/pretendard/dist/web/static/woff-dynamic-subset/Pretendard-Thin.subset.0.woff) format('woff');
  unicode-range: U+f9ca-fa0b, U+ff03-ff05, U+ff07, U+ff0a-ff0b, U+ff0d-ff19, U+ff1b, U+ff1d, U+ff20-ff5b, U+ff5d,
    U+ffe0-ffe3, U+ffe5-ffe6;
}

CDN으로 불러오는 파일을 확인해보면 위와 같다. font-face로 0~91 까지 font-weight별로 비슷한 코드가 나열돼있어서 코드 라인 수만 7,500 줄 정도 된다.

  • font-family: 폰트의 이름을 지정
  • font-style: 폰트의 스타일을 지정(normal, italic 등)
  • font-display: 폰트가 로딩되기 전까지 사용할 fallback 폰트를 지정
    • swap은 폰트가 로딩되면 즉시 교체하는 방식
      • auto: 브라우저 기본 동작
      • block: FOIT (timeout 3s)
      • swap: FOUT
      • fallback: FOIT (timeout 0.1s) / 3초 후 불러오지 못한 경우 기본 폰트 유지
      • optional: FOIT (timeout 0.1s) / 3초 후 네트워크 상태에 따라 기본 폰트로 유지할지 결정
  • font-weight: 폰트의 두께를 지정
  • src: 폰트 파일의 경로를 지정
    • 여기선 쉼표(,)로 구분하여 woff, woff2 둘 다 지정하고 있는데, 브라우저는 이해하는 첫 번째 목록부터 불러오려고 하므로 woff2 > woff 순서로 불러오도록 시도한다.
  • unicode-range: 폰트가 지원하는 유니코드 범위를 지정

unicode-range

pretendard에서 설정한 서브셋 폰트가 필요한 순간에만 다운로드 요청을 보내는 이유는 여기 있다. unicode-range 항목을 지정하면 페이지에서 이 범위의 문자를 사용하지 않으면 글꼴이 다운로드되지 않고, 하나 이상의 글꼴을 사용하는 경우 전체 글꼴이 다운로드된다.

pretendard는 unicode-range를 구분하여 0~91 까지 나눴고, 해당하는 문자가 페이지에 있는 경우 그 폰트만 다운받게 되니 아래처럼 여러 개를 다운받게 되는 것이다.

이를 잘 활용하면 다양한 언어를 제공하는 사이트의 경우 한국어, 영어, 일본어를 사용한다 할 때 한국어만 사용할 때는 굳이 영어와 일본어를 다운로드 받을 필요가 없으므로 대역폭이 절약된다.

출처: https://d2.naver.com/helloworld/4969726

U+BC14는 글자 '바'에 해당하는 유니코드고, U+CC28는 글자 '차'에 해당하는 유니코드다. 전체 텍스트에서 unicode-range 속성에 유니코드로 등록된 글자인 '바'와 '차'에만 웹 폰트가 적용되었다.


결론

결론적으로 웹 폰트 서비스에서 제공하는 서브셋 폰트를 활용해서 폰트 용량을 줄였다. 이미 대부분 웹 폰트 서비스에서 서브셋 폰트를 제공하고 있기 때문에 Pretendard 폰트가 아니라도 간단하게 해결할 수 있을 것이라 예상된다.

그렇지 않을 경우에는 폰트 파일 형식을 woff2 로 사용하거나 꼭 필요한 문자만 모아서 서브셋 폰트를 만들거나 font-face의 unicode-range 속성을 활용해서 필요한 문자가 있을 때 서브셋 폰트를 다운받도록 나누는 방식을 활용할 수 있을 것 같다.


참조

profile
프론트엔드 개발자 배준형입니다.

0개의 댓글