폰트 최적화
웹 페이지의 시각적 요소에서 폰트(Font) 는 이미지 다음으로 큰 용량을 차지하며, 페이지 로딩 성능에 상당한 영향을 미칠 수 있습니다. 폰트 파일이 크거나, 로딩 방식이 비효율적이면 텍스트가 표시되기 전에 지연이 발생하거나, 텍스트가 깜빡이는 현상(FOIT - Flash Of Invisible Text, 또는 FOUT - Flash Of Unstyled Text)이 나타나 사용자 경험을 해칠 수 있습니다. Next.js는 폰트 최적화를 위한 내장 기능을 제공하여 이러한 문제들을 효과적으로 해결할 수 있도록 돕습니다.
이 절에서는 폰트 최적화의 중요성, Next.js의 next/font
컴포넌트 사용법, 그리고 폰트 로딩 성능을 개선하기 위한 다양한 전략들을 알아보겠습니다.
폰트 최적화의 중요성
- 성능 저하: 웹 폰트 파일은 용량이 크기 때문에 다운로드 시간이 길어지면 페이지 렌더링을 지연시킬 수 있습니다. 이는 특히 모바일 환경이나 네트워크 속도가 느린 환경에서 더욱 두드러집니다.
- CLS(Cumulative Layout Shift) 발생: 폰트 파일 로딩이 완료되기 전에 대체 폰트(fallback font)로 텍스트가 렌더링되다가 폰트가 로드된 후 레이아웃이 변경될 때 CLS가 발생합니다. 이는 사용자의 시각적 안정성을 해치고 Core Web Vitals 점수에 부정적인 영향을 미칩니다.
- FOIT/FOUT 현상
- FOIT (Flash Of Invisible Text): 폰트가 로딩될 때까지 텍스트가 전혀 보이지 않는 현상입니다. 사용자에게 빈 화면을 보여주어 답답함을 유발합니다.
- FOUT (Flash Of Unstyled Text): 폰트가 로딩될 때까지 시스템 기본 폰트나 대체 폰트로 텍스트가 표시되다가, 로딩이 완료되면 웹 폰트로 교체되면서 글꼴이 갑자기 바뀌는 현상입니다.
폰트 최적화는 이러한 문제들을 해결하여 웹 페이지의 시각적 안정성과 로딩 성능을 향상시키는 데 기여합니다.
next/font
컴포넌트 사용하기
Next.js 13부터 도입된 next/font
는 Google Fonts 및 로컬 폰트를 자동으로 최적화해 주는 강력한 기능입니다. 이 모듈은 폰트 파일 다운로드를 최적화하고, font-display
속성을 자동으로 설정하여 FOIT/FOUT 문제를 최소화하며, 폰트 메트릭스를 자동으로 조정하여 CLS를 방지합니다.
next/font
는 서버에서 폰트를 처리하고 빌드 시점에 폰트 파일을 정적으로 처리하여 가장 빠른 폰트 로딩을 보장합니다.
Google Fonts 최적화
Google Fonts는 웹에서 가장 널리 사용되는 폰트 서비스 중 하나입니다. next/font/google
을 사용하면 Google Fonts를 빠르고 효율적으로 불러올 수 있습니다.
실습: Google Fonts 적용하기
폰트 불러오기:
src/app/layout.tsx
(루트 레이아웃) 또는 개별 페이지/컴포넌트 파일에서 next/font/google
을 임포트하고 사용할 폰트를 설정합니다.
import './globals.css';
import { Inter, Noto_Sans_KR } from 'next/font/google'; // 사용할 폰트 임포트
// 1. Inter 폰트 설정 (영문 기본 폰트)
const inter = Inter({
subsets: ['latin'], // 필요한 서브셋 지정 (용량 최적화)
display: 'swap', // FOIT/FOUT 방지를 위한 display 속성 (자동 설정되지만 명시적 지정)
variable: '--font-inter', // CSS 변수로 사용하기 위한 이름 지정
});
// 2. Noto Sans KR 폰트 설정 (한글 폰트)
// weight: 사용할 폰트 두께 지정 (용량 최적화)
// display: 'swap' (FOUT 방식)
// variable: CSS 변수 이름 지정
const notoSansKr = Noto_Sans_KR({
subsets: ['latin'], // 한글 폰트이지만 'latin' 서브셋도 필요할 수 있음
weight: ['100', '400', '700', '900'], // 사용할 두께만 지정하여 용량 최적화
display: 'swap',
variable: '--font-noto-sans-kr',
});
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
// 3. body 태그에 CSS 변수 클래스 적용
// classNames 라이브러리 등을 사용하여 조건부 클래스 적용 가능
<html lang="ko" className={`${inter.variable} ${notoSansKr.variable}`}>
<body>{children}</body>
</html>
);
}
CSS에서 폰트 사용:
src/app/globals.css
파일에서 위에서 설정한 CSS 변수를 사용하여 폰트를 적용합니다.
html, body {
padding: 0;
margin: 0;
font-family: var(--font-noto-sans-kr), var(--font-inter), sans-serif;
/* 한글 폰트를 먼저 지정하여 우선 순위를 줍니다. */
}
h1, h2, h3, h4, h5, h6 {
font-family: var(--font-noto-sans-kr), var(--font-inter), sans-serif;
}
/* 특정 요소에 Inter 폰트 적용 예시 */
.english-only {
font-family: var(--font-inter), sans-serif;
}
/* 폰트 두께 사용 예시 */
.font-thin {
font-weight: var(--font-noto-sans-kr), 100;
}
.font-bold {
font-weight: var(--font-noto-sans-kr), 700;
}
next/font/google
의 주요 옵션
subsets
: 사용할 폰트 서브셋을 지정합니다 (예:['latin']
,['korean']
). 필요한 서브셋만 로드하여 파일 크기를 줄이는 데 매우 중요합니다. 한글 폰트의 경우['korean']
을 사용할 수 있지만, 일부 글리프가 누락될 수 있으므로['latin', 'korean']
을 함께 사용하는 경우도 있습니다.weight
: 사용할 폰트 두께를 지정합니다 (예:['400', '700']
). 모든 두께를 로드할 필요 없이 필요한 두께만 로드하여 용량을 최적화합니다.display
: 폰트 로딩 전략을 설정합니다.swap
(권장): 폰트가 로드될 때까지 대체 폰트로 텍스트를 먼저 표시한 후, 폰트가 로드되면 교체합니다. (FOUT 방지, CLS 발생 가능)optional
:swap
과 유사하지만, 네트워크 상황이 좋지 않으면 웹 폰트 대신 시스템 폰트를 계속 사용합니다.fallback
: 폰트가 로드될 때까지 텍스트를 숨기다가 로딩되면 표시합니다. (FOIT 발생)auto
: 브라우저 기본 동작에 따릅니다.
variable
: 폰트의 CSS 변수 이름을 지정합니다. 이를 통해 CSS 파일에서var(--font-변수명)
형태로 폰트를 쉽게 적용할 수 있습니다.preload
: (불리언) 폰트를 미리 로드할지 여부를 결정합니다. 기본적으로true
입니다. LCP(Largest Contentful Paint) 개선에 도움이 됩니다.
로컬 폰트 최적화
직접 호스팅하는 폰트 파일도 next/font/local
을 사용하여 최적화할 수 있습니다.
실습: 로컬 폰트 적용하기
폰트 파일 준비:
프로젝트 내에 폰트 파일을 저장할 디렉토리를 생성합니다 (예: src/fonts
). 여기에 my-custom-font.woff2
와 같은 폰트 파일을 넣습니다. WOFF2 형식은 가장 최신이고 압축 효율이 좋은 형식으로 권장됩니다.
폰트 불러오기:
next/font/local
을 사용하여 폰트 파일을 불러옵니다.
import './globals.css';
import localFont from 'next/font/local'; // 로컬 폰트 임포트
import { Inter } from 'next/font/google';
const inter = Inter({ subsets: ['latin'], variable: '--font-inter', display: 'swap' });
// 1. 로컬 폰트 설정
const myCustomFont = localFont({
src: [
// 여러 폰트 두께 및 스타일을 지정할 수 있습니다.
{
path: '../fonts/my-custom-font-thin.woff2',
weight: '100',
style: 'normal',
},
{
path: '../fonts/my-custom-font-regular.woff2',
weight: '400',
style: 'normal',
},
{
path: '../fonts/my-custom-font-bold.woff2',
weight: '700',
style: 'normal',
},
],
display: 'swap',
variable: '--font-my-custom',
});
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
// 2. body 태그에 CSS 변수 클래스 적용
<html lang="ko" className={`${inter.variable} ${myCustomFont.variable}`}>
<body>{children}</body>
</html>
);
}
CSS에서 폰트 사용
html, body {
padding: 0;
margin: 0;
font-family: var(--font-my-custom), var(--font-inter), sans-serif;
}
next/font/local
의 주요 옵션
src
: 폰트 파일의 경로 배열. 각 객체는path
,weight
,style
을 포함할 수 있습니다.display
,preload
,variable
등은 Google Fonts와 동일하게 작동합니다.
폰트 최적화를 위한 추가 전략
next/font
를 사용하는 것 외에도 폰트 로딩 성능을 더욱 개선할 수 있는 몇 가지 전략이 있습니다.
- 필요한 폰트만 로드: 모든 폰트 두께(thin, regular, bold, black 등)와 모든 스타일(normal, italic)을 한꺼번에 로드하면 폰트 파일 용량이 크게 늘어납니다. 실제로 사용되는 두께와 스타일만 선택하여 로드하세요. (Google Fonts의
weight
및subsets
옵션 활용) - 폰트 형식 선택
- WOFF2: 현재 가장 효율적인 웹 폰트 형식입니다. 대부분의 최신 브라우저에서 지원하며, WOFF보다 약 30% 더 압축됩니다.
- WOFF: WOFF2를 지원하지 않는 구형 브라우저를 위한 대체 형식으로 사용할 수 있습니다.
- TrueType (TTF), OpenType (OTF): 웹에서는 거의 사용되지 않으며, 파일 크기가 매우 크므로 피해야 합니다.
font-display
속성 이해 및 활용: CSS@font-face
규칙에서font-display
속성을 사용하여 폰트 로딩 방식을 제어할 수 있습니다.next/font
는 이 속성을 자동으로 최적화해 주지만, 수동으로 폰트를 로드할 경우 중요합니다.swap
(권장): 폰트가 로드될 때까지 대체 폰트를 먼저 표시한 후, 웹 폰트로 교체합니다. 사용자에게 빠르게 텍스트를 보여줄 수 있어 FOIT를 방지합니다.block
: 폰트가 로드될 때까지 텍스트를 숨깁니다 (FOIT). 짧은 기간만 숨기고 이후 대체 폰트를 보여줄 수 있습니다.fallback
:block
과swap
의 중간. 텍스트를 짧게 숨긴 후 대체 폰트를 표시하고, 웹 폰트 로딩이 완료되면 교체합니다.optional
:fallback
과 유사하지만, 네트워크 상황에 따라 웹 폰트 로드를 포기하고 대체 폰트를 계속 사용할 수 있습니다.
font-loading
API (고급): JavaScript의Font Loading API
를 사용하여 폰트 로딩 상태를 직접 제어하고, 폰트 로드 완료 후 특정 동작을 수행할 수 있습니다. 이는 더 세밀한 제어가 필요할 때 사용됩니다.- 폰트 서브셋팅 (Subsetting): 사용하지 않는 언어 글리프나 문자를 제거하여 폰트 파일 크기를 줄이는 기술입니다. 한글 폰트의 경우 모든 문자를 포함하면 용량이 매우 커지므로, 특정 목적(예: 웹사이트 제목)으로만 사용되는 폰트라면 서브셋팅을 고려할 수 있습니다. (폰트 제공업체 또는 도구를 통해 가능)
- CDN 활용: 폰트 파일을 CDN(Content Delivery Network)에 호스팅하면 사용자에게 지리적으로 가까운 서버에서 폰트를 제공하여 로딩 속도를 향상시킬 수 있습니다.
폰트 최적화는 웹 성능 최적화의 중요한 부분이며, next/font
와 같은 Next.js의 내장 도구를 활용하면 개발자가 직접 복잡한 설정을 할 필요 없이 빠르고 안정적인 폰트 로딩을 구현할 수 있습니다.