웹 성능을 위한 웹폰트 최적화 기법들

이민영·2025년 1월 21일

웹 성능 최적화

목록 보기
2/2
post-thumbnail

🤔 웹폰트가 웹 성능에 어떤 영향을 끼칠까??

웹폰트는 웹사이트의 디자인과 브랜드 정체성을 강화하는 데 중요한 역할을 하지만, 동시에 웹 성능에 영향을 미칠 수 있다. 웹폰트를 사용하는 경우, 사용자의 브라우저는 폰트를 서버에서 다운로드해야 하며, 이 과정이 페이지 로딩 속도에 영향을 미칠 수 있다.

특히, 웹폰트 파일의 크기가 크거나 여러 종류의 웹폰트를 로드해야 하는 경우, 폰트 다운로드 시간이 길어져 텍스트 콘텐츠가 표시되기까지 지연되는 FOIT(Fallback On Invisible Text) 또는 FOUT(Flash of Unstyled Text) 현상이 발생할 수 있다. 이는 사용자 경험을 저하시킬 뿐 아니라 웹사이트의 첫인상을 악화시킬 위험이 있다.

게다가, 특정 폰트 형식(WOFF2, WOFF, TTF 등)이 브라우저에 따라 지원되지 않을 수 있기 때문에, 여러 형식을 준비해야 하며 이는 추가적인 리소스 관리 부담으로 이어질 수 있다. 웹폰트 최적화는 이러한 문제를 완화하는 데 핵심적인 역할을 한다.

🔎 FOIT랑 FOUT는 어떤 현상일까?

FOIT(Fallback On Invisible Text)

웹폰트가 다운로드되는 동안 텍스트 콘텐츠를 화면에 표시하지 않고 대기 상태로 유지하다가, 웹폰트 다운로드가 완료되면 해당 폰트가 적용된 텍스트 콘텐츠를 보여주는 현상이다. 이로 인해 사용자는 한동안 빈 화면을 보게 되어 사용자 경험이 저하될 수 있다.


FOUT(Flash of Unstyled Text)

웹폰트가 다운로드되기 전까지 브라우저의 기본 폰트를 사용해 텍스트 콘텐츠를 먼저 표시하고, 웹폰트가 다운로드되면 이후 웹폰트가 적용된 텍스트 콘텐츠로 대체되는 현상이다. 이로 인해 텍스트의 스타일이 갑작스럽게 변경되는 느낌을 줄 수 있어 일관된 사용자 경험을 방해할 수 있다.

웹폰트 최적화 기법들

1. 폰트 적용 시점 컨트롤하기

웹폰트의 다운로드 완료 시점에 따라 텍스트의 표시 방식을 제어하려면 font-display CSS 속성을 사용한다. 이를 통해 폰트가 로드되는 동안 발생할 수 있는 FOUT (Flash of Unstyled Text) 또는 FOIT (Flash of Invisible Text) 현상을 관리할 수 있다.

주요 옵션

  • auto: 브라우저 기본 동작을 따른다.

  • swap: 폰트가 로드될 때까지 텍스트는 기본 폰트로 렌더링된다. 이후 웹폰트가 로드되면 즉시 해당 폰트로 교체된다. 이 방식은 FOUT현상을 유발할 수 있지만, 폰트가 로드되지 않은 상태에서 사용자에게 빈 화면을 보여주는 것보다는 더 나은 사용자 경험을 제공한다.

  • block: 웹폰트가 완전히 로드될 때까지 텍스트가 보이지 않게 숨겨준다. 이 방식은 FOIT 현상을 일으켜 폰트가 로드되기 전까지 사용자에게 비어있는 화면을 보여주게 된다.

  • fallback: 웹폰트를 3초 이내에 로드하지 못한 경우, 기본 폰트로 텍스트를 렌더링한다. 이후 웹폰트가 로드되면 캐시에서 로드되어 교체된다.

  • optional: 네트워크 상태에 따라, 웹폰트를 로드할지 기본 폰트를 유지할지 결정한다. 네트워크 속도가 느리거나 웹폰트가 로드되지 않으면 기본 폰트를 사용하며, 이후 웹폰트를 로드하지 않을 수도 있다.

적용 예시

@font-face {
  font-family: 'CustomFont';
  src: url('custom-font.woff2') format('woff2');
  font-display: swap;
}

위와 같이 font-display: swap;을 설정하면, 웹폰트가 로드되기 전에 기본 폰트로 텍스트를 표시하고, 폰트가 로드되면 해당 폰트로 즉시 교체된다. 이를 통해 페이지 로드 시간이 길어지는 동안에도 텍스트가 빈 화면으로 표시되지 않도록 할 수 있다.

