[CSS, JS] 웹 폰트 최적화 하는 방법

제리추·2023년 11월 5일
3
post-thumbnail

1년 전쯤 토이 프로젝트를 제작할 때 선배 개발자분께서 코드 리뷰를 달아 주셨다. 지금 생각해 보면 그 당시에는 코드를 짜기 급급했고 기본적인 개념을 간과하며 코드를 작성했던 것이 아쉽다. 이 부분을 회사 코드에 적용하며 다시 공부했고 기본 개념을 정리하며 글을 쓴다!

🐹 들어가기 앞서

1) 웹 폰트란 무엇일까요?

  • 웹 폰트는 로컬 폰트 설치 여부와 관계없이 온라인의 특정 서버에 위치한 폰트를 다운로드해 화면에 표시하는 웹 전용 폰트이다.

  • 웹 폰트의 장점으로는 사용자들이 해당 폰트가 없더라도 모두에게 디자이너가 의도한 대로 폰트 적용이 가능하다.

  • 웹 폰트의 단점으로는 용량이 무겁고 특정 서버에 위치한 폰트 리소스를 다운로드해서 사용하기 때문에 웹 속도가 저하된다. 또한 사용자의 네트워크 환경이 좋지 않은 경우 레이아웃이 변하는 현상이 발생해 UX에 좋지 않다.

2) 브라우저의 동작과 웹 폰트

  • 들어가기 앞서 폰트 최적화를 하기 위해 브라우저의 동작 과정을 알아야 한다.
  • 아래 사진은 브라우저의 렌더링 원리를 의미한다. 신입 프론트엔드 개발자라면 브라우저 동작 원리는 기본사항이며 단골 면접 질문이다 🫠

브라우저 동작 과정

순서설명
T0Loading 과정을 거쳐 HTML 마크업을 읽어낸다.
T1DOM Tree를 빌드하고 CSS 마크업을 처리한다.
T2CSSOM Tree를 빌드한다. 그 후 생성한 DOM 및 CSSOM 트리를 결합한다. 이 시점에서 폰트 리소스를 요청한다.
T3렌더링 트리를 형성하고 렌더링 트리에서 각 노드의 형태를 계산하고 box-model을 생성한다. Layout 과정을 거쳐 개별 노드를 화면에 그리는 과정을 거쳐 렌더링이 이루어진다.
  • 위 과정을 보면 폰트 리소스 요청은 다른 리소스 요청보다 늦게 이뤄진다.
  • 이로인해 FOIT, FOUT 이 현상이 발생하게 된다. FOIT, FOUT의 개념을 알아보자.

3) FOIT, FOUT 이란?

FOIT, FOUT 이미지

  • 위 이미지는 2016년에 미국 상원 의원인 Mitt Rommey에 관한 기사에서 웹 폰트 리소스가 다운로드 되기 전과 후의 화면을 비교한 사진이다. 단어의 Not을 웹 폰트로 지정해 의미를 강조하려 했지만 웹 폰트 리소스 다운로드가 늦어지면서 Not이 없이 기사에 노출이 되었다. 노출된 시간이 짧지만 원래 의미와 정반대에 의미를 사용자들에게 제공하면서 웹 폰트의 문제점이 이슈가 되었다.

  • 우리가 웹 사이트에 처음 진입할 때 발생하는 현상이다. 이러한 현상이 나타나는 이유는 웹 브라우저의 동작 원리에서 봤듯이 폰트 리소스를 늦게 받아오게 되어 발생하는 현상이다.

  • FOIT(Flash of Invisible Text)이란? 폰트 리소스를 완전히 다운로드 하기 전까지 텍스트를 보여주지 않는 것이다. 현재는 FOIT은 크롬, 사파리, 파이어폭스 등에서 폰트를 로드하는 방식으로 사용되고 있다. 그렇지만 크롬에서 테스트해보면 폰트가 완전히 다운되지 않아도 텍스트가 보이는데 이것은 완전한 FOIT이 아니고 3초만 기다리는 FOIT을 사용했기 때문이다. 3초 동안 다운로드를 기다리다가 3초가 지나도 폰트가 다운로드가 되지 않는다면 기본 폰트로 대체한다. 그렇기 때문에 크롬에서 텍스트가 표시되는 페이지는 페이지 로드 후, 3초 동안 텍스트가 보이지 않는다.

  • FOIT은 웹 폰트 다운로드가 완료되면 한 번에 폰트를 보여줄 수 있다는 장점이 있지만, 로딩이 늦으면 비어 있는 텍스트 공간이 발생한다는 것이 단점이다.

  • FOUT(Flash of Unstyled Text)은 폰트의 다운로드 여부과 관계 없이 먼저 대체 텍스트를 보여준 후에 다운로드 되면 폰트를 로드해서 보여준다. 현재 엣지 브라우저에서 사용하는 방식이다.

  • FOUT은 웹 폰트가 다운로드 되기 전에 대체 폰트를 통해 항상 텍스트를 보여줄 수 있다는 장점이 있지만, 이후 다운로드가 되었을 때 글꼴의 높이 및 자간으로 인해 UI 레이아웃이 변경되는 단점을 가지고 있다.

