Next.js i18n
Next.js 앱을 여러 언어로 전환하기 위한 구조적 가이드: 폴더 레이아웃이 어떻게 되어야 하는지, 잘못되었을 때 무엇이 깨지는지, 피해야 할 사항은 무엇인지 설명합니다.
먼저 라이브러리를 선택하세요
기본적으로 App Router의 새로운 프로젝트에는 next-intl을 사용하세요. 빌드 플러그인, 매크로 단계, 로케일별 인스턴스 관리가 필요 없으며, 메시지는 소스 로케일을 기준으로 타입이 지정된 일반 JSON입니다.
| 라이브러리 | 사용 시기 | 비용 |
|---|---|---|
| next-intl (기본) | 새로운 App Router 작업, 모든 팀 규모 | 없음 — 일반 JSON, 코드 생성 불필요 |
| @lingui/react | 컴파일 타임 추출과 작은 런타임, 또는 React/Vue/Astro에서 공통 i18n 레이어가 필요할 때 | SWC 또는 Babel 매크로 플러그인, 정확히 고정 필요 |
| next-i18next | 팀이 이미 i18next를 알고 있거나, Pages Router 코드를 마이그레이션할 때 | 요청별 인스턴스, getT/useT 분리 |
Next.js 내장 i18n | Pages Router 전용. App Router에서는 사용 불가 | 해당 없음 |
Next.js의 i18n: { locales, defaultLocale } 설정은 Pages Router 전용입니다. App Router에서는 아무 효과가 없습니다. App Router에서 로케일 라우팅은 [locale] 세그먼트와 프록시를 의미합니다. 에이전트가 App Router 프로젝트에서 내장 i18n을 '활성화'하도록 두지 마세요.
번들 비용과 라이브러리별 트레이드오프를 포함한 전체 비교는 다음을 참조하세요:
references/library-comparison.md.
표준 아키텍처
정적 딕셔너리는 src/ 외부에 두어 번들링과 가져오기 경로가 명확하게 유지되고, 모든 로케일 관련 메커니즘을 한 폴더에 모으세요.
messages/ # 순수 정적 딕셔너리 — /src 외부 유지
├── en.json # 소스 of truth; 다른 모든 로케일이 이를 미러링
├── de.json
└── ja.json
src/
├── i18n/
│ ├── routing.ts # defineRouting: locales + defaultLocale
│ ├── request.ts # getRequestConfig: 로케일 해석, 메시지 로드
│ └── navigation.ts # createNavigation: 로케일 인식 Link/redirect
├── proxy.ts # 로케일 감지 + 리디렉션 (Next 16; 이전에는 middleware.ts)
└── app/
├── [locale]/ # 전체 로컬라이즈드 앱을 감쌈
│ ├── layout.tsx # 로케일 검증, <html lang>, generateStaticParams
│ └── page.tsx
└── not-found.tsx # 전역 404는 [locale] 외부에 위치
이 구조가 작동하게 하는 규칙:
[locale]는 루트 동적 세그먼트입니다. 모든 로컬라이즈드 콘텐츠는 그 아래에 있습니다. 루트 레벨 라우트(not-found.tsx,error.tsx,api/)는 외부에 있어야 하며 무조건 i18n 훅을 호출해서는 안 됩니다. 그렇지 않으면 런타임에 충돌합니다.- 하나의 소스 로케일.
messages/en.json이 마스터입니다. 다른 모든 파일은 그 형태를 정확히 미러링해야 합니다. - 모든 로케일 지식은
src/i18n/에 있습니다. 컴포넌트는@/i18n/navigation에서Link를 가져오고,next/link에서 가져오지 않습니다. 언어를 추가할 때 한 곳만 수정하면 됩니다.
설정 (신규 프로젝트)
다음 순서대로 실행하세요. 순서가 중요합니다. proxy.ts는 routing.ts에 의존하고, 레이아웃은 request.ts에 의존합니다.
next-intl을 설치하고,createNextIntlPlugin()으로next.config.ts를 감쌉니다.defineRouting을 사용하여src/i18n/routing.ts를 생성합니다.createMiddleware(routing)과 기본 매처를 내보내는src/proxy.ts를 생성합니다.- 로케일을 해석하는
getRequestConfig를 사용하여src/i18n/request.ts를 생성합니다. app/**을app/[locale]/**아래로 이동합니다.generateStaticParams를 추가하고 매개변수를 검증합니다.createNavigation(routing)을 사용하여src/i18n/navigation.ts를 생성합니다.messages/en.json을 생성하고 하드코딩된 문자열을useTranslations로 교체합니다.<html lang>을 설정하고 로컬라이즈드 메타데이터와 hreflang을 추가합니다.
모든 단계의 정확한 파일 내용은 references/next-intl-setup.md를 참조하세요.
주의사항
i18n 코드를 작성하기 전에 이 섹션을 읽으세요. 시간을 낭비하게 만드는 실패 사례들입니다.
setRequestLocale은 레거시입니다. Next.js 16.3+에서는getRequestConfig내부에서next/root-params로 로케일을 읽습니다. 대부분의 가이드, 튜토리얼, LLM 훈련 데이터는 여전히setRequestLocale(params.locale)을 가르칩니다. 여전히 작동하며 하위 호환으로 문서화되어 있지만, 이전 API입니다. Next < 16.3에서만 사용하세요.generateStaticParams는 어느 경우든 계속 필요합니다.params는 Next 15+에서 Promise입니다.const {locale} = await params.params.locale을 런타임 오류가 아닌 타입 오류로 취급하세요.- 로케일을 검증하지 않으면 알 수 없는 값이 렌더링됩니다.
hasLocale(routing.locales, locale)과notFound()를 사용하세요. 그렇지 않으면/xx/about이 가짜 URL에서 영어 페이지를 렌더링합니다. - Next 16에서는
middleware.ts가 아닌proxy.ts입니다.middleware.ts는 더 이상 사용되지 않습니다. 프록시는 Node.js에서만 실행됩니다. Edge 런타임은 지원되지 않습니다. Lingui와 next-i18next는 Edge 안전 프록시를 광고합니다. Edge가 필요하다면, 공짜가 아닌 실제 트레이드오프가 있습니다. - 번역된 문자열을 절대 연결하지 마세요. `