로케일 기반 라우팅 설정
원본 URL: https://next-intl.dev/docs/routing/setup
로케일 기반 라우팅 설정
섹션 제목: “로케일 기반 라우팅 설정”영상으로 보는 편이 좋으신가요?
앱이 지원하는 각 언어마다 고유한 경로명을 사용하기 위해 next-intl은 다음 라우팅 구성을 처리하는 데 사용할 수 있습니다:
- 프리픽스 기반 라우팅 (예:
/en/about) - 도메인 기반 라우팅 (예:
en.example.com/about)
어느 경우든 next-intl은 최상위 [locale] 동적 세그먼트를 사용해 App Router와 통합되며, 이를 통해 다양한 언어의 콘텐츠를 제공할 수 있습니다.
초기 설정
섹션 제목: “초기 설정”로케일 기반 라우팅을 시작하기 위해 다음 파일들을 설정하겠습니다:
src/i18n/routing.ts
섹션 제목: “src/i18n/routing.ts”라우팅 구성을 정의하는 중앙 위치로 routing.ts를 사용합니다:
src/i18n/routing.ts
import {defineRouting} from 'next-intl/routing';
export const routing = defineRouting({ // A list of all locales that are supported locales: ['en', 'de'],
// Used when no locale matches defaultLocale: 'en' });요구사항에 따라 이후 라우팅 구성을 커스터마이징할 수 있지만, 우선 설정을 먼저 마무리해 보겠습니다.
src/proxy.ts
섹션 제목: “src/proxy.ts”라우팅 구성이 준비되면 이를 사용해 프록시를 설정할 수 있습니다:
src/proxy.ts
import createMiddleware from 'next-intl/middleware'; import {routing} from './i18n/routing';
export default createMiddleware(routing);
export const config = { // Match all pathnames except for // - … if they start with `/api`, `/trpc`, `/_next` or `/_vercel` // - … the ones containing a dot (e.g. `favicon.ico`) matcher: '/((?!api|trpc|_next|_vercel|.*\\..*).*)' };참고: proxy.ts는 Next.js 16 이전까지 middleware.ts라고 불렸습니다.
/users/jane.doe처럼 점이 포함된 경로명은 어떻게 매칭하나요?
점이 포함되는 경로명을 예상하는 경우, 명시적인 항목으로 매칭할 수 있습니다:
src/proxy.ts
// ...
export const config = { matcher: [ // Match all pathnames except for // - … if they start with `/api`, `/trpc`, `/_next` or `/_vercel` // - … the ones containing a dot (e.g. `favicon.ico`) '/((?!api|trpc|_next|_vercel|.*\\..*).*)',
// Match all pathnames within `{/:locale}/users` '/([\\w-]+)?/users/(.+)' ] };이렇게 하면 예를 들어 /users/jane.doe를 매칭하며, 로케일 프리픽스가 있는 경우도 선택적으로 매칭합니다.
src/i18n/navigation.ts
섹션 제목: “src/i18n/navigation.ts”추가로, 라우팅 구성을 사용해 내비게이션 API를 설정할 수 있습니다:
src/i18n/navigation.ts
import {createNavigation} from 'next-intl/navigation'; import {routing} from './routing';
// Lightweight wrappers around Next.js' navigation // APIs that consider the routing configuration export const {Link, redirect, usePathname, useRouter, getPathname} = createNavigation(routing);src/i18n/request.ts
섹션 제목: “src/i18n/request.ts”이제 요청 구성에서 매칭된 로케일을 읽을 수 있습니다:
src/i18n/request.ts
import {getRequestConfig} from 'next-intl/server'; import {hasLocale} from 'next-intl'; import {routing} from './routing';
export default getRequestConfig(async ({requestLocale}) => { // Typically corresponds to the `[locale]` segment const requested = await requestLocale; const locale = hasLocale(routing.locales, requested) ? requested : routing.defaultLocale;
return { locale // ... }; });src/app/[locale]/layout.tsx
섹션 제목: “src/app/[locale]/layout.tsx”설정을 완료하기 위해 기존의 모든 레이아웃과 페이지를 [locale] 세그먼트로 이동합니다:
src └── app └── [locale] ├── layout.tsx ├── page.tsx └── ...이제 매칭된 locale은 [locale] 파라미터를 통해 사용할 수 있습니다:
app/[locale]/layout.tsx
import {NextIntlClientProvider, hasLocale} from 'next-intl'; import {notFound} from 'next/navigation'; import {routing} from '@/i18n/routing';
type Props = { children: React.ReactNode; params: Promise<{locale: string}>; };
export default async function LocaleLayout({children, params}: Props) { // Ensure that the incoming `locale` is valid const {locale} = await params; if (!hasLocale(routing.locales, locale)) { notFound(); }
// ... }이것으로 충분합니다! 여기서부터는 라우팅 구성을 통해 필요에 맞게 조정할 수 있습니다.
문제가 발생했다면, 동작하는 앱을 확인할 수 있는 App Router 예제를 참고해 보세요.
정적 렌더링
섹션 제목: “정적 렌더링”로케일 기반 라우팅을 사용할 때 현재 next-intl은 Server Components에서 useTranslations 같은 API를 사용하면 동적 렌더링을 선택합니다. 이는 향후 제거를 목표로 하는 제한사항이지만, 임시 해결책으로 next-intl은 정적 렌더링을 활성화할 수 있는 임시 API를 제공합니다.
generateStaticParams 추가
섹션 제목: “generateStaticParams 추가”[locale] 파라미터에 동적 라우트 세그먼트를 사용하고 있으므로, 빌드 시점에 라우트를 렌더링할 수 있도록 generateStaticParams를 사용해야 합니다.
필요에 따라 generateStaticParams를 레이아웃 또는 페이지에 추가할 수 있습니다:
- 레이아웃: 이 레이아웃 내 모든 페이지에 정적 렌더링을 활성화합니다 (예:
app/[locale]/layout.tsx) - 개별 페이지: 특정 페이지에 정적 렌더링을 활성화합니다 (예:
app/[locale]/page.tsx)
예시:
import {routing} from '@/i18n/routing';
export function generateStaticParams() { return routing.locales.map((locale) => ({locale})); }generateStaticParams에서 모든 로케일을 반환해야 하나요?
빌드 시점에 특정 로케일만 렌더링하거나 아예 렌더링하지 않으려면, generateStaticParams에서 반환할 항목을 선택적으로 지정할 수 있습니다 (Next.js 문서의 Static Rendering 참고).
관련된 모든 레이아웃과 페이지에 setRequestLocale 추가
섹션 제목: “관련된 모든 레이아웃과 페이지에 setRequestLocale 추가”next-intl은 레이아웃과 페이지에서 params를 통해 전달받은 로케일을 분배하여, 해당 요청의 일부로 렌더링되는 모든 Server Components에서 사용할 수 있게 하는 API를 제공합니다.
app/[locale]/layout.tsx
import {setRequestLocale} from 'next-intl/server'; import {hasLocale} from 'next-intl'; import {notFound} from 'next/navigation'; import {routing} from '@/i18n/routing';
type Props = { children: React.ReactNode; params: Promise<{locale: string}>; };
export default async function LocaleLayout({children, params}: Props) { const {locale} = await params; if (!hasLocale(routing.locales, locale)) { notFound(); }
// Enable static rendering setRequestLocale(locale);
return ( // ... ); }app/[locale]/page.tsx
import {use} from 'react'; import {setRequestLocale} from 'next-intl/server'; import {useTranslations} from 'next-intl';
export default function IndexPage({params}) { const {locale} = use(params);
// Enable static rendering setRequestLocale(locale);
// Once the request locale is set, you // can call hooks from `next-intl` const t = useTranslations('IndexPage');
return ( // ... ); }다음 사항을 유의하세요:
setRequestLocale에 전달하는 로케일은 검증되어야 합니다 (예: 루트 레이아웃에서).- Next.js는 레이아웃과 페이지를 독립적으로 렌더링할 수 있으므로, 정적 렌더링을 활성화하려는 모든 페이지와 모든 레이아웃에서 이 함수를 호출해야 합니다.
useTranslations또는getMessages같은next-intl함수들을 호출하기 전에setRequestLocale를 호출해야 합니다.
next-intl은 현재 로케일을 저장하는 변경 가능한 저장소를 만들기 위해 cache()를 사용합니다. setRequestLocale를 호출하면 현재 로케일이 저장소에 기록되어, 로케일이 필요한 모든 API에서 사용할 수 있게 됩니다.
이 저장소는 요청 범위로 한정되므로, 특정 요청이 비동기로 처리되는 동안 병렬로 처리되는 다른 요청에는 영향을 주지 않습니다.
현재 Next.js는 Server Components의 임의 위치에서 locale 같은 라우트 파라미터를 읽는 API를 제공하지 않습니다 (vercel/next.js#58862 참고). locale은 next-intl이 제공하는 모든 API의 핵심이므로, 이를 트리 전체에 prop으로 계속 전달하는 방식은 사용성이 좋지 않습니다.
이 때문에 next-intl은 미들웨어를 사용해 협상된 로케일 값을 담은 x-next-intl-locale 헤더를 들어오는 요청에 추가합니다. 이 방식으로 headers().get('x-next-intl-locale')를 통해 임의 위치에서 로케일을 읽을 수 있습니다.
하지만 headers를 사용하면 해당 라우트는 동적 렌더링으로 전환됩니다.
setRequestLocale를 사용하면 레이아웃과 페이지에서 params로 받은 로케일을 next-intl에 제공할 수 있습니다. 이제 next-intl의 모든 API는 헤더 대신 이 값을 읽을 수 있으므로 정적 렌더링이 가능해집니다.
메타데이터에서 locale 파라미터 사용
섹션 제목: “메타데이터에서 locale 파라미터 사용”페이지 렌더링뿐 아니라 페이지 메타데이터도 정적 렌더링 조건을 충족해야 합니다.
이를 위해 Next.js에서 params로 받은 locale을 next-intl의 await 가능한 함수들에 전달할 수 있습니다.
page.tsx
import {getTranslations} from 'next-intl/server';
export async function generateMetadata({params}) { const {locale} = await params; const t = await getTranslations({locale, namespace: 'Metadata'});
return { title: t('title') }; }