- Published on
- ·9 min read
Next.js - 다국어(i18n) 설정 및 static html로 내보내기 Part 1
개요
다국어가 지원되는 static html 웹 사이트를 만들기로 했습니다.
페이지는 단 2개로, 각 페이지는 영문과 한글을 지원하도록 할 계획이었습니다.
순수 html로 작성할까도 생각했지만, SEO 작업과 다국어 지원을 위해 일일이 눈으로 한국어와 영문 버전으로 작성하기에는 휴먼 에러가 발생할 듯 하여 Next.js를 사용하기로 했습니다.
내용이 길어져 Part 1, Part 2로 나누어 정리했습니다.
Next.js 신규 프로젝트 생성
아래 명령어로 신규 프로젝트를 생성합니다.
npx create-next-app@latest
프로젝트 생성 시 선택은 아래와 같이 했습니다.
√ What is your project named? ... sample-i18n-static-export
√ Would you like to use TypeScript? ... No
√ Would you like to use ESLint? ... Yes
√ Would you like to use Tailwind CSS? ... No
√ Would you like to use `src/` directory? ... No
√ Would you like to use App Router? (recommended) ... No
√ Would you like to customize the default import alias? ... No
TypeScript를 선택하지 않은 이유는 다국어 처리 참고 문서가 JavaScript로 설명되어있었기 때문입니다.(저는 TypeScript에 익숙하지 않기에 위험 부담을 최소화하기로 했습니다.)
App Router를 사용안함으로 지정한 이유는 다국어 처리 참고 문서가 pages router 기준으로 설명이 되어있었기 때문입니다.
Yes
선택: 기본 생성 파일이 app 폴더 기준으로 생성No
선택: 기본 생성 파일이 pages 폴더 기준으로 생성
더 자세한 내용은 Next.js Installation을 참고하세요.
브라우저에서 확인
생성한 프로젝트에서 아래 명령어를 실행합니다.
npm run dev
위 명령어를 실행하면 로컬 웹 서버가 실행되면서 접속할 url(http://localhost:3000
)을 알려줍니다.
(이미 사용중인 포트가 있다면 다른 포트로 변경될 수 있습니다. 저는 이미 3000 포트를 사용중이어서 3001 포트로 실행되었습니다.)
http://localhost:3000
에 접속해 페이지가 정상적으로 나오는지 확인합니다.
페이지 생성
pages/index.js
파일을 열어서 아래와 같이 수정합니다.
export default function Home() {
return (
<div>
<h1>첫 번째 화면</h1>
</div>
)
}
pages/second.js
파일을 생성하고 아래와 같이 작성합니다.
export default function Second() {
return (
<div>
<h1>두 번째 화면</h1>
</div>
)
}
브라우저에서 확인
http://localhost:3000
과 http://localhost:3000/second
에 접속해서 각 페이지가 정상적으로 나오는지 확인합니다.
여기까지가 기본 Next.js 프로젝트 생성과 페이지 생성입니다. 다음은 다국어 설정으로 넘어가보겠습니다.
다국어(i18n) 설정
기본 라우팅 기능
Next.js에는 i18n 다국어 라우팅 처리 기능이 내장되어 있습니다. 라우팅을 처리하는 것이지 언어 설정에 따른 번역 언어 매핑을 해주지는 않습니다.
아래와 같이 각 URL로 라우팅 처리를 해주는 것입니다.
<Link href={'/'} locale={'en'}>to English</Link> <!-- /en -->
<Link href={'/'} locale={'ko'}>한국어로</Link> <!-- /ko -->
언어별 URL로 접속했을 때, 각 언어에 맞는 번역을 해주기 위해 타사 라이브러리 next-i18next
를 사용하기로 했습니다.
기본 라우팅 기능에 대한 자세한 내용은 Next.js Internationalization (i18n) Routing을 참고하세요.
이 단계에서는 기본 라우팅 제공 기능이 있다 정도로 넘어가고, 바로 이어지는 다음 단계(next-i18next 설정)에서 설정을 진행하겠습니다.
next-i18next 설치
next-i18next에서 설치 및 설정 방법을 확인할 수 있습니다.
위 문서대로 진행했던 부분을 정리하면 아래와 같습니다.
먼저, 아래 명령어를 입력하여 설치합니다.
npm install next-i18next react-i18next i18next
번역 파일 추가
public
폴더 하위에locales
폴더를 생성합니다.locales
폴더 하위에en
과ko
폴더를 생성합니다.public/locales/en/common.json
파일을 생성하고 아래와 같이 작성합니다.
{
"first": "First Page",
"second": "Second Page"
}
public/locales/ko/common.json
파일을 생성하고 아래와 같이 작성합니다.
{
"first": "첫 번째 화면",
"second": "두 번째 화면"
}
프로젝트 설정
- 최상위 폴더에
next-i18next.config.js
파일을 생성하고 원하는 언어 설정을 추가합니다.
(저는 아래 코드와 같이 기본 언어는 한국어로, 제공 언어는 한국어와 영어로 설정했습니다.)
/** @type {import('next-i18next').UserConfig} */
module.exports = {
i18n: {
defaultLocale: 'ko',
locales: ['ko', 'en']
}
}
next.config.js
파일을 열어서 i18n 설정을 추가합니다. (재실행이 필요합니다.)
/** @type {import('next').NextConfig} */
const { i18n } = require('./next-i18next.config')
const nextConfig = {
reactStrictMode: true,
i18n
}
module.exports = nextConfig
pages/_app.js
파일을 열어서 아래와 같이 수정합니다.
import { appWithTranslation } from 'next-i18next'
const MyApp = ({ Component, pageProps }) => (
<Component {...pageProps} />
)
export default appWithTranslation(MyApp)
페이지에 다국어 적용
설정은 끝났습니다. 이제 페이지만 수정하면 다국어로 사용할 수 있습니다.
pages/index.js
파일을 열어서 아래와 같이 수정합니다. (동작 확인을 위한Link
태그도 추가했습니다.)
import {useTranslation} from "next-i18next";
import {serverSideTranslations} from "next-i18next/serverSideTranslations";
import Link from "next/link";
// 이 부분이 빠지면 t('...') 동작 안함
export async function getStaticProps({locale}) {
return {
props: {
...(await serverSideTranslations(locale, ['common'])),
},
};
}
export default function Home() {
const {t} = useTranslation(['common']);
return (
<div>
<h1>
{t('first')}
</h1>
<div>
<p><Link href={'/second'}>{t('second')}</Link></p>
<p><Link href={'/'} locale={'en'}>to English</Link> | <Link href={'/'} locale={'ko'}>한국어로</Link></p>
</div>
</div>
)
}
pages/second.js
파일을 열어서 아래와 같이 수정합니다.
import {useTranslation} from "next-i18next";
import {serverSideTranslations} from "next-i18next/serverSideTranslations";
import Link from "next/link";
// 이 부분이 빠지면 t('...') 동작 안함
export async function getStaticProps({locale}) {
return {
props: {
...(await serverSideTranslations(locale, ['common'])),
},
};
}
export default function Second() {
const {t} = useTranslation(['common']);
return (
<div>
<h1>
{t('second')}
</h1>
<div>
<p><Link href={'/'}>{t('first')}</Link></p>
<p><Link href={'/second'} locale={'en'}>to English</Link> | <Link href={'/second'} locale={'ko'}>한국어로</Link></p>
</div>
</div>
)
}
브라우저에서 확인
http://localhost:3000
과http://localhost:3000/en
에 접속해서 설정된 언어로 정상적으로 나오는지 확인합니다.http://localhost:3000/second
와http://localhost:3000/en/second
에 접속해서 설정된 언어로 정상적으로 나오는지 확인합니다.
아래 이미지는 브라우저에서 확인한 캡쳐 화면입니다. (참고로 저는 다른 프로젝트에서 3000 포트를 사용중이라 3001 포트로 접속했습니다.)
정리
Vercel, Netlify 또는 Docker를 사용하여 배포한다면 여기까지 설정해도 되는 것으로 보입니다.
하지만 저는 CloudFlare Pages에 배포할 예정입니다. 그렇다면 static export로 html 결과물을 생성해야 합니다.
Static Export
next.config.js 설정
next.config.js
파일을 열어서output: 'export'
를 추가합니다.
const { i18n } = require('./next-i18next.config')
const nextConfig = {
output: 'export', // 추가
reactStrictMode: true,
i18n
}
module.exports = nextConfig
Export 를 위해 다음 명령어를 실행합니다.
npm run build
아래와 같은 오류가 발생합니다.
> Build error occurred
Error: Specified "i18n" cannot be used with "output: export". See more info here: https://nextjs.org/docs/messages/export-no-i18n
Next.js - Internationalization (i18n) Routing 문서의 내용 중 아래와 같은 부분을 확인할 수 있습니다.
(이 글을 작성하는 2023.09.19 기준 내용이고 이후 변경될 수도 있습니다.)
Note that Internationalized Routing does not integrate with output: 'export' as it does not leverage the Next.js routing layer. Hybrid Next.js applications that do not use output: 'export' are fully supported.
해석해 보면
Internationalized Routing은 output: 'export'와 통합되지 않습니다. Next.js 라우팅 레이어를 활용하지 않기 때문입니다. output: 'export'를 사용하지 않는 하이브리드 Next.js 애플리케이션에는 완벽하게 지원됩니다.
해결 방법?
다행히 next-i18next - Static HTML Export SSG 부분에 해결 방법 링크가 안내되어있습니다.
next-language-detector
라이브러리 설치, 설정 및 소스 추가/수정이 필요합니다.
이제 하나씩 진행해 보겠습니다.
하지만, 내용이 이미 길어졌기에 Part 2로 이어서 작성하겠습니다.