어떤 방식이 더 좋다고 단정 지을 수 없지만 웹사이트에 따라 더 적절한 방법을 사용해야한다. 중요한 것은 폰트를 최대한 최적화 후에 깜빡임 현상을 최소화해서 사용자 경험을 좋게 만드는 것이다. 텍스트가 중요한 경우 FOUT을 중요하지 않는 경우 FOIT을 적용하는 것을 고려해 보자.

🐹 웹 폰트를 최적화 하는 방법 (최대 속도 + 최소 FOUT)

1) 최신 파일의 형식을 사용하자.

파일 크기 : EOT > TTF/OTF > WOFF > WOFF2

Woff2는 웹 글꼴을 최적화하기 위해 작고 효율적인 파일의 형식이다.
IE8을 지원해야 하는 경우가 아니라면 Woff2, woff 외에는 다른 파일의 형식은 필요하지 않다. IE11을 지원할 필요가 없다면 Woff2만 있으면 된다. (IE가 없어져서 아주 편안쓰..👍)

TTF 파일만 있을 때는 온라인 글꼴 변환기와 같은 도구를 사용해서 변환하자. 변환하기 전 오픈소스 라이센스로 글꼴을 사용하지 않는다면 라이센스에서 이를 허용하는지 확인하자.

2) font-display를 사용하자.

위에서 봤듯이 FOIT, FOUT 둘 다 이상적인 현상은 아니지만 사용자가 웹을 처음 방문했을 때 이러한 현상을 직면하게 될 수 있다. 이러한 현상 즉 폰트 적용 시점을 제어하는 방법이 CSS로 font-display 속성을 사용하는 것이다. font-display는 default 값이 auto로 되어 있으며 대부분의 웹 사이트에서는 FOIT을 선호한다. 하지만 웹 사이트의 특성에 맞게 적절한 값을 설정하는 것이 중요하다. font-display에 auto를 제외한 4가지 옵션을 살펴보자.

font-display: swap

@font-face {
  font-family: 'Typefesse';
  src: url('typefesse.woff2') format('woff2'),
    url('typefesse.woff') format('woff');
  font-display: swap;
}
  • FOUT의 효과를 가지고 있고 폰트 리소스 다운로드가 늦어지게 되면 대체 폰트로 렌더링하고, 다운로드가 완료되면 웹 폰트로 전환한다. 기간이 짧든 길든 개발자가 설정한 웹 폰트가 다운로드 된다면 해당 폰트로 변경되기 때문에 레이아웃이 변화될 수 있다.

font-display: block

@font-face {
  font-family: 'Typefesse';
  src: url('typefesse.woff2') format('woff2'),
    url('typefesse.woff') format('woff');
  font-display: block;
}
  • 웹 폰트가 로드될 때까지 브라우저에서 택스트를 숨기도록 할 때 사용한다. 하지만 텍스트가 영원하게 보이지 않게 유지되는 것은 아니다. 폰트가 특정 기간 3초 내에 로드되지 않는다면 브라우저가 대체 글꼴을 사용하고 그 후 로드된 웹 글꼴로 교체한다.

