웹사이트 번역

Next.js의 앱 라우터 및 페이지 라우터 국제화

Next.js의 앱 라우터 및 페이지 라우터 국제화
Rayne Aguilar
작성자
Rayne Aguilar
Elizabeth Pokorny
검토자
Elizabeth Pokorny
업데이트 날짜
2026년 6월 12일

Next.js에는 완전히 다른 시스템을 설명하는 두 개의 공식 i18n 문서 페이지가 있으며, 이 두 페이지 간에는 상호 참조가 전혀 없습니다.

하나는 페이지 라우터를 다루고, 다른 하나는 앱 라우터를 다룹니다. 두 문서 모두 이 둘의 관계, 어떤 라이브러리를 사용해야 하는지, 또는 다국어 SEO를 위해 hreflang을 어떻게 처리해야 하는지에 대해서는 설명하지 않습니다.

두 라우터를 서로 연결할 예정입니다. 라우팅 전략, 라이브러리 선택, 미들웨어 설정, 서버 측 번역, 그리고 다국어 SEO에 대해 살펴보겠습니다.

저희의 목표는 Next.js 앱에 언어 지원을 추가하라는 요청을 받았지만, 구체적인 방안을 결정하기 전에 명확한 개요가 필요한 개발자들을 위한 유용한 자료를 제공하는 것입니다. 그럼 시작해 보겠습니다!

Next.js가 제공하는 국제화 기능

국제화는 세 가지 단계로 구성됩니다:

  • 라우팅은 각 로케일에 고유한 URL을 할당합니다.
  • 올바른 문자열을 불러오는 번역 기능.
  • 날짜, 숫자 및 통화 표시 형식.

Next.js는 라우팅만 지원합니다.

Pages 라우터는 v10.0.0부터 내장된 i18n 라우팅 기능을 지원합니다. next.config.js에 있는 세 개의 필드로 구성된 설정 블록이 로케일 감지, URL 접두사 추가 및 리디렉션을 자동으로 처리합니다.

앱 라우터는 다릅니다. 이 라우터의 i18n 문서 페이지에는 미들웨어를 사용하여 직접 구성하는 패턴이 설명되어 있습니다. [지역 설정] 동적 세그먼트와 라이브러리.

번역 및 서식 설정은 어떤 라우터를 사용하든 직접 처리해야 합니다. App Router 문서에는 호환되는 모든 라이브러리가 나열되어 있지만, 어떤 것을 선택해야 할지에 대한 안내는 제공하지 않습니다. 두 문서 페이지 모두 hreflang 태그나 다국어 사이트맵에 대해서는 다루지 않습니다.

Pages 라우터와 App 라우터 간의 i18n 차이점

Pages Router 방식은 구성 변경을 필요로 합니다. next.config.js 파일에 다음 세 가지 필드가 포함된 i18n 블록을 추가하세요:

// next.config.js
module.exports = {  
  i18n: {    
    locales: ['en', 'fr', 'de'],    
    defaultLocale: 'en',  
  },
}

그게 다예요.

Next.js는 자동으로 로케일을 감지합니다. Accept-Language 헤더, 다음을 준수합니다 다음 지역 쿠키 재정의 기능을 제공하며, 다음을 통해 현재 로케일을 노출합니다. useRouter().locale. 별도의 미들웨어나 디렉터리 구조 변경이 필요하지 않습니다.

앱 라우터에는 이에 상응하는 구성 옵션이 없습니다. 다음 세 가지 요소를 직접 조합하여 라우팅을 구성해야 합니다:

  • proxy.ts, 이전 명칭은 middleware.ts, 로케일 감지 및 리디렉션을 처리합니다. 이 기능은 @formatjs/intl-localematcher 그리고 협상가 구문 분석하기 Accept-Language 헤더. 공식 문서에서는 실제 감지 로직을 생략 부호로 처리하고 있습니다: function getLocale(request) { ... }. 그 내용은 다음 섹션에서 다루겠습니다.
  • [지역 설정] 동적 세그먼트는 전체를 감싸며 앱/ 디렉터리. 폴더 구조를 통해 모든 경로가 지역 설정을 반영하게 됩니다.
  • 정적 매개변수 생성 빌드 시점에 각 로케일 변형을 미리 렌더링합니다.

