들어가며
웹 사이트에 접속하면 사용자들은 대부분 동일한 폰트로 표시된 콘텐츠를 보게 된다. 이는 웹 폰트 기술로서, 사용자의 기기에 특정 폰트가 설치되어 있지 않더라도 웹 페이지 로딩 시 폰트를 다운로드하여 제작자가 의도한 대로 콘텐츠를 표시할 수 있게 해준다. 만약 폰트 다운로드에 시간이 많이 소요되면 전체 페이지 로딩 시간이 길어져 사용자 경험이 저하될 수 있다. 이 글에서는 웹 폰트 용량으로 인해 겪었던 문제와 그 개선 방안에 대해 이야기 할 예정이다.
큰 웹 폰트 용량
프로젝트의 리팩토링 작업과 함께 점검을 진행하던 중 웹 폰트 적용 시간이 조금 느리다는 것을 느꼈다. 기본적으로 폰트에 대해 font-display: swap
설정을 적용하고 있었기 때문에 폰트가 다운로드 되기 전까지는 시스템 폰트로 글자가 표출되다가 다운로드가 되면 그 때 웹 폰트로 교체되고 있던 상황이다. 그러나 시스템 폰트를 표출하는 시간이 체감상 길게 느껴졌고, 웹 폰트로 교체되며 시각적으로 흔들리기 때문에 사용자 경험을 저하시킬 거라 생각되어 적용 시간이 느린 원인을 파악해보았다.
웹 폰트 다운로드 시간이 얼마나 되는지 네트워크 탭에서 확인해보니 7초 정도로 긴 시간이었고, 용량은 무려 2.1MB로 큰 크기를 차지하고 있었다. (이는 네트워크 환경이 좋지 못했을 때 측정했기에 이를 감안해야 한다.) 큰 용량으로 인해 다운로드 시간이 길어졌고 이에 따라 폰트 적용도 늦어지고 있었던 것이었다. WOFF2임에도 용량이 큰 것을 확인할 수 있는데 그 이유는 해당 폰트는 가변폰트이기 때문이다.
가변 폰트
일반 폰트 경우에는 굵기, 기울임과 같은 스타일마다 bold, semibold와 같은 별도의 파일이 필요한데, 가변 폰트의 경우는 모든 스타일 변형을 하나의 파일에 통합하여 관리할 수 있다. 가변 폰트 파일은 단일 스타일의 폰트 파일보다는 용량이 크지만 여러 스타일 파일을 모두 합친 것보다는 작다. 하나의 파일만 사용하므로 HTTP 요청 횟수가 줄어 웹사이트의 로딩 시간을 줄여주고 성능을 향상시켜준다는 장점이 있긴 하지만, 문제가 되었던 것처럼 WOFF2 파일임에도 꽤 큰 용량을 차지한다.
폰트 형식
폰트 파일 형식에는 TTF, OTF, WOFF, WOFF2가 있다. 이 중 WOFF2는 압축된 폰트 형식으로, 다른 형식들에 비해 파일 용량이 작다는 장점이 있다.
- TTF(True Type Font): 가장 기본적인 폰트 형식으로, 주로 일반 문서 작성 시 사용된다.
- OTF(Open Type Font): 그래픽 디자인이나 전문적인 출판물 제작에 주로 사용된다. 고해상도 출력이 필요한 작업에 특화되어 있어 선명한 품질을 제공한다.
- WOFF(Web Open Font Format): 웹에서 사용하기 위해 특별히 설계된 형식이다. zlib 압축 알고리즘을 사용하여 TTF나 OTF 대비 약 40% 더 작은 용량을 실현했으며, 웹 페이지 로딩 시간을 단축시킨다.
- WOFF2.0(Web Open Font Format 2.0): 현재 가장 진보된 웹 폰트 형식이다. Brotli 압축 알고리즘을 도입하여 WOFF보다도 약 30% 더 작은 용량을 제공하며, 최신 웹 환경에서 최적의 성능을 발휘한다.
TTF까지 알아보았지만 WOFF가 기본적으로 모든 브라우저를 커버하고 있기 때문에 웹 폰트로는 비교적 작은 용량을 제공하는 WOFF, WOFF2를 사용하면 좋다. WOFF2 경우 Internet Explore는 지원하지 않을 수 있기 때문에 대체 폰트로 WOFF를 사용하기도 한다.
웹 폰트 최적화
큰 폰트 파일 용량이 문제였다는 것을 알았지만 어떻게 개선할 수 있을까? 폰트 용량 문제의 경우는 사실 TTF와 같이 용량이 큰 폰트 형식을 사용하고 있는 것이 문제여서 WOFF나 WOFF2 형식의 폰트로 변환해서 폰트의 용량을 줄인다. 그런데 나의 경우 이미 WOFF2를 사용하고 있기 때문에 다른 방식을 고려해봐야 한다. 해당 포스트에서는 관련하여 서브셋과 관련한 최적화 방법들을 다뤄볼 예정이다.
서브셋 폰트(subset font)
subset이란 부분집합이라는 뜻이다. 따라서 서브셋 폰트란 모든 글자를 지원하는 기존 폰트 파일에서 불필요한 글자를 제거하고 사용할 글자만 추출한 폰트를 말한다. 이는 특히 한글과 같이 글자 수가 많은 문자 체계에서 중요한 기술이다. 영어의 경우 알파벳 26자를 기준으로 대소문자를 포함해도 52자만으로 모든 표현이 가능한 반면, 한글은 조합형 문자임이기 때문에 자음과 모음의 조합으로 생성 가능한 모든 글자가 11,172자에 달한다.
이러한 특성으로 인해 한글 웹폰트는 다양한 최적화 방식을 채택하고 있다. 가장 기본적은 방식은 KS X 1001 표준을 따르는 것으로 사용 빈도가 높은 2,350자를 선별하여 대한민국 국가 표준으로 지정된 한국어 문자 집합을 구성한다. 이는 파일 크기를 최적화할 수 있다는 장점이 있지만, 제한된 글자 수로 인한 표현의 한계가 있다.
이러한 한계를 보완하기 위해 최근의 폰트들은 더 넓은 범위의 글자를 지원하는 방식을 채택하고 있습니다. 확인해보니 현재 프로젝트에서 사용하고 있는 프리텐다드는 Adobe-KR-9 기준을 따라 2,780자를 지원하고 있다. 반면 일부 폰트는 11,172자 전체를 지원하여 가장 포괄적인 한글 표현을 가능하게 하기도 한다. 이러한 다양한 최적화 방식은 사용 목적과 환경에 따라 선택적으로 활용될 수 있으며, 특히 웹 환경에서는 로딩 속도와 표현력 사이의 균형을 고려하여 적절한 방식을 선택하는 것이 중요하다.
다이나믹 서브셋
프리텐다드에서는 다이나믹 서브셋도 지원한다. 다이나믹 서브셋은 웹폰트 최적화를 위한 진보된 방식으로, 페이지에서 실제로 사용되는 글자의 폰트만을 동적으로 다운로드하는 기술이다. 이는 CSS의 unicode-range 속성을 활용하여 구현된다. 각 유니코드 범위별로 폰트 파일이 분리되어 있어, 특정 글자가 웹페이지에서 처음 사용될 때에만 해당 글자에 대한 폰트 파일을 다운로드한다.
프리텐다드 폰트의 경우 이 다이나믹 서브셋을 CDN을 통해 제공하고 있다. CDN URL에서 제공되는 CSS 파일을 살펴보면, unicode-range 속성으로 정의된 각각의 유니코드 범위와 그에 해당하는 폰트 파일들이 매핑되어 있는 것을 확인할 수 있다.
실제 사용시에는 CSS의 @import
구문이나 HTML의 link
태그를 통해 CDN 링크를 연결해두면 된다. 이렇게 설정해두면 각 웹페이지에서 필요한 글자가 나타날 때마다 해당 유니코드 범위의 폰트만 선택적으로 다운로드되어 사용됩니다. 이는 초기 로딩 시간을 크게 단축시키고, 불필요한 데이터 전송을 방지하는 효율적인 방식이다.
예를 들어, 특정 페이지에서 ‘Hello’라는 영문만 사용된다면 영문 유니코드 범위의 폰트만 다운로드되고, ‘안녕’이라는 한글이 추가되면 해당 한글 글자가 포함된 유니코드 범위의 폰트가 추가로 다운로드되는 방식으로 동작한다.
<link rel="stylesheet" as="style" crossorigin href="https://cdnjs.cloudflare.com/ajax/libs/pretendard/1.3.9/static/pretendard-dynamic-subset.min.css" />
서브셋 폰트 제작
서브셋 폰트와 다이나믹 서브셋 중 어느 방식을 선택할지 고민했는데, 다이나믹 서브셋의 경우 페이지를 이동할 때마다 사용된 글자에 맞는 폰트 파일을 다운로드하게 되어 과도한 네트워크 호출이 발생할 수 있고, 이는 성능 저하로 이어질 수 있다는 점이 염려되었다. 따라서 서브셋 폰트로 최적화하기로 했다.
프리텐다드 폰트는 서브셋 폰트를 별도로 제공하지만, 가변 폰트에 대한 서브셋 폰트는 제공하지 않는다. 따라서 직접 제작을 결정했고, 글자 기준으로는 KS X 1001 표준을 선택했다. 프로젝트가 랜딩 페이지였기 때문에 다양한 케이스에 대한 처리가 필요하지 않았기 때문이다.
제작 방법
서브셋 폰트를 생성하기 위해서는 Font Subset Generate Online이나 FontForge Open Source Font Editor 와 같은 서브셋 폰트 생성 사이트를 이용할 수 있다.
외에도 fontTools와 같은 라이브러리를 사용할 수 있는데 fontTools는 폰트 파일을 조작할 수 있는 Python 오픈 소스 라이브러리다. 이 라이브러리에서 제공하는 pyftsubset이라는 도구는 폰트 파일에서 필요한 문자만을 추출하여 용량이 최적화된 서브셋 폰트를 생성하는 데 사용된다.
나는 이 방법 중 라이브러리를 사용해 만들어보고 싶어서 fontTools를 이용해 서브셋 폰트를 생성했다.
fontTools 사용법
우선은 fonttools를 다운로드 받아야 한다. pip는 npm과 같은 패키지 매니저로 pip install
을 통해 패키지를 설치할 수 있다.
pip install fonttools
기본적인 서브셋 폰트 생성 명령어 구조는 다음과 같다.
pyftsubset 원본파일경로 --text="<포함할글자모음>" --output-file=<서브셋폰트저장경로>
만약 ‘가나다라’라는 글자만 포함하려면 다음과 같이 작성해주면 된다.
pyftsubset /content/PretendardVariable.woff2 --text="가나다라" --output-file=/content/PretendardVariableSubnet.woff2
이렇게 생성한 서브셋 폰트 설치 후 확인해보면 ‘가나다라’ 글자만 지원하는 것을 확인할 수 있다.
서브셋 폰트 생성 시에는 필요한 글자를 선택하는 것뿐만 아니라, 폰트의 정상적인 표시를 위해 사용되는 여러 정보들도 함께 고려해야 한다. 필요한 정보가 누락될 경우 폰트가 제대로 표시되지 않을 수 있기 때문에, fontTools에서 제공하는 다양한 옵션들을 잘 살펴보고 적용하는 것이 중요하다. 이는 fontTools 가이드 문서에서 확인할 수 있다.
주요 옵션은 다음과 같다. 외에도 다양한 옵션들이 있다.
--unicodes
: 유니코드 범위 지정 (예: U+0000-00FF)--layout-features
: OpenType 레이아웃 기능 지정 (예: kern,liga)--flavor
: 출력 포맷 지정 (WOFF, WOFF2, TTF 등)--glyph-names
: 글리프 이름 유지--symbol-cmap
: cmap 심볼 유지--no-hinting
: 힌팅 정보 제거--with-zopfli
: WOFF 압축에 Zopfli 알고리즘 사용
최적화 결과
fontTools로 제작한 서브셋 폰트는 102KB, 기존 폰트(2.1MB) 대비 용량이 약 80% 감소했다. 이를 통해 폰트 다운로드 시간이 7초에서 2초로 단축되었고, 시스템 폰트 로딩 속도도 개선되어 랜딩 페이지의 FOUT(Flash of Unstyled Text) 현상이 개선되었다.
+)
해당 글이 글또 프론트 큐레이션 글로 선정 되었다 🥳
참고 문서