2. 폰트 용량 줄이기

폰트 용량을 줄이면 서버에서 웹폰트를 다운로드하는 시간을 단축시킬 수 있어 페이지 로딩 속도를 개선할 수 있다. 아래 방법들을 활용해 폰트 최적화를 진행할 수 있다.

0. 웹폰트 변환 사이트

https://transfonter.org/

Transfonter는 웹 개발자나 디자이너가 웹폰트를 쉽고 빠르게 변환하고 최적화할 수 있도록 도와주는 웹사이트 이다. 해당 사이트는 폰트 포맷 변환, 웹폰트 키트 생성, 서브셋 생성, 압축 및 최적화와 같은 작업을 간편하게 할 수 있다. 또한 사용이 간단하고 무료로 제공되니 해당 사이트를 참고해보자.

1. 웹폰트 포맷(format) 설정하기

웹폰트 포맷은 브라우저 호환성과 파일 크기에 따라 선택해야 한다. 주요 포맷은 다음과 같다.

  • EOT: 매우 오래된 포맷으로 IE 6~8에서만 사용하며, 최신 브라우저에서는 지원되지 않는다. 압축 기능이 거의 없어 파일 크기가 크다.

  • TTF/OTF: 파일 크기가 EOT보다는 작지만, 웹 최적화를 고려한 포맷은 아니다. 대체로 모든 브라우저에서 지원된다.

  • WOFF: W3C에서 웹 환경에 적합한 폰트를 제공하기 위해 만든 포맷이다. 대부분의 브라우저에서 지원되며, 현재도 많이 사용되지만 WOFF2에 비해 효율이 낮아 점차 대체되는 추세이다.

  • WOFF2: 기존 WOFF 대비 30% 이상 작은 파일 크기를 제공한다. 가장 효율적인 웹폰트 포맷이지만 최신브라우저에서만 지원하기 때문에 구형 브라우저와 호환되지 않는다.


웹폰트 포맷에 따른 웹폰트 압축은 위에 소개해준 Transfer 사이트에서 할 수 있다.

사이트로 이동하여 변환을 원하는 폰트를 등록하고, Formats에서 원하는 형식의 format을 선택한 후 다운로드 받으면 된다.

WOFF2, WOFF가 압출륙이 좋지만 브라우저마다 지원하는 폰트가 다르기 때문에 @font-face 규칙을 적용하여 여러 포맷을 지정해 브라우저가 지원하는 파일을 우선 다운로드하도록 설정해야 한다.

적용 예시

@font-face {
  font-family: 'CustomFont';
  src: url('customfont.woff2') format('woff2'), /* 최신 브라우저용 */
       url('customfont.woff') format('woff'), /* 구형 브라우저용 */
       url('customfont.ttf') format('truetype');
  font-display: swap;
}

2. local 폰트 사용하기

만약 사용자의 브라우저에 서버에서 다운로드할 웹폰트가 존재한다면, 추가로 웹폰트를 다운로드하지 않아도 되므로 로딩 시간을 단축할 수 있다.

적용 예시

@font-face {
  font-family: 'LocalFont';
  src: local('LocalFont'), /* 사용자의 시스템에서 폰트를 검색 */
  	   url('localfont.woff2') format('woff2'), /* 로컬에 폰트가 설치되어있지 않다면 다음 폰트들을 적용한다 */
       url('localfont.woff') format('woff'), 
       url('localfont.ttf') format('truetype');
}

3. subset 폰트 사용하기

한글은 자음과 모음의 조합으로 이루어져 있어, 모든 조합을 포함하면 만 개가 넘는 글자가 생성된다. 하지만 실제 서비스에서는 사용하지 않는 조합(예: 긗, 귶)도 많다.

웹폰트에 모든 글리프(문자)를 포함하면 파일 크기가 불필요하게 커지는데, 실제 사용하는 문자만 남기고 나머지를 제거한 서브셋 폰트를 생성하면 용량을 크게 줄일 수 있다. 이를 통해 폰트 파일 크기를 최적화하고 페이지 로딩 속도를 개선할 수 있다.

다만, 서브셋 폰트를 사용하는 경우 주의할 점이 있다. 텍스트가 동적으로 생성되는 페이지에서는 서브셋 폰트에 포함되지 않은 글자가 나타날 가능성이 있다. 이런 상황에서는 일관성 있는 텍스트 디자인을 제공하지 못할 수 있다. 따라서 서브셋 폰트는 주로 사용되는 문자가 고정된 랜딩 페이지나 정적인 콘텐츠에 적합하다.