이 세 가지 요소 중 generateStaticParams는 빌드 시점에 Pages Router가 자동으로 처리하던 기능을 대체하는 부분입니다.

export function generateStaticParams() {  
	return [{ locale: 'en' }, { locale: 'fr' }, { locale: 'de' }]
}

🚨 Pages Router의 내장 i18n 기능은 다음에서 작동하지 않습니다. 출력: 'export'. 완전히 정적인 내보내기가 필요한 경우, 대신 수동 폴더 기반 라우팅을 사용해야 합니다. 이 제한 사항은 내장된 구성에만 적용됩니다. 두 라우터 모두 수동 설정을 통해 정적 내보내기를 지원합니다.

URL 변경 없이 서브 경로 라우팅, 도메인 라우팅 및 국제화(i18n) 구현

라이브러리를 선택하기 전에 라우팅 전략을 먼저 정하세요. 이는 SEO, DNS 설정, 그리고 작성해야 할 미들웨어의 양에 영향을 미칩니다:

  • 서브 경로 라우팅은 URL 경로에 지역 설정을 포함하는 방식입니다(예: /fr/products 또는 /de/products). 이는 대외용 사이트의 기본 설정입니다. 검색 엔진은 각 지역 설정을 별개의 페이지로 색인화하며, 이는 다국어 SEO에 있어 바람직한 방식입니다.
  • 도메인 라우팅은 지역 정보를 example.fr이나 de.example.com과 같은 별도의 도메인이나 하위 도메인에 매핑합니다. 이는 국가 코드 최상위 도메인(ccTLD)을 통해 더 강력한 지역 타겟팅 신호를 전달하지만, DNS 구성이 필요하고 로컬 개발 과정을 복잡하게 만듭니다.
  • 경로 설정 없음 모든 지역 설정을 동일한 URL에 통합하고, 프로필에 저장된 쿠키나 사용자 기본 설정에서 지역 설정을 확인합니다. 검색 엔진은 하나의 URL만 인식하므로 지역별 변형을 별도로 색인화할 수 없습니다. 이는 SEO가 필수 요건이 아닌 인증 기반 SaaS 앱이나 내부 도구에 적합한 방식입니다. next-intl 이를 주요 선택지로 적극 권장합니다.

SEO가 필수적인 공개 사이트의 경우, 서브 경로 라우팅이 적절한 기본 설정입니다. 도메인 라우팅은 강력한 지역 타겟팅 신호가 반드시 필요한 경우에만 그 복잡성을 감수할 가치가 있습니다.

URL 구조를 수동으로 관리하고 싶지 않다면, Weglot 리버스 프록시가 하위 디렉터리 및 하위 도메인 라우팅을 자동으로 처리해 주며, 여기에는 hreflang 삽입 및 번역된 URL도 포함됩니다.

next-intl vs. react-i18next vs. Lingui

앞서 언급했듯이, 공식 문서에는 특정 라이브러리를 추천하지 않은 채 목록만 제시되어 있습니다. 주요 옵션들을 비교해 보면 다음과 같습니다:

기능 next-intl react-i18next 링기
RSC 지원 원어민 비동기 초기화를 통해 매크로를 통해
SEO/hreflang 메타데이터 API를 통한 수동 방식 메타데이터 API를 통한 수동 방식 메타데이터 API를 통한 수동 방식
경로 설정 통합형 next-i18n-router를 통해 매뉴얼
TypeScript 엄격한 타입 지정 플러그인을 통해 추출을 통해
서식 지정 useFormatter를 통해 Wraps Intl API를 감싸줍니다 FormatJS 참가자 매뉴얼

next-intl은 App Router의 사실상의 표준입니다. RSC 지원이 내장되어 있고, 라우팅 기능이 통합되어 있으며, 별도의 설정 없이도 TypeScript 자동 완성 기능을 사용할 수 있습니다.

