WebFont 로드 및 렌더링 최적화

Ilya Grigorik
Ilya Grigorik

필요하지 않을 수 있는 모든 스타일 버전과 사용되지 않을 수 있는 모든 글리프를 포함하는 '전체' 웹 글꼴은 수 메가바이트 크기의 다운로드를 쉽게 발생시킬 수 있습니다. 이 도움말에서는 방문자가 사용할 것만 다운로드하도록 WebFonts 로드를 최적화하는 방법을 알아봅니다.

모든 변형이 포함된 대용량 파일의 문제를 해결하기 위해 @font-face CSS 규칙은 글꼴 모음을 리소스 모음으로 분할할 수 있도록 특별히 설계되었습니다. 예를 들어 유니코드 하위 집합, 고유한 스타일 변형이 있습니다.

이러한 선언을 고려하여 브라우저는 필요한 하위 집합 및 버전을 파악하고 텍스트를 렌더링하는 데 필요한 최소 집합만 다운로드하며, 이것은 매우 편리합니다. 그러나 주의하지 않으면, 주요 렌더링 경로에서 성능 병목 현상이 발생할 수도 있고 텍스트 렌더링이 지연될 수도 있습니다.

기본 동작

글꼴의 지연 로드에는 중요한 함축이 숨겨져 있습니다. 즉, 텍스트 렌더링을 지연시킬 수 있다는 것입니다. 브라우저는 텍스트를 렌더링하는 데 필요한 글꼴 리소스가 무엇인지 인식하기 전에 DOM 및 CSSOM 트리에 종속된 렌더링 트리를 생성해야 합니다. 따라서, 글꼴 요청은 다른 주요 리소스 훨씬 후로 지연되며, 브라우저는 리소스를 가져올 때까지 텍스트를 렌더링하지 못하도록 차단될 수 있습니다.

글꼴 주요 렌더링 경로

  1. 브라우저가 HTML 문서를 요청합니다.
  2. 브라우저가 HTML 응답 파싱과 DOM 구성을 시작합니다.
  3. 브라우저가 CSS, JS, 기타 리소스를 발견하고 요청을 전달합니다.
  4. 브라우저가 모든 CSS 콘텐츠가 수신된 후 CSSOM을 생성하고 이를 DOM 트리와 결합하여 렌더링 트리를 생성합니다.
    • 렌더링 트리가 페이지에 지정된 텍스트를 렌더링하는 데 필요한 글꼴 버전이 무엇인지 나타내면 글꼴 요청이 발송됩니다.
  5. 브라우저가 레이아웃을 실행하고 콘텐츠를 화면에 페인팅합니다.
    • 글꼴을 아직 사용할 수 없으면 브라우저가 텍스트 픽셀을 렌더링하지 못할 수 있습니다.
    • 글꼴을 사용할 수 있게 되면, 브라우저가 텍스트 픽셀을 페인팅합니다.

렌더링 트리가 빌드된 직후에 실행할 수 있는 페이지 콘텐츠의 첫 번째 페인트와 글꼴 리소스에 대한 요청 간의 '경합'으로 인해 '빈 텍스트 문제'가 발생합니다. 이 경우 브라우저가 페이지 레이아웃을 렌더링할 수는 있지만 텍스트를 생략합니다.

WebFonts를 미리 로드하고 font-display를 사용하여 사용할 수 없는 글꼴에서 브라우저가 작동하는 방식을 제어하면 글꼴 로드로 인한 빈 페이지와 레이아웃 변경을 방지할 수 있습니다.

웹폰트 리소스 미리 로드

사전에 알고 있는 URL에서 호스팅되는 특정 웹폰트가 페이지에 필요할 가능성이 높은 경우 리소스 우선순위 지정을 활용할 수 있습니다. <link rel="preload">를 사용하면 CSSOM 생성을 기다릴 필요 없이 중요한 렌더링 경로의 초반에 웹폰트 요청이 트리거됩니다.

텍스트 렌더링 지연 맞춤설정