FOUT이 보기 좋지 않아 이 선택을 한다면, 텍스트가 보이지 않으면 페이지의 내용을 읽을 수 없다는 것을 생각해 보자.

font-display: fallback

@font-face {
  font-family: 'Typefesse';
  src: url('typefesse.woff2') format('woff2'),
    url('typefesse.woff') format('woff');
  font-display: fallback;
}

우선 100ms 동안 텍스트가 보이지 않고 그 후에 대체 폰트로 표시된다. 웹 폰트 리소스가 3초 내에 다운로드 되지 않는다면 대체 폰트를 사용한다.

사용자가 사이트를 처음 방문할 때 웹 폰트를 크게 신경 쓰지 않는다면 fallback은 좋은 선택이다.

font-display: optional

@font-face {
  font-family: 'Typefesse';
  src: url('typefesse.woff2') format('woff2'),
    url('typefesse.woff') format('woff');
  font-display: optional;
}

fallback과 비슷한데 ~100ms 동안 텍스트가 보이지 않고 그 후로 대체 폰트로 표시된다. 이후 웹 폰트 리소스가 다운로드 된다면 사용자의 네트워크 상태를 보고 글꼴을 로드할 수 없다고 판단하는 경우 폰트를 캐싱 처리만 하고 글꼴을 교체하지 않는다.

3) 웹폰트 파일을 preload 하자

앞에서 설명했듯이 웹 폰트는 다른 리소스보다 늦게 리소스를 받아온다. 이런 문제점을 최소화하기 위해 웹 폰트 파일을 preload한다. HTML을 사용하면 글꼴을 일찍 받아올 수 있다.

<link rel="preload" href="/typefesse.woff2" as="font" type="font/woff2" crossorigin>

그렇지만 preload의 과도한 사용은 좋지 않다. 많은 양의 폰트들을 preload하게 된다면 로딩 시간이 길어져 장점이 없어지게 된다. 따라서 각 글꼴의 Woff2 형식만 preload 하는 것이 좋다.

4) Local() 옵션을 사용

src: local('Nanum-Gothic'), 
     url(/static_fonts/NanumGothic-Regular.woff2) format("woff2"), 
     url(/static_fonts/NanumGothic-Regular.woff) format("woff");

사용자가의 로컬 환경에 설치되어 있는 폰트는 local() 이라는 구문을 사용해 지정이 가능하다.
local 문법의 사용을 지향한다. 위와 같이 local을 사용하면 해당 폰트가 시스템에 설치되어 있다면 리소스를 요청하지 않는다. format으로 사이즈가 작은 Woff2를 가장 앞에 위치하자.

5) 서브셋 폰트 사용

다양한 방법으로 폰트를 최적화 해봤지만 아직도 폰트의 용량이 크다. 그 이유는 폰트는 모든 글자에 대한 스타일 정보를 가지고 있기 때문이다. 따라서 특정 글자만 폰트를 지정하고 싶은 경우 모든 글자에 대한 폰트 정보를 가지고 있을 필요 없이 특정 글자에 대한 폰트 정보를 가지고 있으면 된다. 이것이 서브셋 폰트라고 한다.

🐹 결론

  • 각 단계 별로 최적화 방법이 있지만 모두 종합하여 적용했을 때 큰 효과를 볼 수 있다. 적용해 보고 lighthouse나 다른 웹 브라우저 성능 테스트 도구를 통해 체크해 보자.
  • 웹 폰트를 사용할 때 Woff2 > Woff, TTF/OTF, EOT 순서대로 사이즈가 작은 format을 사용하는 것이 효율적.
  • local 옵션을 사용해 추가적인 리소스 요청을 하지 않음.
  • preload를 방식을 통해 font resource를 빠르게 받아오기.
  • font-display 속성을 사용해 폰트 적용 시점을 제어하기.
  • 특정 문자만 폰트를 적용하는 경우 서브셋 폰트 활용.

🐹 참조

5 steps to faster web fonts
웹 개발 스킬을 한 단계 높여 주는 프론트엔드 성능 최적화 가이드

profile
안녕하세요. 소프트웨어 엔지니어 제리입니다 🐹

0개의 댓글