Pages Router를 사용 중이거나 스택의 다른 부분에서 이미 i18next를 사용하고 있다면 react-i18next가 더 적합합니다. App Router도 지원되지만, 수동으로 설정해야 할 부분이 더 많습니다.

Lingui는 기존 .po 파일 파이프라인을 갖춘 팀에 적합합니다. 번역 내용은 빌드 시점에 추출되므로, 사용되지 않는 메시지는 절대 배포되지 않습니다.

💡 Paraglide JS는 주요 경쟁자들에 비하면 그다지 주목받지 못하지만, 컴파일러 기반의 대안으로서 더 작은 번들 크기를 제공한다는 점에서 주목할 만합니다. 다만, 이 도구에 관심이 있다면 다른 도구들에 비해 생태계가 아직 미성숙하다는 점을 감수해야 합니다.

날짜 및 숫자 서식 지정을 위해 next-intl은 useFormatter를 통해 네이티브 Intl API를 감싸서 사용합니다. 다른 라이브러리들은 FormatJS에 위임하거나, 사용자가 직접 Intl.DateTimeFormat 및 Intl.NumberFormat을 호출하도록 합니다.

💡 이러한 라이브러리는 렌더링을 담당합니다. 번역 내용을 직접 작성하고 콘텐츠가 변경될 때마다 이를 최신 상태로 유지해야 하는 작업은 여전히 필요합니다. Weglot 코드베이스 외부의 리버스 프록시를 통해 번역 콘텐츠를 관리하므로, 완전히 다른 수준에서 Weglot . 따라서 이를 단순한 라이브러리 대체품으로 생각해서는 안 됩니다.

미들웨어 설정, 로케일 감지 및 서버 구성 요소 번역

Next.js 16에서는 middleware.ts 의 이름을 proxy.ts로 변경했습니다. 이는 “middleware”라는 용어가 오해를 불러일으키고, 일반적인 애플리케이션 내 로직을 연상시키기 때문입니다. 실제로 이 파일은 네트워크 경계에서 실행되며, 요청이 애플리케이션에 도달하기 전에 프록시처럼 요청을 가로채고 수정합니다.

다음은 공식 문서의 앞서 언급된 생략된 부분을 보완하는, 로케일 감지 및 리디렉션 구현의 전체 코드입니다:

// proxy.ts
import { match } from '@formatjs/intl-localematcher'
import Negotiator from 'negotiator'
import { NextRequest, NextResponse } from 'next/server'

const locales = ['en', 'fr', 'de']
const defaultLocale = 'en'

function getLocale(request: NextRequest): string {
  const cookie = request.cookies.get('NEXT_LOCALE')?.value
  if (cookie && locales.includes(cookie)) return cookie

  const headers = { 'accept-language': request.headers.get('accept-language') ?? '' }
  const languages = new Negotiator({ headers }).languages()
  return match(languages, locales, defaultLocale)
}

export function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl
  const hasLocale = locales.some(l => pathname.startsWith(`/${l}/`) || pathname === `/${l}`)
  if (hasLocale) return

  const locale = getLocale(request)
  request.nextUrl.pathname = `/${locale}${pathname}`
  return NextResponse.redirect(request.nextUrl)
}

파일은 다음 아래에 중첩됩니다 app/[지역 설정]/, 그리고 사전/ 레이아웃과 페이지와 함께 폴더를 생성합니다. 루트 레이아웃은 지역 설정 매개변수로 전달하여 html 태그:

export default async function RootLayout({ children, params }) {
  const { locale } = await params

  return (
    <html lang={locale}>
      <body>{children}</body>
    </html>
  )
}

번역과 관련하여, 공식 문서에는 별도의 라이브러리 없이 사용할 수 있는 기본 사전 패턴이 소개되어 있습니다:

const dictionaries = {
  en: () => import('./dictionaries/en.json').then(m => m.default),
  fr: () => import('./dictionaries/fr.json').then(m => m.default),
}