미리 로드로 인해 페이지의 콘텐츠가 렌더링되었을 때 웹폰트를 이용할 수 있게 될 수 있지만, 이것을 보장하지는 않습니다. 아직 사용할 수 없는 font-family를 사용하는 텍스트를 렌더링할 때 브라우저가 어떻게 동작하는지 고려해야 합니다.

글꼴 로드 중에 보이지 않는 텍스트 방지 게시물에서 기본 브라우저 동작이 일관되지 않는 것을 확인할 수 있습니다. 하지만 font-display를 사용하여 최신 브라우저의 동작 방식을 지정할 수 있습니다.

브라우저 지원

  • Chrome: 60
  • Edge: 79
  • Firefox: 58
  • Safari: 11.1.

소스

일부 브라우저에서 구현하는 기존 글꼴 제한시간 동작과 마찬가지로 font-display는 글꼴 다운로드 수명을 세 개의 주요 기간으로 구분합니다.

  1. 첫 번째 기간은 글꼴 차단 기간입니다. 이 기간에는 글꼴이 로드되지 않으면 글꼴 사용을 시도하는 모든 요소가 대신 보이지 않는 폴백 글꼴로 렌더링해야 합니다. 차단 기간에 글꼴이 성공적으로 로드되면 글꼴이 정상적으로 사용됩니다.
  2. 글꼴 스왑 기간은 글꼴 차단 기간 직후에 발생합니다. 이 기간에는 글꼴이 로드되지 않으면 사용을 시도하는 모든 요소가 대신 폴백 글꼴로 렌더링해야 합니다. 스왑 기간에 글꼴이 성공적으로 로드되면 글꼴이 정상적으로 사용됩니다.
  3. 글꼴 실패 기간은 글꼴 스왑 기간 직후에 발생합니다. 이 기간이 시작할 때 글꼴이 아직 로드되지 않았다면 실패한 로드로 표시되어 정상 글꼴 폴백을 일으킵니다. 그렇지 않으면 글꼴이 정상적으로 사용됩니다.

이러한 기간을 이해하면 font-display를 이용하여 글꼴의 다운로드 여부와 시점에 따라 글꼴을 렌더링하는 방법을 결정할 수 있습니다.

font-display 속성으로 작업하려면 @font-face 규칙에 다음을 추가하세요.

@font-face {
  font-family: 'Awesome Font';
  font-style: normal;
  font-weight: 400;
  font-display: auto; /* or block, swap, fallback, optional */
  src: local('Awesome Font'),
       url('/fonts/awesome-l.woff2') format('woff2'), /* will be preloaded */
       url('/fonts/awesome-l.woff') format('woff'),
       url('/fonts/awesome-l.ttf') format('truetype'),
       url('/fonts/awesome-l.eot') format('embedded-opentype');
  unicode-range: U+000-5FF; /* Latin glyphs */
}

font-display는 현재 다음 범위의 값을 지원합니다.

  • auto
  • block
  • swap
  • fallback
  • optional

글꼴 미리 로드 및 font-display 속성에 관한 자세한 내용은 다음 게시물을 참고하세요.

Font Loading API

<link rel="preload">와 CSS font-display를 함께 사용하면 많은 오버헤드를 추가하지 않고도 글꼴 로딩 및 렌더링을 상당 부분 제어할 수 있습니다. 그러나 추가적인 맞춤설정이 필요하며 실행 중인 JavaScript로 도입된 오버헤드를 발생시키고자 한다면 다른 선택지가 있습니다.

Font Loading API는 CSS 글꼴을 정의하고 조작하며, 해당 다운로드 진행 상황을 추적하고, 해당 기본 지연 로드 동작을 재정의할 수 있는 스크립팅 인터페이스를 제공합니다. 예를 들어 특정 글꼴 버전이 필요하다고 확신할 경우 이 글꼴 버전을 정의하고 브라우저에 글꼴 리소스에 대한 즉각적인 가져오기를 시작하도록 지시할 수 있습니다.

브라우저 지원

  • Chrome: 35.
  • Edge: 79
  • Firefox: 41.
  • Safari: 10.

