카테고리 없음

웹 폰트 최적화 (1) : 꼭 필요한 이유와 font-family 설정 팁

합주기 2025. 4. 9. 21:04

웹 폰트 최적화

웹 폰트는 네트워크를 통해 다운로드되기 때문에,
다운로드 사이즈를 줄이거나, 폰트 적용 시점을 조절해 최적화할 수 있다.

 

예제 상황

 

네트워크 설정을 Fast 4G로 설정하고 테스트했다.

  • 폰트 사이즈: 1.9MB
  • 로드 시간: 11.76초
  • 문제: 페이지가 로드된 후 약 12초 뒤에야 폰트가 적용됨

웹 폰트 최적화해야 하는 이유

네트워크 상황에 따라 느리게 폰트가 적용될 가능성이 있으며, 다음과 같은 문제가 생길 수 있다.

  1. 폰트가 깜빡인다. (기본 폰트 → 커스텀 폰트로 변경되는 과정에서의 깜빡임)
  2. 다른 요소를 밀어낼 수 있다.

폰트 렌더링 방식 (FOUT vs FOIT)

폰트는 "다운로드가 완료된 시점"에 적용된다.

그럼 다운로드 전까지 텍스트는 어떻게 보일까?

FOUT 기본 폰트를 먼저 보여주고, 커스텀 폰트 로드 후 교체됨
FOIT 커스텀 폰트가 로드될 때까지 텍스트가 숨겨짐

사실, FOIT 방식이라고 해서 다운로드될 때까지 안보이는 것은 아니다. 

timeout(n초) 기다린 후 다운로드 되지 않는다면 일단 기본 폰트를 보여주고, 완료되면 폰트를 적용한다.

아래에서 자세히 설명하니 넘어가도 된다.

브라우저 기본 동작

  • FOUT: IE, Edge
  • FOIT: Chrome, Safari, Firefox

폰트 최적화 방법

  1. 폰트 적용 시점 제어 (FOUT or FOIT)
  2. 폰트 용량 최적화

1. 폰트 적용 시점 제어 – font-display

@font-face에서 font-display 속성을 사용하여 폰트 표시 방식을 제어할 수 있다.

속성 값 5가지 요약


auto 브라우저 기본값  
block FOIT (3초) 3초간 텍스트 숨김 → 이후 기본 폰트 적용, 다운로드 완료되면 교체
swap FOUT 기본 폰트 → 커스텀 폰트
fallback FOIT (0.1초) 0.1초 후 기본 폰트 적용, 이후 캐시로 빠르게 교체
optional FOIT (0.1초 + 네트워크 상황 반영) fallback과 유사하지만, 네트워크가 느릴 경우 커스텀 폰트 적용 안함 (구글 권장 방식)

참고) 3초는 브라우저에 따라 다른 값을 가질 수 있음

정리

  • swap: FOUT 방식 – 깜빡이지만 빠르게 텍스트 표시
  • block, fallback, optional: FOIT 방식
    • block: 3초 대기
    • fallback, optional: 0.1초 대기 후 기본 폰트 표시
  • optional: 네트워크 상태 고려 → 구글 권장

실제 적용

참고로 나는 배너의 텍스트라 덜 중요하다고 판단하여, 느슨한 timeout을 가진 FOIT 방식인 block을 선택했다.

 

폰트에 애니메이션 적용

block 설정한 경우, 갑자기 '뿅' 하고 나오기 때문에 부자연스럽다고 느낄 수 있다.

따라서 폰트에 서서히 나타나는 애니메이션을 적용하여 부드럽게 나올 수 있도록 설정했다.

  • opacity, transition 활용

우선 폰트가 다운로드 완료된 시점을 알아야 한다.
왜냐하면 폰트가 다운로드되면 커스텀 폰트로 적용이 될 것이기 때문에, 그 시점에 opacity를 0 → 1로 변경하면서 애니메이션을 적용할 예정이기 때문이다.

 

다행히도, 폰트의 로드된 시점을 알려주는 라이브러리 fontfaceobserver가 있다.

 

설치

npm i fontfaceobserver
 

폰트 로드되는 시점 감지

// BannerVideo.js
import FontFaceObserver from "fontfaceobserver";
import React, { useEffect, useState } from "react";

function BannerVideo() {
  const [fontLoaded, setFontLoaded] = useState(false);

  useEffect(() => {
    const font = new FontFaceObserver("BMYEONSUNG");
    
    font.load(null, 10000) // 10초 타임아웃
      .then(() => {
        setFontLoaded(true);
      })
  }, []);

  return (
    <div className="BannerVideo">
      {fontLoaded && (
        <div style={{ opacity: fontLoaded ? 1 : 0, transition: "opacity 0.3s ease-in-out" }}>
          TEXT TEXT TEXT TEXT
        </div>
      )}
    </div>
  );
}

 

폰트에 애니메이션 적용

  • 폰트가 로드되자마자 opacity를 1로 변경
  • opacity: 0 → 1로, transition 애니메이션 적용하여 서서히 나타나도록 설정