Next.js Git Blog (4): 폰트 설정 및 최적화

목차

소개#

보기 좋은 떡이 먹기도 좋다고, 블로그 인상에 폰트가 주는 영향은 상당히 크다고 생각된다.
이번 포스팅에서는 Next.js 환경에서 폰트를 불러오는 두 가지 방식(Google Fonts, Local Fonts)을 살펴보고자 한다.

사용할 폰트 조합:

  • 코드 영역: Fira Code
  • 본문 영역: Pretendard

설정 준비#

Next.js는 next/font를 통해 폰트를 최적화한다. 따라서 먼저 폰트 설정을 한곳에서 관리할 수 있도록 별도의 파일을 준비하자.

src
└── fonts
    └── index.ts

📌 왜 public 디렉토리에 직접 넣지 않을까?

물론 public 폴더에 넣고 경로로 불러올 수도 있다. 하지만 next/font 기능을 사용하면 빌드 타임에 폰트 파일을 자동으로 최적화하고, 필요한 CSS를 미리 생성해 줍니다. 무엇보다 폰트 로딩 시 발생하는 레이아웃 시프트(CLS) 현상을 방지해주기 때문에 성능적으로 좋은 효과를 가질 수 있다.

내부 폰트 설정: Google Fonts (Fira Code)#

코드 영역에 적용할 Fira Code를 가져와 보자. 이 폰트는 Google Fonts에서 제공하므로, 파일을 따로 다운로드할 필요 없이 next/font/google을 통해 간편하게 불러올 수 있다.

// src/fonts/index.ts
import { Fira_Code } from 'next/font/google';
 
export const firaCode = Fira_Code({
	subsets: ['latin'],
	weight: ['400', '500', '600', '700'],
	display: 'swap',
	variable: '--font-fira-code',
});

외부 폰트 설정: Local Fonts (Pretendard)#

본문에 사용할 Pretendard는 로컬 폰트 방식으로 설정한다. 확인해보니 Variable 폰트 파일의 경우 용량이 약 3MB에 달하는데, 웹 환경에는 조금 부담스러운 크기라고 생각한다.

그래서 성능을 위해 전체 폰트 대신 필요한 글자만 담은 Subset 파일(woff2) 을 사용하는 것을 권장한다.
사용하고 싶은 서브셋 폰트들을 프로젝트 내부 경로로 위치시키자.

src
└── fonts
    ├── Pretendard-Bold.subset.woff2
    ├── Pretendard-Medium.subset.woff2
    ├── Pretendard-Regular.subset.woff2
    ├── Pretendard-SemiBold.subset.woff2
    └── index.ts

그 후 next/font/local을 이용해 아래와 같이 정의한다.

// src/fonts/index.ts
import localFont from 'next/font/local';
 
export const pretendard = localFont({
	src: [
		{
			path: './Pretendard-Regular.subset.woff2',
			weight: '400',
		},
		{
			path: './Pretendard-Medium.subset.woff2',
			weight: '500',
		},
		{
			path: './Pretendard-SemiBold.subset.woff2',
			weight: '600',
		},
		{
			path: './Pretendard-Bold.subset.woff2',
			weight: '700',
		},
	],
	display: 'swap',
	variable: '--font-pretendard',
});

폰트 적용#

이제 정의한 폰트들을 컴포넌트에 적용해 보자. 상황에 따라 아래 중 가장 괜찮은 방식을 선택해 사용하면 좋을 것 같다.

1. 컴포넌트 수준에서 적용 (className)

가장 간단한 방법으로, Next.js 폰트 객체에서 제공하는 className 속성을 사용하는 방식이다. 특정 컴포넌트나 레이아웃에만 폰트를 입히고 싶을 때 유용하다.

import { pretendard } from '@/fonts';
 
export default function Intro() {
  return (
    <section className={pretendard.className}>
      <h1>폰트가 적용된 소개 페이지</h1>
    </section>
  );
}

2. CSS 스타일 시트에서 활용 (CSS Variable)

next/font 설정 시 지정했던 variable 값을 이용해 일반 CSS 파일이나 Inline Style에서 활용하는 방식이다.

/* globals.css */
.custom-title {
	font-family: var(--font-pretendard);
	font-weight: 700;
}
 
code {
	font-family: var(--font-fira-code);
}

3. Tailwind v4와 연동하여 활용

Tailwind v4에서는 메인 CSS 파일(globals.css)에서 변수를 직접 매핑하는 방식을 사용한다. 먼저 폰트 변수를 최상위 태그에 주입한다.

// src/pages/_app.tsx
<html lang="ko" className={`${pretendard.variable} ${firaCode.variable}`}>

그 후, CSS 파일에서 @theme 블록을 이용해 기본 font-family를 우리가 정의한 폰트로 덮어씌운다.

/* src/styles/globals.css */
@import 'tailwindcss';
 
@theme {
	/* --font-pretendard 변수를 Tailwind의 sans 폰트로 지정 */
	--font-sans: var(--font-pretendard);
 
	/* --font-fira-code 변수를 Tailwind의 mono 폰트로 지정 */
	--font-mono: var(--font-fira-code);
}

이렇게 설정하면 별도의 스크립트 설정 없이도, 프로젝트 전체에서 font-sans(기본)와 font-mono(코드 영역) 클래스를 사용할 때 우리가 설정한 최적화된 폰트를 적용할 수 있다.

📌 주의: Tailwind 테마 변수와 일반 CSS 변수의 차이

Tailwind v4의 @theme 내부에 정의한 --font-sans 같은 값들은 Tailwind 내부의 유틸리티 클래스를 생성하기 위한 용도다. 따라서 일반 CSS 파일에서 var(--font-sans)를 직접 사용하면 브라우저가 인식하지 못할 수 있다.


마무리#

폰트 설정을 마치며 느낀 점은, 블로그의 전반적인 스타일은 Tailwind CSS로 제어하고 마크다운 본문은 일반 CSS로 관리하다 보니 스타일 정의가 이원화되는 느낌이 들었다.

이를 좀 더 깔끔하게 관리할 수 있는 방법을 한 번 찾아봐야겠다.