소스

var font = new FontFace("Awesome Font", "url(/fonts/awesome.woff2)", {
  style: 'normal', unicodeRange: 'U+000-5FF', weight: '400'
});

// don't wait for the render tree, initiate an immediate fetch!
font.load().then(function() {
  // apply the font (which may re-render text and cause a page reflow)
  // after the font has finished downloading
  document.fonts.add(font);
  document.body.style.fontFamily = "Awesome Font, serif";

  // OR... by default the content is hidden,
  // and it's rendered after the font is available
  var content = document.getElementById("content");
  content.style.visibility = "visible";

  // OR... apply your own render strategy here...
});

또한 check() 메서드를 통해 글꼴 상태를 확인하고 해당 다운로드 진행 상황을 추적할 수 있으므로 페이지에 텍스트를 렌더링하기 위한 맞춤형 전략을 정의할 수도 있습니다.

  • 글꼴을 사용할 수 있게 될 때까지 모든 텍스트 렌더링을 보류할 수 있습니다.
  • 각 글꼴별로 맞춤형 제한 시간을 구현할 수 있습니다.
  • 대체 글꼴을 사용하여 렌더링이 차단되지 않도록 하고 글꼴을 사용할 수 있게 되면 원하는 글꼴을 사용하는 새로운 스타일을 삽입할 수 있습니다.

무엇보다도, 위에 설명된 전략을 페이지의 서로 다른 콘텐츠에 맞게 적절히 혼합할 수도 있습니다. 예를 들어, 글꼴을 사용할 수 있게 될 때까지 일부 섹션에서 텍스트 렌더링을 지연시킬 수 있고, 대체 글꼴을 사용한 후 글꼴 다운로드가 완료된 후 다시 렌더링할 수 있습니다.

적절한 캐싱은 필수

글꼴 리소스는 일반적으로 자주 업데이트되지 않는 정적 리소스입니다. 따라서 긴 max-age 만료에 적합합니다. 모든 글꼴 리소스에 조건부 ETag 헤더최적의 Cache-Control 정책을 모두 지정해야 합니다.

웹 애플리케이션이 서비스 워커를 사용하는 경우 대부분의 사용 사례에서 캐시 우선 전략으로 글꼴 리소스를 제공하는 것이 적합합니다.

localStorage 또는 IndexedDB를 사용하여 글꼴을 저장하면 안 됩니다. 이러한 방법에는 각각 고유한 성능 문제가 있습니다. 브라우저의 HTTP 캐시는 글꼴 리소스를 브라우저에 제공하는 최고이자 가장 강력한 메커니즘을 제공합니다.

WebFont 로드 체크리스트

  • <link rel="preload">, font-display 또는 Font Loading API를 사용하여 글꼴 로드 및 렌더링 맞춤설정: 기본 지연 로드 동작은 텍스트 렌더링 지연을 초래할 수 있습니다. 이러한 웹 플랫폼 기능을 사용하면 특정 글꼴에 대해 이 동작을 재정의하고 페이지의 다양한 콘텐츠에 대해 맞춤 렌더링 및 제한 시간 전략을 지정할 수 있습니다.
  • 유효성 재검사 및 최적의 캐싱 정책 지정: 글꼴은 자주 업데이트되지 않는 정적 리소스입니다. 여러 페이지 간에 효율적인 글꼴 재활용이 가능하도록, 서버가 장기간 활성 상태로 유지되는 max-age 타임스탬프와 유효성 재검사 토큰을 제공해야 합니다. 서비스 워커를 사용하는 경우 캐시 우선 전략이 적합합니다.

Lighthouse를 사용한 WebFont 로드 동작 자동 테스트

Lighthouse를 사용하면 웹 글꼴 최적화 권장사항을 준수하는지 확인하는 프로세스를 자동화할 수 있습니다.

다음 감사를 통해 페이지가 시간이 지남에 따라 웹 글꼴 최적화 권장사항을 계속 준수하는지 확인할 수 있습니다.