국제화 (i18n) 구현
현대의 웹 애플리케이션은 전 세계 사용자를 대상으로 하는 경우가 많습니다. 다양한 언어와 문화권을 지원하는 것은 사용자 경험을 향상시키고 더 넓은 시장에 도달하는 데 필수적입니다. 국제화(Internationalization, i18n) 는 이러한 다국어 및 다문화 지원을 위한 애플리케이션 설계 및 개발 과정을 의미합니다. Next.js는 강력한 국제화 라우팅 기능을 내장하고 있어, 다국어 웹사이트 구축을 용이하게 합니다.
이 절에서는 국제화의 개념과 중요성, Next.js의 내장 국제화 기능을 활용한 라우팅 설정, next-i18next
와 같은 라이브러리를 사용한 콘텐츠 번역 및 관리 방법, 그리고 다국어 SEO 최적화 전략에 대해 상세히 알아보겠습니다.
국제화 (i18n)란?
국제화(i18n) 는 소프트웨어가 다양한 언어와 지역(로케일)에 맞게 조정될 수 있도록 하는 개발 프로세스입니다. 여기서 '18'은 'Internationalization' 단어에서 'I'와 'n' 사이에 18개의 글자가 있다는 것을 의미하는 약어입니다.
국제화의 핵심 요소
- 다국어 지원(Localization, L10n): 텍스트 번역뿐만 아니라, 날짜/시간 형식, 숫자 형식, 통화, 주소 형식, 측정 단위, 이미지, 레이아웃 등 특정 지역의 문화적 관습에 맞게 콘텐츠를 조정하는 과정입니다. 'L10n'은 'Localization'에서 'L'과 'n' 사이에 10개의 글자가 있다는 것을 의미합니다.
- 라우팅: 사용자가 선택한 언어에 따라 다른 URL 경로를 제공합니다 (예:
/ko/about
,/en/about
). - SEO 최적화: 검색 엔진이 다국어 콘텐츠를 올바르게 인덱싱하고 해당 지역 사용자에게 적절한 언어 버전을 제공하도록 돕습니다.
Next.js의 내장 국제화 라우팅
Next.js 10부터 국제화 라우팅이 기본적으로 지원됩니다. next.config.js
파일에 i18n
설정을 추가하여 다국어 경로를 구성할 수 있습니다.
next.config.js
설정
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
i18n: {
// 지원하는 모든 로케일(언어) 목록을 정의합니다.
locales: ['en', 'ko', 'ja'], // 영어, 한국어, 일본어
// 기본 로케일 (fallback 언어)
defaultLocale: 'en',
// 서브패스 라우팅 전략을 사용할지 여부 (true 권장)
// true: /en/about, /ko/about
// false: /about (en), /ko/about (ko) - 기본 로케일은 접두사 없음
localeDetection: false, // 브라우저 언어 감지 비활성화 (선택 사항, 기본은 true)
},
// 다른 Next.js 설정들...
};
module.exports = nextConfig;
설명
locales
: 애플리케이션이 지원하는 모든 언어 코드를 배열로 정의합니다. ISO 639-1 (언어 코드) 또는 ISO 639-1 + ISO 3166-1 alpha-2 (언어-국가 코드, 예:en-US
,fr-CA
) 형식을 사용합니다.defaultLocale
: 사용자의 브라우저 언어 설정과 일치하는 언어가 없을 때 기본적으로 사용될 언어를 지정합니다. 이 언어는 일반적으로 URL 접두사가 없습니다 (예:/about
이 영어 페이지).localeDetection
: 브라우저의Accept-Language
헤더를 기반으로 사용자의 언어를 자동으로 감지하여 리다이렉션할지 여부를 결정합니다.false
로 설정하면 사용자가 수동으로 언어를 선택해야 합니다.
라우팅 동작
i18n
설정 후 Next.js는 자동으로 다국어 라우팅을 처리합니다.
-
서브패스 라우팅:
locales
에 정의된 각 언어에 대해/ko/
또는/en/
와 같은 접두사가 URL에 자동으로 추가됩니다./ko/about
/en/about
defaultLocale
에 해당하는 페이지는 접두사가 없을 수 있습니다 (예:/about
이en
페이지).
-
라우터 객체 접근: 클라이언트 컴포넌트나 페이지에서
useRouter
훅을 통해 현재 로케일(router.locale
)에 접근할 수 있습니다.app/some-client-component.tsx // app/some-client-component.tsx "use client"; import { useRouter } from 'next/navigation'; export default function LanguageSwitcher() { const router = useRouter(); const currentLocale = router.locale; // 현재 'en', 'ko', 'ja' 중 하나 const changeLanguage = (locale: string) => { router.push(router.pathname, undefined, { locale }); // 현재 경로 유지하며 언어만 변경 }; return ( <div> <span>현재 언어: {currentLocale}</span> <button onClick={() => changeLanguage('en')}>English</button> <button onClick={() => changeLanguage('ko')}>한국어</button> </div> ); }
콘텐츠 번역 및 관리 (next-i18next
)
Next.js의 내장 국제화 기능은 라우팅만 처리하며, 실제 텍스트 번역은 직접 구현하거나 라이브러리의 도움을 받아야 합니다. next-i18next
는 Next.js와 react-i18next
를 통합하여 서버 사이드 렌더링(SSR) 및 정적 사이트 생성(SSG) 환경에서 다국어 콘텐츠를 쉽게 관리할 수 있도록 해주는 인기 있는 라이브러리입니다.
next-i18next
설치 및 설정
npm install next-i18next react-i18next i18next
# 또는
yarn add next-i18next react-i18next i18next
next-i18next.config.js
(프로젝트 루트에 생성):
// next-i18next.config.js
const path = require('path');
module.exports = {
i18n: {
defaultLocale: 'en',
locales: ['en', 'ko', 'ja'],
},
// 번역 파일이 저장될 경로를 지정합니다.
// 기본적으로 public/locales/[locale]/[namespace].json 형식으로 저장됩니다.
localePath: path.resolve('./public/locales'),
// debug: true, // 디버그 모드 활성화 (개발 시 유용)
};
public/locales
디렉토리 구조
public/locales/en/common.json
예시
{
"greeting": "Hello",
"welcome_message": "Welcome to our website!"
}
public/locales/ko/common.json
예시
{
"greeting": "안녕하세요",
"welcome_message": "저희 웹사이트에 오신 것을 환영합니다!"
}
next.config.js
에 next-i18next
연동
// next.config.js
const { i18n } = require('./next-i18next.config'); // i18n 설정 임포트
/** @type {import('next').NextConfig} */
const nextConfig = {
i18n, // Next.js의 i18n 설정과 연동
// 다른 Next.js 설정들...
};
module.exports = nextConfig;
컴포넌트에서 번역된 텍스트 사용
next-i18next
는 클라이언트 컴포넌트와 서버 컴포넌트 모두에서 번역된 텍스트를 사용할 수 있도록 합니다.
클라이언트 컴포넌트 (App Router)
react-i18next
의 useTranslation
훅을 사용합니다.
// app/components/ClientComponentWithTranslation.tsx
"use client";
import { useTranslation } from 'react-i18next'; // useTranslation 훅 임포트
export default function ClientComponentWithTranslation() {
// 'common' 네임스페이스에서 번역 텍스트를 로드합니다.
// 이 파일이 포함된 라우트 세그먼트의 layout.tsx 또는 page.tsx에서 서버 컴포넌트를 통해 번역 파일을 미리 로드해야 합니다.
const { t } = useTranslation('common');
return (
<div>
<h1>{t('greeting')}</h1>
<p>{t('welcome_message')}</p>
</div>
);
}
서버 컴포넌트 (App Router)
App Router의 서버 컴포넌트에서 번역 텍스트를 사용하려면 @next-localization/i18n
또는 i18next-http-backend
와 같은 추가 라이브러리가 필요하거나, 직접 번역 파일을 로드하는 방식을 구현해야 합니다. next-i18next
는 주로 Page Router의 SSR/SSG 페이지와 App Router의 클라이언트 컴포넌트에서 사용됩니다.
App Router에서 서버 컴포넌트 번역을 위해선 다음과 같은 접근법을 고려할 수 있습니다.
i18next-fs-backend
(Node.js 환경): 서버 컴포넌트는 Node.js 환경에서 실행되므로 파일 시스템에서 직접 번역 파일을 로드할 수 있습니다.server-only-i18n
패턴: 별도의i18n
초기화 함수를 서버 전용으로 만들어 사용합니다.
서버 컴포넌트 번역 예시 (간소화된 접근)
실제 프로덕션 환경에서는 더 견고한 라이브러리(예: next-intl
)를 사용하는 것이 좋습니다. 여기서는 개념 설명을 위해 i18next
를 직접 구성하는 방식을 보여줍니다.
// i18n.ts (서버 전용 i18n 초기화 로직)
import { createInstance } from 'i18next';
import resourcesToBackend from 'i18next-resources-to-backend';
const initI18next = async (lng: string, ns: string | string[]) => {
const i18nInstance = createInstance();
await i18nInstance
.use(resourcesToBackend((language: string, namespace: string) =>
import(`./public/locales/${language}/${namespace}.json`)
))
.init({
lng,
ns,
defaultNS: 'common',
fallbackLng: 'en',
preload: typeof window === 'undefined' ? i18nInstance.options.locales : [],
});
return i18nInstance;
};
export async function getTranslation(lng: string, ns: string | string[]) {
const i18nInstance = await initI18next(lng, ns);
return {
t: i18nInstance.getFixedT(lng, ns),
i18n: i18nInstance,
};
}
// app/[locale]/page.tsx (서버 컴포넌트 예시)
import { getTranslation } from '@/i18n'; // 위에서 정의한 getTranslation 함수 임포트
import ClientComponentWithTranslation from '@/components/ClientComponentWithTranslation'; // 클라이언트 컴포넌트
interface HomePageProps {
params: {
locale: string;
};
}
export default async function HomePage({ params: { locale } }: HomePageProps) {
const { t } = await getTranslation(locale, ['common', 'home']); // 'common'과 'home' 네임스페이스 로드
return (
<main style={{ padding: '40px', textAlign: 'center' }}>
<h1>{t('home:page_title')}</h1> {/* home 네임스페이스의 키 */}
<p>{t('welcome_message')}</p> {/* common 네임스페이스의 키 */}
<ClientComponentWithTranslation />
</main>
);
}
다국어 SEO 최적화
다국어 웹사이트의 SEO는 일반 웹사이트보다 더 복잡하지만, 올바르게 구현하면 검색 엔진에서 더 많은 트래픽을 유도할 수 있습니다.
hreflang
태그: 각 페이지의<head>
섹션에hreflang
메타 태그를 추가하여 검색 엔진에 동일한 콘텐츠의 다른 언어 버전을 알려줍니다. Next.js는next/head
(Page Router) 또는generateMetadata
(App Router)를 통해 이 태그를 추가할 수 있습니다.app/[locale]/layout.tsx // app/[locale]/layout.tsx 또는 page.tsx의 generateMetadata import { Metadata } from 'next'; export async function generateMetadata({ params: { locale } }: { params: { locale: string } }): Promise<Metadata> { const siteUrl = 'https://yourwebsite.com'; // 실제 웹사이트 URL return { title: '다국어 페이지 예시', alternates: { canonical: `${siteUrl}/${locale}/your-page`, // 현재 페이지의 표준 URL languages: { 'en-US': `${siteUrl}/en/your-page`, 'ko-KR': `${siteUrl}/ko/your-page`, 'ja-JP': `${siteUrl}/ja/your-page`, // x-default: 기본 언어를 명시 (선택 사항) 'x-default': `${siteUrl}/en/your-page`, }, }, }; }
- 사이트맵 (Sitemap): 각 언어 버전의 모든 URL을 포함하는 다국어 사이트맵을 생성합니다.
- 언어별 콘텐츠: 각 언어 버전에 맞는 고품질의 번역된 콘텐츠를 제공합니다. 기계 번역만으로는 부족할 수 있습니다.
- URL 구조: Next.js의 서브패스 라우팅 (
/en/about
,/ko/about
)은 SEO에 권장되는 URL 구조입니다. - Google Search Console: Google Search Console에 웹사이트를 등록하고, 언어 및 지역 타겟팅 설정을 확인합니다.
기타 고려사항
- 날짜, 시간, 숫자 형식:
Intl.DateTimeFormat
,Intl.NumberFormat
등 JavaScript의Intl
객체를 사용하여 로케일에 맞는 형식으로 표시합니다. - RTL (Right-to-Left) 언어 지원: 아랍어, 히브리어 등 오른쪽에서 왼쪽으로 쓰는 언어를 지원해야 한다면 CSS
direction: rtl
속성을 사용하고 레이아웃을 조정해야 합니다. - 폰트: 다국어 지원을 위해 필요한 모든 문자를 포함하는 폰트(예: Google Noto Sans)를 사용합니다.
- 이미지 및 미디어: 특정 문화권에 더 적합하거나 번역이 필요한 이미지나 비디오를 제공할 수 있습니다.
Next.js의 강력한 국제화 기능과 next-i18next
같은 라이브러리를 활용하면 다국어 웹 애플리케이션을 효율적으로 구축하고 전 세계 사용자에게 최적화된 경험을 제공할 수 있습니다.