서브셋 폰트 또한 Transfonter 사이트에서 생성할 수 있다.

사이트로 이동하여 변환을 원하는 폰트를 등록하고, characters에 원하는 글자 목록을 추가하면 된다.

4. unicode-range 속성 사용하기

한 페이지에서 특정 문자 집합만 사용하는 경우, CSS의 unicode-range 속성을 활용하면 브라우저가 필요한 글리프(문자)만 로드하도록 설정할 수 있다. 이를 통해 웹폰트 파일 크기를 줄이고 로딩 속도를 최적화할 수 있다.

기본 예시
아래 예제는 기본 라틴 문자만 로드하도록 설정한 경우다.

@font-face {
  font-family: 'MyFont';
  src: url('myfont.woff2') format('woff2');
  unicode-range: U+0000-007F; /* 기본 라틴 문자만 사용 */
}

다국어 웹폰트 설정
다국어 서비스를 제공하는 경우, unicode-range를 사용해 나라별로 필요한 폰트를 구분하여 로드할 수 있다. 이렇게 하면 모든 폰트를 한 번에 다운로드하지 않아도 된다.

/* 한글 전용 폰트 */
@font-face {
  font-family: 'koreaFont';
  src: url('koreafont.woff2') format('woff2');
  unicode-range: U+1100-U+11FF; /* 한글 문자만 */
}

/* 라틴어 전용 폰트 */
@font-face {
  font-family: 'latinFont';
   src: url('latinfont.woff2') format('woff2');
  unicode-range: U+0000-005FF; /* 라틴 문자만 */
}

5. data-uri 변환하기

data-uri 방식은 웹폰트 리소스를 CSS 파일에 직접 포함하여 추가 네트워크 요청을 줄이고 초기 로딩 속도를 개선할 수 있다는 장점이 있다. 또한, CSS와 리소스를 하나의 파일로 묶어 관리가 간편해진다는 이점도 있다.

그러나 Base64 인코딩으로 인해 파일 크기가 원본보다 약 33% 커지고, 포함된 리소스는 브라우저에서 캐싱되지 않아 반복 요청 시 비효율적일 수 있다. 더불어, 긴 Base64 문자열은 읽기 어렵고 수정이나 디버깅이 불편하다는 단점도 있다.

data-uri는 작은 리소스를 포함해 초기 로딩 속도를 개선하는 데 효과적이지만, 파일 크기와 캐싱 문제를 고려해 신중히 활용해야 한다.


data-uri 또한 Transfonter 사이트에서 변환할 수 있다.

사이트로 이동하여 변환을 원하는 폰트를 등록하고, Base64 encode를 활성화 하면 된다.

적용 예시

@font-face {
  font-family: 'MyFont';
  src: url(data:font/woff2;base64,...); /* Base64 인코딩된 폰트 데이터 */
}

3. 웹폰트 Preload

웹폰트 Preload는 브라우저가 폰트 파일을 CSS가 로드되기 전에 미리 다운로드하도록 지시하는 기법이다. 이를 통해 FOUT(Flash of Unstyled Text) 또는 FOIT(Flash of Invisible Text)를 방지하고, 페이지 로딩 속도를 향상시킬 수 있다.

Preload는 HTML의 <link> 태그를 사용하여 명시하며, 브라우저는 이를 최우선 순위로 로드한다. 이를 통해 웹폰트가 CSS보다 먼저 로드되도록 보장한다.

다만, Preload는 잘못 사용하면 리소스 로딩 순서가 뒤섞여 성능이 저하를 유발할 수 있다. 따라서 초기 화면에 필요한 폰트만 Preload하는 것이 중요하다. Preload로 폰트를 로드한 후에는, CSS에서 @font-face 규칙을 사용해 폰트를 정의해야 정상적으로 사용할 수 있다.

적용 예시

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Webfont Preload Example</title>

  <!-- 웹폰트 Preload -->
  <!--  rel="preload": 리소스를 미리 로드하도록 지시.-->
  <!-- href: 폰트 파일 경로.-->
  <!-- as="font": 리소스 타입을 지정(폰트).-->
  <!-- type="font/woff2": 폰트 파일의 포맷 지정.-->
  <!-- crossorigin="anonymous": 크로스 오리진 요청 시 사용(외부 폰트 URL이 있는 경우 필요). -->
  <link rel="preload" href="/fonts/myfont.woff2" as="font" type="font/woff2" crossorigin="anonymous">

  <!-- CSS 파일 -->
  <link rel="stylesheet" href="styles.css">