export const getDictionary = async (locale: string) => dictionaries[locale]()

이 방법은 작동하지만 복수형 변환이나 보간 기능을 지원하지 않습니다. next-intl 두 가지 모두를 다룹니다 getTranslations() 그리고 useTranslations() 에서 서버 구성 요소와 클라이언트 구성 요소 각각.

어느 쪽이든 번역 파일은 서버에서 처리되며, 최종 HTML 파일만 브라우저로 전송되므로 메시지 파일의 크기는 클라이언트 번들 크기에 영향을 미치지 않습니다.

클라이언트 컴포넌트의 경우, 가능한 한 서버 컴포넌트 부모 컴포넌트에서 번역된 문자열을 props로 전달하십시오. 다음을 사용하십시오 NextIntlClientProvider 클라이언트 컴포넌트가 번역을 직접 처리해야 하는 경우에만 스코프가 지정된 메시지 하위 집합을 사용합니다.

언어 전환 기능의 경우, 서버 컴포넌트가 로케일 라벨을 렌더링하고 클라이언트 컴포넌트는 useRouter() 호출:

// LocaleSwitcher.tsx (Server Component)
import LocaleSwitcherClient from './LocaleSwitcherClient'

export default function LocaleSwitcher({ locale }) {
  return <LocaleSwitcherClient locale={locale} labels={{ en: 'English', fr: 'Français' }} />
}

클라이언트는 선택 사항의 변경을 감지하면 새로운 로케일 경로를 전송합니다:

// LocaleSwitcherClient.tsx
'use client'
import { useRouter } from 'next/navigation'

export default function LocaleSwitcherClient({ locale, labels }) {
  const router = useRouter()
  return (
    <select value={locale} onChange={e => router.push(`/${e.target.value}`)}>
      {Object.entries(labels).map(([value, label]) => (
        <option key={value} value={value}>{label}</option>
      ))}
    </select>
  )
}

Weglot 사용 중이라면 이 내용은 모두 해당되지 않습니다. Weglot 언어 전환 위젯이 기본으로 Weglot 프록시 설정이나 미들웨어가 필요하지 않습니다.

Hreflang, 사이트맵 및 메타데이터를 활용한 다국어 SEO

두 라우터 모두 다국어 SEO를 자동으로 처리하지 않습니다:

  • Pages 라우터는 html lang 정확하게 구현되었지만, 관련 문서에는 hreflang 구현이 “사용자 재량에 달려 있다”고 명시되어 있습니다.
  • 앱 라우터에는 hreflang에 대한 언급이 전혀 없습니다.

App Router를 사용하면 메타데이터 API를 통해 자동화에 가장 가까이 다가갈 수 있습니다. 로케일 URL을 메타데이터 생성 ~을 통해 대체 언어 객체와 Next.js의 자동 주입 link rel="alternate" hreflang="..." 태그:

export async function generateMetadata({ params: { locale } }) {
  return {
    alternates: {
      languages: {
        'en': 'https://example.com/en',
        'fr': 'https://example.com/fr',
        'de': 'https://example.com/de',
      },
    },
  }
}

가장 수작업이 많이 필요한 부분은 페이지별로 각 로케일 변형에 맞는 올바른 URL을 수집하는 것입니다. 이 로직은 직접 작성하셔야 합니다.

사용하는 라우터와 관계없이 수동으로 설정해야 할 두 가지 사항이 더 있습니다. 중복 콘텐츠 신호를 방지하기 위해 로케일별로 표준 URL을 설정해야 합니다. 귀하의 sitemap.ts 필요 사항 xhtml:link 모든 페이지의 모든 로케일 변형에 대한 항목.

~에 hreflang 값 자체, 사용 hreflang="en-US" 특정 지역을 대상으로 할 때, 그리고 hreflang="en" 모든 영어 사용자를 대상으로 할 때. 이 두 가지를 혼동하면 검색 엔진에 모순된 신호를 보내게 됩니다.

