Next.js Font Optimization (Fonts) 정리
Next.js Docs – Getting Started: Font Optimization (Fonts) 페이지를 기반으로, App Router 환경에서의 폰트 사용 및 최적화 방식을 정리한 문서입니다.
1. 개요
Next.js는 next/font 모듈을 통해 폰트를 자동으로 최적화합니다.
- 외부 네트워크 요청(예: Google Fonts CDN)을 제거하여 프라이버시와 성능을 개선합니다.
- 어떤 폰트 파일이든 자동 self-hosting(직접 호스팅)을 지원합니다.
- 폰트를 최적화된 방식으로 로드하여 레이아웃 시프트(Layout Shift) 없이 텍스트를 표시할 수 있습니다.
기본 사용 방식은 다음과 같습니다.
next/font/google또는next/font/local에서 폰트 함수를 import- 해당 함수를 옵션과 함께 호출하여 폰트 인스턴스를 생성
- 생성된 인스턴스의
className을 적용하고 싶은 요소에 지정
// app/layout.tsx
import { Geist } from 'next/font/google'
const geist = Geist({
subsets: ['latin'],
})
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" className={geist.className}>
<body>{children}</body>
</html>
)
}
- 폰트는 사용된 컴포넌트 범위(scope) 에만 적용됩니다.
- 애플리케이션 전체에 공통 폰트를 적용하고 싶다면 Root Layout (
app/layout.tsx) 에서 설정하는 것이 일반적입니다.
2. Google Fonts
2.1 자동 self-hosting
Next.js는 Google Fonts를 자동으로 self-hosting 합니다.
- 선택한 Google Font가 빌드 시 정적 에셋으로 다운로드되어,
- 실제 서비스 시에는 자체 도메인에서 폰트 파일을 제공합니다.
- 사용자가 웹사이트에 접속할 때 브라우저는 Google 서버로 요청을 보내지 않습니다.
2.2 기본 사용 예시
// app/layout.tsx
import { Geist } from 'next/font/google'
const geist = Geist({
subsets: ['latin'],
})
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en" className={geist.className}>
<body>{children}</body>
</html>
)
}
next/font/google에서 사용할 폰트를 import 합니다.subsets옵션에['latin'],['latin', 'latin-ext']등 필요한 서브셋만 지정하면- 다운로드되는 폰트 파일 크기를 줄여 성능을 최적화할 수 있습니다.
2.3 Variable Font 권장
문서에서는 variable font(가변 폰트) 사용을 추천합니다.
- 장점
- 한 파일에 여러 굵기/스타일이 포함되므로 요청 수와 용량을 줄일 수 있음
- 디자인적으로 더 유연하게 폰트 두께를 조절 가능
- Google Fonts에서 variable font를 지원하는 패밀리를 선택하는 것이 좋습니다.
2.4 Variable Font를 사용하지 못할 경우
Variable Font를 사용할 수 없는 경우, 명시적으로 weight(굵기)를 지정해야 합니다.
// app/layout.tsx
import { Roboto } from 'next/font/google'
const roboto = Roboto({
weight: '400', // 또는 ['400', '700'] 처럼 배열도 가능
subsets: ['latin'],
})
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en" className={roboto.className}>
<body>{children}</body>
</html>
)
}
weight는'400','700'등의 문자열 또는 문자열 배열을 사용할 수 있습니다.- 필요 이상으로 많은 굵기를 포함할수록 폰트 파일 용량이 커지므로,
- 프로젝트에서 실제로 사용하는 굵기만 선택하는 것이 좋습니다.
3. Local Fonts (로컬 폰트)
3.1 기본 사용
로컬 폰트를 사용하려면 next/font/local에서 default export를 import 한 뒤,
src 옵션에 로컬 폰트 파일 경로를 지정합니다.
// app/layout.tsx
import localFont from 'next/font/local'
const myFont = localFont({
src: './my-font.woff2',
})
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en" className={myFont.className}>
<body>{children}</body>
</html>
)
}
- 폰트 파일은 다음 위치에 둘 수 있습니다.
public폴더 내부 (예:/public/fonts/my-font.woff2)app폴더 내부에 컴포넌트와 함께 위치(co-located)
src에 상대 경로를 사용할 때는 해당 파일이 위치한 곳을 기준으로 경로를 작성합니다.
3.2 여러 파일을 하나의 폰트 패밀리로 사용하기
하나의 폰트 패밀리에 대해 여러 굵기/스타일 파일을 함께 사용하고 싶다면,
src를 배열로 지정할 수 있습니다.
import localFont from 'next/font/local'
const roboto = localFont({
src: [
{
path: './Roboto-Regular.woff2',
weight: '400',
style: 'normal',
},
{
path: './Roboto-Italic.woff2',
weight: '400',
style: 'italic',
},
{
path: './Roboto-Bold.woff2',
weight: '700',
style: 'normal',
},
{
path: './Roboto-BoldItalic.woff2',
weight: '700',
style: 'italic',
},
],
})
- 각 항목은 다음 속성을 가집니다.
path: 폰트 파일 경로weight:'400','700'등 폰트 굵기style:'normal'또는'italic'
이렇게 설정해 두면, Next.js가 한 폰트 패밀리를 기준으로
여러 굵기와 스타일을 자동으로 매핑하여 사용할 수 있도록 CSS를 생성합니다.
4. 폰트 적용 범위와 Root Layout
4.1 컴포넌트 스코프
next/font로 생성된 폰트는 해당 폰트를 사용하는 컴포넌트 범위에만 적용됩니다.
- 예를 들어, 특정 섹션 컴포넌트에만 다른 폰트를 적용하고 싶다면,
- 그 컴포넌트 파일에서 폰트를 import/생성하고,
- 원하는 요소에만
className={myFont.className}을 적용하면 됩니다.
// app/(marketing)/hero.tsx
import localFont from 'next/font/local'
const displayFont = localFont({
src: './Display-Title.woff2',
})
export function HeroSection() {
return (
<section>
<h1 className={displayFont.className}>브랜드 메시지</h1>
<p>본문은 기본 폰트 사용</p>
</section>
)
}
4.2 애플리케이션 전체 폰트 설정
앱 전체에 동일한 폰트를 적용하고 싶다면 Root Layout에 설정하는 것이 일반적입니다.
// app/layout.tsx
import { Geist } from 'next/font/google'
const geist = Geist({
subsets: ['latin'],
})
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en" className={geist.className}>
<body>{children}</body>
</html>
)
}
<html>에className={geist.className}을 지정하면,
전체 문서에 해당 폰트가 적용됩니다.- 필요하다면 특정 영역만 다른 폰트로 덮어쓰는 것도 가능합니다.