카테고리 없음

[이모티어리] 폰트 최적화

Ahyeon, Jung 2024. 4. 8. 12:20

가끔씩 폰트가 뒤늦게 적용되는 적이 꽤나 많았는데, 다른거 먼저 하느라 내 인터넷이 느린거라고 못본척하면서 지나갔다. 그러면서 맨날 신경쓰였는데 리팩토링하다보니 이유가 궁금해져서 찾아보았다. 개발입문 3달차쯤에 찾아봤을 때는 이해를 못했었는데, 이제 그래도 기술 블로그들을 읽을 수는 있게 됐나보다. 


Font Flash

페이지가 로드되는 시점에 폰트 다운로드가 완료되지 않아 일단 기본 폰트로 보이고, 다운로드가 완료된 후에 적용되면서 깜박거리는 현상이 나타난다. 이 현상을 폰트 플래시 또는 폰트 퍼실레이션이라고 칭한다. 이는 레이아웃의 변경으로 인해 사용자에게 불편함을 주는 Layout Shift이다.

글꼴 로딩 전략

FOUT(Flash of Unstyled Text)

폰트가 다운로드되지 않았다면 로컬 폰트나 대체 글꼴로 렌더링 된 후 다운로드되면 적용하는 방식

ex. Edge

FOIT(Flash of Invisible Text)

브라우저가 폰트를 완전히 다운로드하기 전에 텍스트가 보이지 않는 방식

ex. 크롬, 사파리, 파이어폭스

* 크롬의 경우, 3초만 기다리는 FOIT

font-display로 방식 변경하기

서비스 또는 콘텐츠의 특성에 따라 FOUT, FOIT 방식을 선택 가능하다.

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

 

  • auto: 브라우저 기본 동작(대부분의 브라우저는 FOIT)
  • block: 특정 기간(보통 3초) 내에 로드되지 않으면 대체 폰트 사용하는 FOIT
  • swap: FOUT
  • fallback: 3초 후에도 불러오지 못한 경우 페이지의 수명동안 대체 글꼴이 사용되는 FOIT
  • optional: 네트워크 상태에 따라 로드가 짧은 시간동안 로드되지 않으면 기본 폰트로 유지되는 FOIT

 

빠르게 텍스트를 보여줘야하는 경우나 중요한 내용의 텍스트라면 FOUT을 권장

* 갑자기 나타나서 어색한 경우를 해결하기 위해 fade-in 애니메이션을 적용하는 fontfaceobserver

Font preload

FOIT, FOUT 기간을 최소화하기 위해 가능한 빨르 웹 글꼴 파일을 로드하기 위해 <link rel="preload">를 html <head>의 css 앞에 추가한다. 브라우저는 해당 폰트가 페이지 렌더링에 필요한 리소스임을 인식하고 우선적으로 다운로드 한다. 즉, CSS 파일이나 스타일 시트가 로드되기 전에 웹 폰트 파일을 다운로드하기 시작하여 FOIT와 FOUT를 최소화하는 방식이다.

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

폰트 파일 크기 최적화

폰트 포맷 변경하기

  • EOT(Embedded Open Type) - Microsoft에서 IE용으로 설계된 글꼴 형식. 가장 큰 용량
  • TTF/OTF(TrueType/OpenType) - TTC-는 Apple과 Microsoft에서 개발되었고, OTF는 Microsoft와 Adobe에서 개발. 글리프의 벡터 기반 윤곽선을 포함하며 리거처, 케르닝 페어 등의 추가 기능 포함 가능.
  • WOFF(Web Open Font Format) - 웹에서 사용하기 위해 최적화된 글꼴 형식. 메타데이터를 포함하고 있는 압축된 글꼴 형식
  • WOFF2 - WOFF의 개선된 버전으로 더 나은 압축과 더 작은 파일 크기로 더 빠른 로딩 시간과 개선된 성능을 제공. 가장 작은 용량

Online @font-face generator — Transfonter

 

Online @font-face generator

The @font-face CSS rule allows web developers to specify online fonts to display text on their web pages. By allowing authors to provide their own fonts, @font-face eliminates the need to depend on the limited number of fonts users have installed on their

transfonter.org

@font-face {
  font-family: BMYEONSUNG;
  src: url('./assets/fonts/BMYEONSUNG.woff2') format('woff2'),
    url('./assets/fonts/BMYEONSUNG.woff') format('woff'),
    url('./assets/fonts/BMYEONSUNG.ttf') format('truetype'),
  font-display: block;
}

서브셋 폰트 사용

모든 문자가 아닌 일부 문자의 폰트 정보만 가지고 있는 서브셋 폰트를 사용.

Transfonter의 Characters를 이용하여 특정 문자를 넣어 서브셋 폰트 파일을 생성 후 적용

 

폰트를 파일 형태가 아닌 Data-URI 형태로 CSS 파일에 포함.

Transfonter의 Base64 encode를 활성화시켜 폰트를 문자열 데이터로 변환 후 적용

@font-face {
  font-family: 'BM YEONSUNG';
  src: url('data:font/woff2;charset=utf-8;base64,d09...'),
    url('./assets/fonts/BMYEONSUNG.woff') format('woff'),
    url('./assets/fonts/BMYEONSUNG.ttf') format('truetype');
  font-weight: normal;
  font-style: normal;
  font-display: block;
}

구글 폰트에서 기본 폰트로 font-display=swap으로 FOUT 형식을 제공해서 다운로드 전에 기본 폰트가 적용되었던 것이다.. 애매하게 계속 거슬리던 거치고는 너무 쉬운 해결이었다.

<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=Noto+Sans+KR:wght@300;400;500;700;800&family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap" rel="stylesheet">
<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=Noto+Sans+KR:wght@300;400;500;700;800&family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=block" rel="stylesheet">

 

프리지링크에서도 초반에 같은 문제가 발생했었다. 하지만 협업하시는 분이 어느날 뚝딱 해결하시고 끝났어서 지나갔었는데, 다시 살펴보니 비슷한 맥락이었다. Tailwind CSS에서 @layer 지시문은 CSS가 생성되는 순서를 제어하는 데 사용된다. 즉, @font-face 규칙을 @layer base 블록 내에 배치하면 기본 레이어에 포함되어 CSS 기본 스타일의일부로 간주되는 것이다. 

@layer base {
  @font-face {
    font-family: 'Pretendard-Regular';
    src: url('https://cdn.jsdelivr.net/gh/Project-Noonnu/noonfonts_2107@1.1/Pretendard-Regular.woff') format('woff');
  }
  html,
  body {
    padding: 0;
    margin: 0;
    font-family: Pretendard-Regular;
    color: #363a45;
  }
}

 


Reference

프론트엔드 성능 최적화 가이드, 유동균(2022.11)

웹폰트 빠르게 로드하기, FOIT 와 FOUT, font-display 속성 차이 (devs.vercel.app)