</head>
<body>
  <h1 style="font-family: 'MyFont';">웹폰트 Preload 테스트</h1>
</body>
</html>

4. 웹팩을 이용한 Preload 리소스 관리하기

웹팩(Webpack)이나 비트(Vite) 같은 번들링 도구로 웹 애플리케이션을 빌드할 때, 폰트 파일, 이미지, 스크립트 같은 정적 리소스에 해시값(hash)이 추가된다. 이렇게 하면 캐시를 무효화할 수 있어서 항상 최신 리소스를 로드할 수 있다.

하지만 이 해시값 때문에 HTML의 <link rel="preload"> 태그 경로를 일일이 업데이트해야 하는 번거로움이 생긴다. 이걸 해결하려면 웹팩 플러그인을 써서 Preload 태그를 자동으로 생성하고 관리할 수 있다.

1. PreloadWebpackPlugin 설치 (Preload 태그 자동 생성)
먼저 Preload 태그 자동 생성으로 생성해주는 preload-webpack-plugin을 설치한다.

npm install preload-webpack-plugin --save-dev

2. 웹팩 설정 파일 작성
웹팩 설정 파일(webpack.config.js)에 PreloadWebpackPlugin을 추가해서 자동화한다.

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const PreloadWebpackPlugin = require('preload-webpack-plugin');

module.exports = {
  entry: './src/index.js', // 애플리케이션 진입점
  output: {
    path: path.resolve(__dirname, 'build'), // 빌드 출력 경로
    filename: '[name].[contenthash].js', // JS 파일 이름에 해시 추가
    assetModuleFilename: 'fonts/[name].[contenthash][ext]', // 폰트 파일 이름에 해시 추가
  },
  module: {
    rules: [
      {
        test: /\.css$/, // CSS 처리
        use: ['style-loader', 'css-loader'],
      },
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/, // 폰트 파일 처리
        type: 'asset/resource',// 웹팩 5의 기본 정적 파일 처리 방식
      },
    ],
  },
  //플러그인
  plugins: [
    new HtmlWebpackPlugin({
      template: './public/index.html', // HTML 템플릿 경로
    }),
    new PreloadWebpackPlugin({
      rel: 'preload', // Preload 태그 생성
      as: (entry) => {
        if (/\.woff2?$/.test(entry)) return 'font'; // 폰트 파일은 font로 설정
        if (/\.css$/.test(entry)) return 'style'; // CSS 파일은 style로 설정
        if (/\.js$/.test(entry)) return 'script'; // JS 파일은 script로 설정
        return 'auto';
      },
      include: 'allAssets', // 모든 에셋 포함
      fileWhitelist: [/\.(woff|woff2)$/i], // woff, woff2 파일만 포함
      crossorigin: 'anonymous', // CORS 지원을 위해 추가
    }),
  ],
  devServer: {
    static: './build', // 개발 서버 정적 파일 경로
    port: 3000, // 개발 서버 포트
    open: true, // 브라우저 자동 열기
  },
};

3. 설정 주요 포인트

  • Preload 태그의 파일 타입 관리
    as 옵션을 사용해서 파일 형식에 맞는 as 속성을 설정한다. 예를 들어, 폰트 파일은 as="font", 스크립트는 as="script"로 설정한다.

  • 파일 필터링
    fileWhitelist 옵션을 사용해서 특정 형식의 파일만 Preload 태그에 포함시킬 수 있다. 위 예시에서는 woff와 woff2 파일만 포함시킨다.

  • CORS 지원
    웹폰트는 CORS 정책에 의해 리소스가 다운로드되지 않을 수 있으므로, Preload 태그를 생성할 때 crossorigin="anonymous" 속성을 추가하는 것이 중요하다. 이 속성은 웹폰트를 안전하게 로드할 수 있도록 도와준다.

4. 빌드 및 실행
설정이 완료되었으니 빌드하거나 개발 서버를 실행한다.(스크립트 설정에 따라 명령어는 달라질 수 있다.)

  • 빌드
npm run build
  • 개발 서버 실행
npm start

5. 결과 확인
빌드된 HTML 파일에서 Preload 태그가 자동으로 생성된 걸 확인할 수 있다.

<link rel="preload" href="assets/myfont.abcdef.woff2" as="font" type="font/woff2" crossorigin="anonymous">

출처



혹시라도 잘못된 내용이 있거나 질문사항 있으시면 댓글 남겨주세요! 어떤 댓글이든 달게 받겠습니다!
profile
Frontend Developer

0개의 댓글