next-intl 부분적인 자동화 기능을 하나 추가합니다: 프록시가 hreflang을 자동으로 삽입합니다 링크 지역화된 경로명 라우팅을 사용할 때의 응답 헤더. 그 외의 라우팅 구성은 여전히 수동으로 구현해야 합니다.

사이트가 성장함에 따라 지속적인 유지 관리에 상당한 노력이 필요하게 됩니다. 다행히 Weglot 리버스 프록시가 서버 측에서 모든 작업을 자동으로 처리하므로, 별도의 코드 없이도 번역된 페이지가 검색 엔진에 완전히 색인될 수 있습니다.

Weglot 다국어 SEO 접근 방식

사실, Weglot 리버스 프록시는 이전 섹션에서 다룬 모든 사항, 즉 hreflang 태그, 번역된 URL, 캐노니컬 태그, 사이트맵 생성을 자동으로 처리합니다. 번역된 페이지는 서버 측에서 제공되므로 검색 엔진이 완전히 색인화할 수 있습니다.

⚠️ Weglot 자바스크립트 snippet 기능은 SEO에 도움이 되지 않습니다. 클라이언트 측 번역은 검색 엔진 크롤링이 불가능합니다. SEO가 중요하다면 리버스 프록시 설정이 필요합니다.

또 다른 장점은 빌드 시간입니다. Weglot 빌드 시점에 번역된 페이지를 생성하는 대신 자체 CDN을 통해 Weglot , Weglot 사용하는 사이트에 10개 언어를 추가하더라도 빌드 시간에는 아무런 영향을 미치지 않습니다. 다음 빌드 소요되는 시간.

Next.js 프로젝트에 적합한 접근 방식 선택하기

올바른 선택은 무엇을 최적화하느냐에 달려 있습니다.

App Router를 기반으로 새로 시작하신다면, next-intl 이것이 기본 선택지입니다. 이 패키지는 라우팅, 번역, TypeScript 자동 완성, RSC 지원을 모두 포함하고 있습니다.

Pages Router를 사용 중이거나 이미 스택에 i18next가 포함되어 있다면, react-i18next 가장 수월한 길이다.

다국어 SEO와 지속적인 번역 관리가 최우선 과제라면, 두 라이브러리 모두 문제를 완전히 해결해 주지는 못합니다. 여전히 hreflang 태그와 사이트맵을 구현해야 하며, 콘텐츠가 변경될 때마다 번역 내용을 최신 상태로 유지하기 위한 워크플로우도 마련해야 합니다. Weglot 리버스 프록시는 이러한 모든 과정을 자동으로 처리해 줍니다.

Weglot 컴포넌트 수준 라이브러리는 서로 배타적이지 않습니다. Weglot HTML 및 프록시 계층에서 Weglot , 컴포넌트에서 사용하는 어떤 라이브러리와도 함께 사용할 수 있습니다.

해당 방식이 귀하의 상황에 적합하다면, Weglot 자바스크립트 통합 페이지를 참고하여 시작해 보시고 14일 무료 체험을 이용해 보세요!

방향 아이콘
Weglot을 만나보세요

번역플랫폼 Weglot을 통해 글로벌 트래픽을 높여보세요

첫 번째 언어 설정을 도와드립니다. 직접 사용해보며 어디까지 확장할지 결정해보세요. 지금 바로 번역 플랫폼 Weglot을 무료로 시작해보세요.

이 글에서는 다음 내용을 알아볼게요:
로켓 아이콘

시작할 준비 되셨나요?

Weglot 힘을 이해하는 가장 좋은 방법은 직접 확인해 보는 Weglot . 무료로, 아무런 의무 없이 테스트해 보세요.

아직 웹사이트를 연결할 준비가 되지 않았다면 대시보드에서 데모 웹사이트를 이용할 수 있습니다.

좋아할 만한 다른 글도 읽어보세요.

자주 묻는 질문 아이콘

자주 묻는 질문

찾는 항목이 없어요.

파란색 화살표

파란색 화살표

파란색 화살표