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
자체 호스팅 폰트를 사용하면:
- 외부 네트워크 요청 제거
- GDPR/CCPA 완전 준수
- 로딩 성능 향상 (CDN 지연 시간 제거)
- 완전한 제어권 확보
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
- woff2 포맷 우선: 브라우저 호환성을 위해 woff와 woff2 모두 제공
- subset 분리: 한국어 등 비-Latin 문자는 별도 subset으로 분리
- preload 최적화: critical fonts만 preload하여 초기 로딩 시간 단축
- CSS 변수 활용:
font-family대신 CSS 변수 사용으로 일관성 유지
This blog does not accept any external sponsorships, affiliate marketing, or ad revenue.