웹 폰트의 동작 방식, 웹 폰트 최적화 방법에 대해 정리하고 코드에 적용해보기
웹 폰트는 웹 페이지에서 사용되는 폰트를 온라인 상에서 다운로드하여 쓸 수 있는 기술입니다.
웹 폰트는 사용자가 미리 해당 폰트를 시스템에 설치하지 않아도 사용이 가능하기 때문에, 웹 페이지를 사용하는 모든 사용자가 동일한 폰트로 일관된 디자인을 경험할 수 있습니다.
하지만 잘못된 방법으로 사용하게 된다면 오히려 웹 페이지의 성능을 낮추고 사용자 경럼을 해칠 수 있기 때문에, 사용 시 주의해야 합니다.
웹 폰트는 사용자 로컬 기기에 저장되어 있는 폰트 파일을 사용하는 것이 아닌, 온라인 상에서 폰트 파일을 다운로드하여 사용하게 됩니다.
즉, 웹 폰트를 사용하는 페이지에 접근할 때 웹 서버 이외에 또 다른 서버로 웹 폰트를 요청하게 됩니다.
이때 브라우저가 렌더링 하는 과정은 아래와 같습니다.
출처: web.dev
웹 폰트를 사용할 때 사용자 경험 측면에서 고려할만한 상황은 두 가지가 있습니다.
바로 FOIT(Flash Of Invisible Text)
*와 FOUT(Flash Of Unstyled Tex)
**입니다.
이 두가지 현상은 사용자 경험을 저하시키고 개발자가 의도하지 않은 동작을 합니다. 이를 방지하기 위해 웹 폰트 최적화가 필요합니다.
*FOIT: 브라우저가 웹 글꼴을 다운로드하기 전에 텍스트가 보이지 않는 현상
**FOUT: 브라우저가 웹 글꼴을 다운로드하기 전에 텍스트가 대체 글꼴로 렌더링되는 현상
웹 폰트를 사용한다는 것은 결국 파일을 추가로 다운로드하는 것입니다. 그렇다면 다운로드 시간을 단축시키는 방법에는 무엇이 있을까요? 가장 쉬운 방법은 다운로드할 대상 파일의 크기를 줄이는 것입니다.
일반적으로 사용자 기기에서 사용하는 것으로는 TTF(True Type Font)나 OTF(Open Type Font)가 있지만, 웹 폰트에서는 이 외에도 다른 형식이 있습니다.
WOFF는 모질라 파운데이션과 오페라 소프트웨어, 타입키드 그리고 마이크로소프트가 협력해서 개발한 웹 폰트 형식입니다. 이는 기존의 폰트 형식인 TTF, OTF와 동일하게 동작하지만 압축을 통해 더 작은 파일 크기를 가집니다.
WOFF는 2010년 첫 출시되었으며, 이후 2012년에 W3C(World Wide Web Consortium)에 권장 사항으로 등록되어 현재는 거의 모든 브라우저에서 이 형식을 지원합니다.
@font-face {
font-family: "NanumGoGothic";
src: url("font/NanumGoGothic.woff") **format("woff")**;
font-weight: normal;
font-style: normal;
}
WOFF의 사용 방법은 기존 TTF, OTF 파일을 사용하는 방법과 크게 다르지 않습니다. css에서 font-face로 폰트를 정의해서 사용할 때 src에 format(”WOFF”)
로 어떤 형식을 사용할 것인지 명시하면 됩니다.
이름에서 유추 가능하듯 WOFF2는 WOFF를 개선한 버전입니다. 동이랗게 웹 폰트를 압축하고 최적화히기 위한 웹 폰트 형식이지만 WOFF2는 WOFF 대비 약 30~50% 정도 더 작은 파일 크기를 가집니다. 이는 WOFF보다 우수한 성능을 가졌지만 WOFF보다 낮은 호환성을 가지고 있어 WOFF를 폴백(fallback) 폰트로 같이 사용하고 있습니다.
@font-face {
font-family: "NanumGoGothic";
src: url("font/NanumGoGothic.woff2") format("woff2"),
**url("font/NanumGoGothic.woff") format("woff");**
font-weight: normal;
font-style: normal;
}
폴백 폰트를 설정하는 방법은 위와 같이 WOFF2 폰트 다음으로 동일하게 작성하면 됩니다. 이렇게 작성하면 WOFF2를 지원하지 않는 브라우저라도 자동으로 WOFF 폰트를 사용해 렌더링을 진행하게 됩니다.
웹 폰트는 로컬 기기에 저장돼 있는 폰트 파일을 사용하는 것이 아닌, 온라인 상에서 폰트 파일을 다운로드하여 사용합니다. 그렇다면 실제로 웹 폰트를 다운로드하는 과정은 어떻게 되는지 아래 코드를 통해 알아보겠습니다.
<html>
<head>
<link
href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR&display=swap"
/>
<link href="./style.css" ref="stylesheet" />
</head>
<body>
<p>반갑습니다. Eunjee 입니다.</p>
</body>
</html>
* {
font-family: 'Noto Sans KR';
}
<head>
태그에서 구글의 웹 폰트와 css 파일을 불러와 적용시키는 간단한 예제 코드입니다. 크롬 개발자 모드 기능 중 네트워크 탭을 사용해 그 과정을 자세히 살펴보겠습니다.
우선 브라우저는 html 파일을 제일 먼저 요청하여 다운로드합니다. 그 다음 css 파일을 요청하고, 마지막으로 css 파일 내 font-face로 설정된 폰트 파일을 요청합니다.
즉, html과 css를 모두 불러오기 전까지 폰트 파일은 다운로드를 시작조차 하지 못한다는 것인데요, 폰트 파일이 준비되지 않으면 다른 대체 폰트가 보이게 되고 이후 폰트 파일이 준비되면 다시 한 번 리렌더링 과정을 거칩니다. 이는 앞서 소개했던 FOUT 현상입니다.
이러한 현상을 최소화하기 위해, 즉시 필요한 웹 폰트를 미리 로딩하도록 설정할 수 있습니다.
<head>
태그 내부에 rel="preload"
속성을 넣어 폰트를 요청해 리소스 대기열의 우선순위를 높일 수 있습니다.
<link
**rel="preload"**
href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@100;300;400;500;700;900&display=swap"
/>
이제 폰트 파일은 css 파일이 모두 로딩될 때까지 기다리지 않고, css 파일 요청과 동시에 요청하여 가져오는 모습을 보여줍니다. 이처럼 폰트 파일 요청을 미리 진행하여 FOUT 현상을 최대한 방지할 수 있습니다.
하지만 이를 남용하게 될 경우 웹 페이지가 보이는 시간이 증가할 수 있기 때문에 명확하게 필요한 폰트 파일만 설정해야 합니다.
하지만 위의 방법의 내부를 살펴보면 웹 폰트를 로드하는 css 코드 말고는 아무 내용도 없습니다. 브라우저는 실제 페이지 내에서 사용할 폰트만 다운로드 해주긴 하지만, 불필요한 css 파일을 다운로드 받는 것은 낭비입니다.
CSS 파일 전체를 다운로드하기 보다는 필요한 폰트 파일을 직접 설정하는 방법이 네트워크 요청 수를 줄이고 폰트 파일의 다운로드 시점을 앞당길 수 있는 방법입니다.
위 예시 링크를 눌러보면 많은 font-face 코드가 있는 것을 확인할 수 있습니다.
이 중에 실제로 필요한 font-face를 찾아 웹 폰트 설정을 해보도록 하겠습니다.
‘한글’은 우리나라만 사용하지만 ‘영어’의 경우 여러 나라에서 사용됩니다. 그리고 나라마다 언어를 다르게 사용합니다. 하나의 영어 폰트는 여러 나라에 쓰일 것을 대비하여 여러 버전이 존재합니다.
latin
: 우리가 ‘영어’하면 생각나는 보편적인 알파벳들이 들어있습니다. 미국과 유럽권 등 주로 서구권 국가에서 사용하는 알바벳들이 들어있는 버전latin-ext
: 주요 영어권 국가들이 알파벳 + 유럽 대부분의 국가에서 사용하는 알파벳이 담긴 버전vietnames
: 베트남어에 사용 되는 알파벳이 담긴 버전이와 비슷한 맥락으로 spanish 등 많은 버전 들이 존재합니다. 그럼 이제 어떤 버전의 웹 폰트를 사용하는지 알 수 있습니다!
사용할 폰트를 다운로드하여 웹 폰트를 설정하도록 하겠습니다.
@font-face {
font-family: 'Nanum Gothic';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(https://fonts.gstatic.com/s/nanumgothic/v23/PN_3Rfi-oW3hYwmKDpxS7F_D-dje5Hkr3w.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
❗
font-style: normal
이 외의italic
등 여러 스타일을 사용한다면 각각 폰트를 다운로드 받아줘야 합니다.
이 작업은 영어 폰트가 아닌 한글 폰트에 유용한 작업입니다. 한글은 여러 자음 모음을 혼합하여 글자를 만드는 형식이기 때문에 폰트 하나가 무려 11,172 가지나 됩니다. 하지만 ‘괆’과 같이 실생활에 전혀 쓰일 것 같지 않은 글자는 이용자에게 보여줄 일이 없기 때문에 폰트 파일 안에 포함되어 있을 필요가 없습니다.
이런 식으로 필요없는 글자들을 폰트 파일 안에서 제거해서 만드는 작업을 서브셋팅이라고 합니다.
저는 포트폴리오 사이트에 사용할 필요가 없기 때문에 서브셋을 따로 설정해주지는 않았습니다.
서브셋을 도와주는 참고 사이트
위의 font-face woff2 형식만을 지원한다는 단점이 있습니다. woff2는 구형 브라우저에서 지원을 하지 않을 수 있기 때문에 가능하다면 많은 형식의 폰트 파일을 font-face에 명시해주는 것이 좋습니다.
위 사이트를 통해 하나의 폰트 파일 형식으로 다른 .ttf
, .otf
, .eot
, .woff
파일로 컨버터 할 수 있습니다.
폰트 형식을 모두 다운로드 후, 아래처럼 적용하면 됩니다.
@font-face {
font-family: 'JetBrains Mono';
font-style: normal;
font-weight: 400 700;
font-display: swap;
src: url("/src/static/fonts/JetBrainsMono.woff2") format("woff2"),
url("/src/static/fonts/JetBrainsMono.woff") format("woff"),
url("/src/static/fonts/JetBrainsMono.ttf") format("truetype"),
url("/src/static/fonts/JetBrainsMono.otf") format("opentype"),
url("/src/static/fonts/JetBrainsMono.eot") format("embedded-opentype");
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
여기서 주의할 점은, 단순히 url만 설정해주는 것이 아닌, format 또한 명시해줘야 합니다.
브라우저는 자신이 적용할 수 있는 폰트 파일 형식인지를 이 format 형식을 보고 판단할 수 있기 때문입니다. format 형식이 없다면 브라우저는 해당 폰트 파일을 지원할 수 없음에도 일단 해당 url을 다운로드 해보고 적용할 수 없다고 판단하면 다음 fallback 웹 폰트를 다운로드 하는 식으로 동작합니다.
font-display는 브라우저가 웹 폰트를 어느 시점에 적용할지를 결정하는 속성입니다. 이를 명시해주지 않으면 브라우저는 자신의 기본 설정에 따라 웹 폰트의 적용 시점을 결정해 버립니다. 크롬, 사파리, 파이어폭스 브라우저는 웹 폰트가 다운로드 완료될 때까지 글자를 보여주지 않다가 웹 폰트가 모두 다운로드 된 후에 글자를 보여주는 FOIT 방식을 선택하고 있으며 엣지와 IE 브라우저는 웹 폰트를 다운 받는 동안 기존에 설정 된 fallback 폰트를 이용자에게 대신 보여주고 다운로드가 완료되면 웹 폰트를 적용하는 FOUT 방식을 기본으로 선택하고 있습니다.
여기서 font-display 값을 block으로 하면 FOIT 방식을, swap으로 하면 FOUT 방식을 폰트 적용 방식으로 설정하게 됩니다.
font-display: swap
이 일반적으로 사용됩니다. 이용자가 대기 시간 동안 보지 못하는 글자가 있다면 사용성이 크게 저하될 수 있기 때문입니다.
그렇다면 FOIT 방식에서 글자를 보여주지 않는 시간이 긴 시간이 아니라 0.1초라면 어떨까요?
0.1초라면 사실상 사용자가 인지하기 어려울 정도의 찰나의 시간이며 웹 폰트가 다운로드 완료되는 데에도 충분한 시간입니다. 이 같이 FOUT와 FOIT 방식의 중간에 있는 방식을 적용하기 위해서는 font-display: fallback
으로 값을 설정해주면 됩니다.
출처: eunjee github
이전에는 font-face
를 사용만 해본 정도여서 무슨 속성이 있는지, 정확히 왜 사용하는지 왜 font 파일을 다운받아 사용하는지 용량 때문인지 어림짐작 정도만 했었는데 이번 기회를 통해 최적화 하는 방법을 알게 되었다. 사용자 경험을 위해 앞으로 프로젝트에 폰트는 무조건 최적화하는게 좋을 것 같다. 페이지 다운로드 속도가 느리다면 font-display를 잠시 swap으로 주고 성능 개선을 하는 방법도 좋을 것 같다는 생각이 들었다. 다국어를 지원한다면 unicode-range 설정도 해줘야 하는데 추후에 이 것도 정리하고 적용해보고 싶다.
원본 보러가기: [FE 성능 최적화] 웹폰트 최적화