Vercel에서 다국어 라우팅 설정
Next.js와 Vercel을 사용하여 다국어 라우팅을 설정하는 방법을 알아봅니다.
Vercel에서 다국어 라우팅 설정
Introduction
글로벌 애플리케이션을 개발할 때 다국어 지원은 필수적입니다. Next.js와 Vercel을 사용하여 효율적인 다국어 라우팅을 구현하는 방법을 알아보겠습니다.
Environment
- Vercel 프로젝트 (Next.js 14+)
- next-intl 라이브러리
- TypeScript
- React 18+
Problem
다국어 라우팅 설정 시 발생하는 문제:
// 하드코딩된 다국어 처리
export default function Home({ params }) {
const lang = params.lang;
if (lang === 'ko') {
return 안녕하세요
;
} else if (lang === 'en') {
return Hello
;
}
// 유지보수 어려움, 확장성 부족
}Analysis
Next.js 다국어 라우팅 방식:
- Locale in Path:
/ko/about,/en/about - Subdomain:
ko.example.com,en.example.com - Query Parameter:
?lang=ko
Vercel에서의 고려사항:
- 미들웨어를 통한 라우팅 처리
- SEO 최적화 (hreflang 태그)
- 빌드 시간 최적화
Solution
1. next-intl 설정
npm install next-intl2. 다국어 설정 파일
// i18n/request.js
import { getRequestConfig } from 'next-intl/server';
import { cookies } from 'next/headers';
export default getRequestConfig(async () => {
const cookieStore = cookies();
const locale = cookieStore.get('locale')?.value || 'en';
return {
locale,
messages: (await import(`../messages/${locale}.json`)).default,
};
});3. 메시지 파일 구조
// messages/en.json
{
"common": {
"welcome": "Welcome",
"hello": "Hello, {name}!"
},
"home": {
"title": "Home Page",
"description": "Welcome to our application"
},
"about": {
"title": "About Us"
}
}// messages/ko.json
{
"common": {
"welcome": "환영합니다",
"hello": "안녕하세요, {name}!"
},
"home": {
"title": "홈 페이지",
"description": "アプリケーション에 오신 것을 환영합니다"
},
"about": {
"title": "회사 소개"
}
}4. 미들웨어 설정
// middleware.js
import createMiddleware from 'next-intl/middleware';
import { locales, defaultLocale } from './i18n/config';
export default createMiddleware({
locales,
defaultLocale,
localeDetection: false,
});
export const config = {
matcher: ['/((?!api|_next|.*\\..*).*)'],
};5. i18n 설정
// i18n/config.js
export const locales = ['en', 'ko', 'ja'];
export const defaultLocale = 'en';
export function isValidLocale(locale) {
return locales.includes(locale);
}6. 컴포넌트에서 사용
// app/[locale]/page.js
import { useTranslations } from 'next-intl';
import { setRequestLocale } from 'next-intl/server';
export default function HomePage({ params }) {
setRequestLocale(params.locale);
const t = useTranslations('home');
const common = useTranslations('common');
return (
{t('title')}
{t('description')}
{common('hello', { name: 'World' })}
);
}
export async function generateStaticParams() {
return locales.map((locale) => ({ locale }));
}7. 언어 전환 컴포넌트
// components/LanguageSwitcher.jsx
'use client';
import { useRouter, usePathname } from 'next/navigation';
import { locales } from '@/i18n/config';
export default function LanguageSwitcher({ currentLocale }) {
const router = useRouter();
const pathname = usePathname();
const switchLocale = (newLocale) => {
const segments = pathname.split('/');
segments[1] = newLocale;
router.push(segments.join('/'));
};
return (
{locales.map((locale) => (
))}
);
}8. SEO 메타데이터
// app/[layout]/layout.js
import { getTranslations } from 'next-intl/server';
export async function generateMetadata({ params }) {
const t = await getTranslations({ locale: params.locale });
return {
title: t('home.title'),
description: t('home.description'),
alternates: {
languages: {
en: '/en',
ko: '/ko',
ja: '/ja',
},
},
};
}9. 동적 번역 가져오기
// lib/i18n-utils.js
export async function getTranslation(locale, namespace, key) {
try {
const messages = await import(`../messages/${locale}.json`);
const keys = `${namespace}.${key}`.split('.');
let value = messages.default;
for (const k of keys) {
value = value?.[k];
}
return value || key;
} catch (error) {
console.error(`Translation error: ${locale}.${namespace}.${key}`);
return key;
}
}Lessons Learned
- 구조화된 번역: 번역 파일을 네임스페이스별로 분리하면 관리가 용이합니다.
- SEO 고려: hreflang 태그와 함께 올바른 URL 구조를 설정해야 합니다.
- 기본 언어 설정: 사용자의 브라우저 설정이나 위치를 기반으로 기본 언어를 설정하면 좋습니다.
- 번역 완성도: 모든 언어에 대한 번역이 완료되어야 합니다.
- 테스트: 다양한 언어에서의 렌더링을 테스트해야 합니다.
This blog does not accept any external sponsorships, affiliate marketing, or ad revenue.