[TL;DR]
기본적으로 사용하는 폰트를 최소화하고, 폰트 사이즈를 줄여 'woff2' 포맷 파일로 'preload' 한다.
폰트가 적용된 글자수가 적다면 불필요한 글자를 제거한 서브셋 폰트를 사용하자.
잘 모르겠다면 구글 웹폰트를 사용하자. 쓰려는 폰트가 없거나 더 욕심나면 커스텀 하자.
디자이너와 개발자 모두 읽을 수 있도록 가벼운 수준으로 작성했으니 참고 부탁드립니다.
[목차]
1. 사용 폰트 최소화
2. font-display - FOUT vs FOIT
3. 폰트 사이즈 줄이기
4. 폰트 호출 시점 앞당기기
5. 구글 웹폰트
1. 사용 폰트 최소화
기본적으로 사용하는 폰트는 최소화하는 게 좋다. 당신이 디자인을 하든 개발을 하든 모두 해당된다. 나처럼 열심히 공부하지 않은 날라리라도 모던 디자인을 관통하는 미스 반 데어 로에의 'Less is More'를 모를 리 없고, 디터 람스의 'Less but Better'의 철학에 공감하지 않을 수 없다. 디자인을 공부하면 할수록 공감하지 않을 수 없다. 물론 기능이 형태를 따라간다는 새로운 바람이 거침없다 하더라도 말이다. 그런 시대적 흐름과는 별개로 'Less is More'는 클래식이다.
미학적이든 기술적이든 실제로 폰트를 덜 쓰는 게 좋은데, 웹이라고 다르지 않다. 느린 웹사이트를 들어가 본 적 있지 않는가. 폰트가 깜빡하고 나타나거나 '맑은고딕', '돋움' 등 시스템 폰트로 보이던 폰트가 갑자기 다른 폰트로 바뀐다거나 하는 경험이 분명 있을 것이다. 이런 경험을 돌이켜 생각해 보면, ui/ux 는 말할 것도 없고 네트워크 비용과 시간 등 폰트를 불러오는데 들어가는 비용이 생각보다 비싸다는 걸 알 수 있다. 그래서 최적화도 필요한 것이고, 굳이 또 폰트 성능 개선을 하려고 하는 이유다.
요샌 폰트 시장도 발전을 많이 해서 개별적인 폰트 하나가 아니라 폰트 패밀리가 제대로 디자인된 게 많다. 그래서 그런 폰트를 사용할 때 '난 한 개만 썼는데...' 하더라도 멋모르고 사용하면 필요 없는 폰트 패밀리 전체를 호출하게 되기도 한다.
구글 Noto Sans : Noto Sans KR
프리텐다드 : Pretendard
보통 폰트 굵기가 100부터 900까지 제대로 된 패밀리 셋이 있는 폰트라면 못해도 noto sans 처럼 6가지 정도 폰트를 호출하게 되는 거고, 거기에 브라우저별 호환성까지 고려하면 woff2, woff, ttf/otf, eot 등 벌써 6 x 4 = 24개가 필요해진다. 9가지 패밀리 서체라면 9 x 4 = 36개로 불어난다. 크롬에서 Pretendard-1.3.6 cdn을 호출하면 woff2, woff를 불러오기 때문에 프리텐다드의 경우엔 18개다. eot는 IE(인터넷 익스플로러)가 은퇴했기 때문에 대응을 하지 않은 듯 하다. 이처럼 IE를 버린다고 eot를 제외하더라도 여전히 많은 숫자다.(물론 호출은 하나만 하겠지만) 내 경우엔 '오늘의 날씨와 경제'라는 타이틀 글자에만 특정 폰트를 적용했기 때문에 해당 폰트 전체가 필요없는 상황이다. svg나 이미지로 만들 수도 있으나, 폰트 최적화 후 계속 사용하려고 한다. 따라서 최적화가 필요하다.
상황이 이렇기 때문에 디자인 전에 폰트 사용과 관련해서 개발자와 합(?)을 맞추고 시작하는 걸 권장한다. 디자이너라면 적어도 폰트가 웹에 어떻게 적용되는지 기본 원리 정도는 알아두는 편이 좋다. 디자인 시스템이 없는 상황의 개발자라면 디자이너에게 폰트 가이드를 요청하거나 물어보자. 전체적으로 최소한 어떤 폰트와 굵기가 사용되었는지 확인이 필요하다. 디자이너는 폰트의 크기와 굵기를 다양하게 사용하는데, 이걸 캐치하지 못하는 개발자가 많다. 혹은 우선순위를 낮게 두어 까먹는 경우가 생기기도 한다. 디자이너 입장에선 디자인대로 구현되지 않아, 바빠 보이는데 말은 못 하고 내심 서운해하고 있을지도 모를 일이다.
2. font-display
본격적인 폰트 성능 개선에 앞서 폰트의 설정을 알고 넘어가자. font-display는 폰트가 적용되는 방식을 결정할 수 있다. 만약 인터넷 환경이 빠르고 호출되는 자원이 많지 않다면 인지하기 어려운 수준이지만 아래 첨부 영상처럼 느린 인터넷(와이파이) 환경이라면 폰트의 호출 여부에 따라 시각적으로 확연히 구분 된다.
Name | font-display |
For | @font-face |
Value | auto - 브라우저 기본값 block - FOIT (timeout = 3s) swap - FOUT fallback - FOIT (timeout = 3s), 3초 후에도 불러오지 못하면 기본 폰트로 유지, 이후 캐시 optional - FOIT (timeout = 1s), 이후 네트워크 상태에 따라 기본 폰트로 유지할지 웹폰트를 적용할지 결정, 이후 캐시 |
Initial | auto |
display 속성 중 swap과 block이 어떻게 다른지 아래 영상으로 확인해보자.(개인적으로 fallback과 optional은 거의 쓸 일이 없어서 패스)
네트워크 속도를 느리게 만들어서 좀 더 확인하기 쉽도록 세팅했다. 폰트가 깜빡하고 나타나는 것을 볼 수 있는데, 위에 있는 'font-display: swap' 옵션은 FOUT방식으로 폰트가 다운로드 되기 전까지는 기본 폰트로 보여지고, 아래 'font-display: block' 옵션은 안보이다가 타임아웃 3초 후 나타나는 것을 볼 수 있다. 3초를 기다려도 다운로드가 되지 않아 일단 기본 폰트로 보여줬다가 바뀌는 모습을 볼 수 있다. 다운로드 시작 시간과 걸린 시간을 알려면 개발자도구 > 네트워크 탭에서 타이밍 탭을 확인하면 된다.
보다 명확한 네트워크 테스트를 위해 빠른 3G 통신 환경으로 설정하다 보니 많이 느린 점 참고.
FOUT vs FOIT
다시 한 번 정리하자면,
FOUT은 Flash of Unstyled Text의 약자로 브라우저가 폰트를 다운로드하기 전에 대체 폰트(정해진 게 없다면 시스템 폰트)로 보이는 상황을 말한다.
FOIT은 Flash of Invisible Text의 약자로 브라우저가 폰트를 다운로드 하기 전에 텍스트가 보이지 않는 상황을 말한다.
위에서 영상으로 확인했다시피 FOUT과 FOIT는 사용성이 다르다. 따라서 각자의 상황에 맞게 적절한 선택을 하도록 하자. FOIT 방식을 선택했다면 다운로드 되서 폰트가 나타날 때 '페이드 인' 효과를 적용하면 좀 더 자연스럽게 인지되는 편이다.
3. 폰트 사이즈 줄이기 (서브셋 폰트 만들기)
폰트 사이즈는 woff2 파일 형식으로 변환을 하면 자연스럽게 줄어든다. 브라우저 하위 호환을 위해 woff도 병행해서 쓴다. 이번엔 '오늘의 날씨와 경제'라는 8글자만 별도로 필요해서 겸사겸사 서브셋 폰트를 만들어 용량을 직접 줄이려고 한다. 여러 방법이 있다. 서브셋 폰트는 일본어로 된 서브셋 폰트 메이커나 fontTools 라이브러리를 사용해 만들 수 있다. 하지만 로그인이나 다운로드, 설치가 필요없는 transfonter 웹서비스도 있다. 이게 간편하다.
일단 줄이고 싶은 폰트 ttf 파일을 준비한다. 그리고 아래처럼 옵션을 선택한 다음 Add fonts 버튼을 눌러 폰트를 업로드 한다. 옵션을 설정한다. 필요한 글자를 입력하고, 여기선 swap 방식을 이용했다. display옵션은 변경할 수 있으니 상관없다.
이제 Convert 버튼을 눌러 보면 변환이 진행되고 다운로드 가능한 링크가 나온다.
7060kb 파일이 9kb가 되었다. 8글자만 변환했더니 용량이 엄청나게 줄어들었다.
다운로드 된 파일을 풀어보면 아래와 같다.
demo 파일은 당연히 필요없고, css, woff, woff2만 있으면 된다. stylesheet.css 파일을 열어보면 복붙하기 좋은 코드가 나온다. 추가적으로 local을 더했다. 상위 순서부터 호출하고, local에 있다면 local에서 불러오는 게 당연히 제일 빠르다.
@font-face {
font-family: 'Gowun Dodum';
src: local('subset-GowunDodum-Regular.woff2'),
url('subset-GowunDodum-Regular.woff2') format('woff2'),
url('subset-GowunDodum-Regular.woff') format('woff');
font-weight: normal;
font-style: normal;
font-display: swap;
}
이렇게.
display 속성은 여기서 변경하면 된다. 일단 swap 옵션을 그대로 사용할 예정이다.
바로 적용해서 다시 테스트 해봤다.
예상대로 3.2kb 짜리 woff2가 순식간에 적용된다.
서브셋 폰트를 적용했기 때문에 '오늘의 날씨와 경제' 글자만 변환된 것을 확인할 수 있다.
호출 시점이 1.23초부터 시작하기 때문에 만약 다른 호출이 많다면 더 밀려서 늦어질 수 있다. 이때 preload 옵션을 넣어 주면 호출 시점을 앞으로 당길 수 있다.
4. 폰트 호출 시점 앞당기기 (preload)
<link rel="preload" href="./subset-GowunDodum-Regular.woff2" as="font" type="font/woff2" crossorigin="anonymous">
미리 다운로드 받아 놓은 woff2 파일을 font.css 에서 사용하기 때문에 폰트가 더 빠르게 적용된다.
마지막으로 위에서 봤던 평화평창체를 이 방식으로 적용해 봤다. 실제 얼마나 차이가 날지 체감 속도를 비교해 보자. 실행 환경도 빠른 3G 통신으로 동일하다. 캐시없이 새로고침 한 것이다.
css를 호출하기도 전에 폰트 파일부터 읽어오기 때문에 깜빡임도 거의 없이 적용되는 것을 알 수 있다. 다른 변수들이 있을 수 있겠지만 기본적으로 이정도면 폰트 최적화는 상당 부분 진행된 상태다. 글자별 유니코드를 아예 css 안에 넣는 방법도 있지만, 여기선 그렇게까지 할 필요는 없을 것 같다. (유니코드를 넣는다는 게 어떤건지 궁금하신 분은 아래 쪽 코드 예시를 보세요)
캐싱을 하면 훨씬 더 빠르다. 하지만 외부 폰트라면 캐싱을 임의로 조절하긴 어렵다. 그래서 이제 대중적으로 많이 사용하는 구글 웹폰트에 대해 잠깐 언급하려고 한다.
5. 구글 웹폰트
https://googlefonts.github.io/korean/
Google Fonts는 머신 러닝에 기반을 둔 최적화 기술을 통해 한글 폰트를 동적으로 분할 다운로드합니다.
웹상의 방대한 한국어 문서를 분석한 결과, Google은 주제에 따라 사용되는 글자의 패턴을 발견하고, 패턴에 따라 한글 폰트에 포함된 17,388개의 글리프를 100여 가지 그룹으로 나누었습니다. 여기에 Google Fonts는 사용자가 웹 페이지를 불러올 때, 폰트 전체를 다운로드하는 대신 내용을 표시하는 데 꼭 필요한 몇 가지 그룹만을 선택적으로 다운로드 하는 방식으로 폰트를 제공합니다. 이 기술을 적용한 Google Font를 사용하면 보다 빠르게 폰트 전체를 다운로드한 것과 다름없는 페이지를 제공할 수 있습니다.
또한 Google Fonts API의 사이트 간 캐싱(cross-site caching)을 통해 해당 폰트가 여러 웹사이트에서 사용될수록 전체 다운로드 시간은 줄어들고, 한글 웹 폰트를 둘러싼 사용자 경험은 그만큼 개선될 것입니다.
그렇다. 구글 웹폰트는 알아서 동적 분할과 캐싱을 지원하고 있었다.
실제로도 그러한지 한 번 확인해 보자. 아래처럼 사용중인 '고운 돋움'을 적용한 간단한 html 페이지를 만들었다.
<!DOCTYPE html>
<html lang="kr">
<head>
<meta charset="UTF-8">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Gowun+Dodum&display=swap" rel="stylesheet">
<style>
body {
font-family: 'Gowun Dodum', sans-serif;
}
</style>
<title>Document</title>
</head>
<body>
<h1>오늘의 날씨와 경제</h1>
</body>
</html>
호출한 결과를 크롬 개발자도구 > 네트워크 탭에서 보면,
이처럼 동적으로 필요한 부분만(쪼개진 파일들) 호출하는 것을 볼 수 있다.
하나 클릭해 보면,
응답 헤더에 캐시 설정이 되어 있는 것을 알 수 있다. 구글이 설정한 max-age 값을 보면 유효기간 1년짜리인 것을 알 수 있다.(60 * 60 * 24 * 365 = 31536000초) 캐시를 삭제하지 않는 이상 1년동안 다시 다운로드 하지 않는다. 그럼 당연히 다시 호출했을 때 캐시를 불러오기 때문에 굉장히 빠르다.
이렇게 100~180밀리초에서 0밀리초 정도로 빨라진 걸 확인할 수 있다. 내용이 많다면 더 오래 걸린다.
커버리지 탭을 통해 실제로 적용된 css를 열어 보면,
/* [0] */
@font-face {
font-family: 'Gowun Dodum';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(https://fonts.gstatic.com/s/gowundodum/v7/3Jn5SD_00GqwlBnWc1TUJF0AFPJJ3oAcQhA685dKXogXvvOvaew.0.woff2) format('woff2');
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;
}
~~~ 생략 ~~~
/* [119] */
@font-face {
font-family: 'Gowun Dodum';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(https://fonts.gstatic.com/s/gowundodum/v7/3Jn5SD_00GqwlBnWc1TUJF0AFPJJ3oAcQhA685dKXogXvvOvaew.119.woff2) format('woff2');
unicode-range: U+20-22, U+27-2a, U+2c-38, U+3a-3b, U+3f, U+41-47, U+4a-4c, U+4f-5d, U+61-7b, U+7d, U+a1, U+ab, U+ae, U+b7, U+bb, U+bf, U+2013-2014, U+201c-201d, U+2122, U+ac00, U+ace0, U+ae30, U+b2e4, U+b85c, U+b9ac, U+c0ac, U+c2a4, U+c2dc, U+c774, U+c778, U+c9c0, U+d558;
}
글자별 유니코드 서브셋으로 쪼개진 woff2 파일을 호출해 오는 것을 알 수 있다. '오늘의 날씨와 경제'로 시작하는 텍스트 출력에 120개로 쪼개진 woff2를 호출한다. 이마저도 개선하고 싶다면 저 위에서 언급한 것처럼 직접 필요한 서브셋 woff2 파일을 만들면 된다.
이정도만 되도 충분히 사용성이 좋은 편인 것 같다.
역시 갓구글. Don't Be Evil, 구글!
참,
혹시 유니코드로 바꿔보고 싶은 분은 아래 지식잡식님 글에 있는 변환기를 이용해 보세요.
한글 ↔ 유니코드 간편 변환기
구글 웹폰트
한글 웹폰트 찾기 좋은 사이트
[참고] - 지식 공유 감사합니다.🙏
프론트엔드 성능 최적화 가이드 - 유동균
구글웹폰트 : https://fonts.google.com/knowledge/using_type/using_web_fonts
네이버D2 : https://d2.naver.com/helloworld/4969726
'개발 > Etc' 카테고리의 다른 글
토이프로젝트 Plus Typo (0) | 2023.03.20 |
---|---|
git 간단 명령어 모음 (0) | 2023.01.25 |
Cumulative Layout Shift(누적 레이아웃 이동, CLS) (0) | 2023.01.11 |
npm trends _ 라이브러리 선택할 때 유용한 서비스 (2) | 2023.01.11 |
웹 성능 개선 - 이미지 lazy loading(리액트) (0) | 2023.01.10 |