performance2025-06-14·11 min·24/348

next/font self-host 폰트 완전 가이드

Google Fonts 없이 완전히 자체 호스팅되는 폰트를 next/font로 최적화하는 방법을 알아봅니다.

next/font self-host 폰트 완전 가이드

Introduction

Next.js 13+에서 도입된 next/font는 폰트를 최적화하여 레이아웃 시프트를 방지합니다. Google Fonts에 의존하지 않고 완전히 자체 호스팅되는 폰트를 설정하면 GDPR 준수와 로딩 성능을 동시에 달성할 수 있습니다. 이 글에서는 자체 폰트 파일을 next/font와 통합하는 전체 과정을 다룹니다.

Environment

# 프로젝트 구조
my-app/
├── public/
│   └── fonts/
│       ├── Inter-Regular.woff2
│       ├── Inter-Medium.woff2
│       └── Inter-Bold.woff2
├── app/
│   ├── layout.tsx
│   └── globals.css
├── next.config.js
└── package.json

# 기술 스택
Next.js: 14.2.5
Inter 폰트: v4.0
# 폰트 파일 준비
mkdir -p public/fonts
# 폰트 파일을 public/fonts에 복사

Problem

기본적으로 next/font/google을 사용하면 외부 CDN에서 폰트를 로드합니다:

// ❌ 외부 의존성
import { Inter } from 'next/font/google';

const inter = Inter({
  subsets: ['latin'],
  display: 'swap',
});

Analysis

자체 호스팅 폰트를 사용하면:

  1. 외부 네트워크 요청 제거
  2. GDPR/CCPA 완전 준수
  3. 로딩 성능 향상 (CDN 지연 시간 제거)
  4. 완전한 제어권 확보

Solution

1. Local Fonts 설정

// app/layout.tsx
import { LocalFont } from 'next/font/local';

const inter = LocalFont({
  src: [
    {
      path: '../public/fonts/Inter-Regular.woff2',
      weight: '400',
      style: 'normal',
    },
    {
      path: '../public/fonts/Inter-Medium.woff2',
      weight: '500',
      style: 'normal',
    },
    {
      path: '../public/fonts/Inter-Bold.woff2',
      weight: '700',
      style: 'normal',
    },
  ],
  display: 'swap',
  variable: '--font-inter',
  fallback: ['Arial', 'sans-serif'],
});

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    
      {children}
    
  );
}

2. CSS 변수 활용

/* app/globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;

:root {
  --font-inter: 'Inter', sans-serif;
}

body {
  font-family: var(--font-inter);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* 폰트 로딩 상태 관리 */
.fonts-loading body {
  opacity: 0.5;
}

.fonts-loaded body {
  opacity: 1;
  transition: opacity 0.3s ease;
}

3. 다중 웨이트 및 스타일 지원

// lib/fonts.ts
import { LocalFont } from 'next/font/local';

// 별도의 폰트 설정 내보내기
export const fonts = {
  inter: LocalFont({
    src: [
      {
        path: '../public/fonts/Inter-Thin.woff2',
        weight: '100',
        style: 'normal',
      },
      {
        path: '../public/fonts/Inter-Light.woff2',
        weight: '300',
        style: 'normal',
      },
      {
        path: '../public/fonts/Inter-Regular.woff2',
        weight: '400',
        style: 'normal',
      },
      {
        path: '../public/fonts/Inter-Medium.woff2',
        weight: '500',
        style: 'normal',
      },
      {
        path: '../public/fonts/Inter-SemiBold.woff2',
        weight: '600',
        style: 'normal',
      },
      {
        path: '../public/fonts/Inter-Bold.woff2',
        weight: '700',
        style: 'normal',
      },
      {
        path: '../public/fonts/Inter-ExtraBold.woff2',
        weight: '800',
        style: 'normal',
      },
      {
        path: '../public/fonts/Inter-Black.woff2',
        weight: '900',
        style: 'normal',
      },
      // Italic 변형
      {
        path: '../public/fonts/Inter-Italic.woff2',
        weight: '400',
        style: 'italic',
      },
      {
        path: '../public/fonts/Inter-BoldItalic.woff2',
        weight: '700',
        style: 'italic',
      },
    ],
    display: 'swap',
    variable: '--font-inter',
  }),
};
// app/layout.tsx
import { fonts } from '@/lib/fonts';

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    
      {children}
    
  );
}

4. 폰트 파일 변환 스크립트

// scripts/convert-fonts.js
const fs = require('fs');
const path = require('path');

// woff2 변환 (Node.js 16+)
async function convertToWoff2(inputPath, outputPath) {
  // 실제 환경에서는 wawoff2 라이브러리 사용
  console.log(`Converting ${inputPath} to ${outputPath}`);
}

// 디렉토리 내 모든 TTF 파일을 woff2로 변환
async function convertAllFonts() {
  const fontsDir = path.join(__dirname, '../public/fonts');
  const files = fs.readdirSync(fontsDir);
  
  for (const file of files) {
    if (file.endsWith('.ttf')) {
      const inputPath = path.join(fontsDir, file);
      const outputPath = path.join(fontsDir, file.replace('.ttf', '.woff2'));
      await convertToWoff2(inputPath, outputPath);
    }
  }
}

convertAllFonts();

5. 프리로드 설정

// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  // 폰트 파일 캐싱 설정
  async headers() {
    return [
      {
        source: '/fonts/:path*',
        headers: [
          {
            key: 'Cache-Control',
            value: 'public, max-age=31536000, immutable',
          },
        ],
      },
    ];
  },
};

module.exports = nextConfig;

Lessons Learned

  1. woff2 포맷 우선: 브라우저 호환성을 위해 woff와 woff2 모두 제공
  2. subset 분리: 한국어 등 비-Latin 문자는 별도 subset으로 분리
  3. preload 최적화: critical fonts만 preload하여 초기 로딩 시간 단축
  4. CSS 변수 활용: font-family 대신 CSS 변수 사용으로 일관성 유지

This blog does not accept any external sponsorships, affiliate marketing, or ad revenue.