개발자님 저희 사이트가 검색이 안돼요 - Next.js App Router에 i18next 다국어 적용하기 (1)

치와와견주·2024년 9월 7일
0

Next_I18n

목록 보기
1/1
post-thumbnail

안녕하세요~! 이번 글에서는 Next.js로 마이그레이션을 결정하게 된 계기와 그 방법에 대해 소개해드리려고 합니다.

해당 프로젝트는 회사에 대한 소개 위주의 글로벌 홈페이지로, 처음에는 Vue 3의 CSR(Client-Side Rendering) 방식을 사용해 빠르게 구축되었습니다. 당시 제한된 개발 기간으로 인해 Vue 3라는 프레임워크를 사용하게 되었습나더. Vue3 프레임워크는 빠른 개발을 가능하게 해줬고, 복잡한 기능이 없는 단순한 소개 사이트로는 충분하다고 생각했습니다.

그러나 사이트 운영이 지속되면서 CSR 방식의 몇 가지 한계가 눈에 띄기 시작했습니다:

첫째, 초기 로딩 속도 문제입니다. CSR 특성상 모든 JavaScript 파일이 클라이언트 측에서 실행되다 보니, 첫 화면이 로딩되는 데 시간이 걸렸습니다. 이로 인해 사용자가 첫 화면을 보기까지 기다려야 하는 시간이 길어졌고, 이탈률이 높아지는 문제를 겪게 되었습니다.

둘째, SEO 최적화의 어려움입니다. CSR 방식에서는 초기 HTML이 거의 비어있고, 모든 콘텐츠가 클라이언트 측에서 렌더링되기 때문에 검색 엔진이 페이지를 제대로 인덱싱하지 못했습니다. 그 결과, 검색 엔진 결과에서 우리 사이트의 노출도가 낮아지는 문제가 발생했습니다.

이런 문제들을 해결하기 위해 Next.js 14로의 마이그레이션을 결정했습니다. Next.js는 Server-Side Rendering(SSR)과 Static Site Generation(SSG)을 지원하여 초기 로딩 속도를 크게 개선할 수 있었고, SEO 친화적인 구조로 사이트가 검색 엔진에 더 잘 노출될 수 있도록 해줬습니다. 또한, Next.js 14의 최신 기능인 App Router와 i18next 통합을 통해 더욱 빠르고 유연한 다국어 지원이 가능해졌습니다.

이 글에서는 Next.js로의 전환 과정을 설명드리겠습니다.

1. i18next 라이브러리 사용

Next.js 14로의 전환에서 중요한 부분 중 하나는 다국어 지원이었습니다. 기존 Vue 3 프로젝트에서는 i18n이라는 다른 국제화 라이브러리를 사용했지만, Next.js로 전환하면서 i18next와 react-i18next를 사용하기로 했습니다. i18next는 Next.js의 App Router와 잘 맞물려 작동하며, 서버 사이드와 클라이언트 사이드 모두에서 안정적으로 다국어 처리를 지원해주기 때문에 선택하게 되었습니다.

$ pnpm install react-i18next i18next --save

2. 파일구조

project-root/
│
├── app/
│   ├── [locale]/
│   │   ├── page.tsx
│   │   └── layout.tsx
│   │
│   └── i18n/
│       ├── locales/
│       │   ├── en / common.json
│       │   └── ko / common.json
│       └── index.ts
│
└── package.json

1. 파일 설명

app/[locale]/page.tsx

이 파일은 각 언어별로 페이지를 구성하는 파일입니다. [locale]은 URL 경로에서 사용되는 언어 코드로, 예를 들어 en이나 ko가 될 수 있습니다. 이 구조 덕분에 URL 경로에 따라 자동으로 해당 언어 페이지를 렌더링할 수 있습니다.

app/i18n/locales/

언어별 번역 파일은 이곳에 위치합니다. 번역 파일은 각각의 언어와 관련된 모든 페이지와 레이아웃에서 사용되며, 다국어 지원의 핵심을 담당합니다.

왜 이런 구조를 선택했을까요?

  • 언어별 관리의 효율성: app/[locale]/ 구조를 통해 언어별로 페이지와 레이아웃을 분리함으로써, 각 언어에 맞는 디자인이나 콘텐츠를 쉽게 적용하고 관리할 수 있습니다.
  • SEO 및 사용자 경험 향상: URL 경로에 언어 코드를 포함시키는 구조로, 검색 엔진이 각 언어 페이지를 정확하게 인식할 수 있게 하여 SEO에 긍정적인 영향을 미칩니다.
  • 유지보수의 용이성: 언어별로 독립된 파일을 사용해 수정이 필요한 경우 해당 언어의 파일만 업데이트하면 되어 유지보수가 쉬워집니다.

component 폴더의 구조는 Atomic 디자인패턴을 도입했으나 이 글의 목적과는 맞지 않아 설명은 생략하겠습니다.

2. 동적 라우팅을 위한 설정

웹사이트를 다국어로 제공할 때 사용자 경험을 최적화하기 위해 가장 중요한 요소 중 하나는 사용자의 브라우저 언어를 감지하여 올바른 언어 페이지로 리디렉션하는 것입니다. Next.js 14와 App Router를 사용하여 기본 URL로 접근할 때 사용자의 브라우저 언어를 감지하고, 올바른 언어 페이지로 자동으로 리디렉션하는 방법에 대해서 설명하겠습니다.

원하는 동작 방식

저희 팀이 원하는 동작 방식은 다음과 같았습니다.
1. 기본 언어 설정: 기본 언어는 en(영어)로 설정합니다. 모든 페이지는 언어 코드가 포함된 URL로 접근하게 됩니다.
예를 들어:

  • 영어 페이지: localhost:3000/en
  • 프랑스어 페이지: localhost:3000/fr
  1. 브라우저 언어 감지를 통한 리디렉션:
  • 사용자가 localhost:3000과 같이 언어 코드가 없는 기본 URL로 접근할 때, 서버 측 미들웨어가 실행되어 브라우저의 언어 설정을 감지합니다.
  • 감지된 언어가 fr이면 localhost:3000/fr로 리디렉션하고, 지원되지 않는 언어라면 기본 언어인 en으로 리디렉션합니다.

2-1) i18nConfig 파일 생성

// app/i18n/i18nConfig.ts
// 지원하는 언어 코드 타입 정의
export type Locale = "en" | "fr" | "th"; // 지원하는 언어 코드 목록

// 설정 타입 정의
interface I18nConfig {
  locales: Locale[]; // 지원하는 언어 목록
  defaultLocale: Locale; // 기본 언어 설정
}

// 설정 객체
const i18nConfig: I18nConfig = {
  locales: ["en", "fr", "th"], // Locale 타입만 허용됨
  defaultLocale: "en",
};

export default i18nConfig;

2-2 ) 미들웨어 설정에서 i18nConfig 사용하기

이제 이 i18nConfig를 미들웨어에서 사용하여 동적 라우팅을 처리하도록 설정을 수정합니다. i18nConfig를 import하여 지원 언어와 기본 언어를 간편하게 사용할 수 있게 됩니다.

// middleware.ts
import { NextRequest, NextResponse } from "next/server";
import i18nConfig, { Locale } from "./app/i18n/i18nConfig"; // i18nConfig 파일 import

export function middleware(request: NextRequest) {
  // 현재 URL 경로
  const { pathname } = request.nextUrl;

  // 이미 언어가 경로에 포함된 경우
  if (i18nConfig.locales.some((locale) => pathname.startsWith(`/${locale}`))) {
    return NextResponse.next();
  }

  // 쿠키에서 선호하는 언어를 가져옴
  const preferredLocale = request.cookies.get("preferredLocale")?.value;

  // 브라우저 언어를 감지 (쿠키에 값이 없을 때만 사용)
  const browserLanguage = request.headers
    .get("accept-language")
    ?.split(",")[0]
    .slice(0, 2);

  // 타입 가드를 통해 Locale 타입으로 변환
  const isLocale = (lang: string | undefined): lang is Locale =>
    lang !== undefined && i18nConfig.locales.includes(lang as Locale);

  // 쿠키 언어 > 브라우저 언어 > 기본 언어 순으로 결정
  const locale: Locale = isLocale(preferredLocale)
    ? preferredLocale
    : isLocale(browserLanguage)
    ? (browserLanguage as Locale)
    : i18nConfig.defaultLocale;

  // 언어에 맞는 경로로 리디렉션
  return NextResponse.redirect(new URL(`/${locale}${pathname}`, request.url));
}

export const config = {
  matcher: ["/((?!api|_next|static|.*\\..*|favicon.ico).*)"],
};

middleware에서 원하는 동작은 다음과 같았습니다.
1. 언어가 URL에 포함된 경우: 사용자가 이미 /en, /fr 등의 경로로 접근했을 때는 별도의 리디렉션 없이 해당 언어 페이지를 그대로 보여줍니다.
2. 언어가 URL에 포함되지 않은 경우: localhost:3000과 같이 언어 코드가 없는 URL로 접근했을 때:

  • 우선, 쿠키에서 사용자가 설정한 언어(preferredLocale)가 있는지 확인합니다.
  • 쿠키에 언어가 없을 경우, 브라우저의 Accept-Language 헤더에서 언어를 감지합니다.
  • 두 방법 모두 실패하면 기본 언어인 en으로 설정합니다.

왜 이렇게 구현했는가?

  1. 사용자 경험 개선: 사용자 설정을 우선시하고, 사용자가 언어를 변경하지 않은 경우에도 적절한 언어로 페이지를 제공하여 일관된 경험을 유지합니다.
  2. 유연한 언어 감지: 쿠키와 브라우저 설정을 유연하게 사용하여 다양한 사용자의 환경에 대응할 수 있도록 설계되었습니다.
  3. SEO 최적화: 각 언어가 고유한 URL을 가지므로, 검색 엔진이 각 언어 페이지를 정확하게 인덱싱할 수 있어 SEO에 긍정적인 영향을 줍니다.

이번 포스팅에서는 Next.js에서 미들웨어를 활용해 사용자의 언어를 감지하고 적절한 페이지로 리디렉션하는 방법을 다뤄봤습니다. 이를 통해 더 나은 사용자 경험을 제공하고, SEO 성능을 최적화할 수 있는 방법을 확인할 수 있었습니다.

다음 포스팅에서는 클라이언트 컴포넌트와 서버 컴포넌트에서 i18n을 적용하는 방법에 대해 설명하겠습니다. 특히, Next.js의 최신 기능을 활용하여 효율적으로 다국어를 처리하는 방법과 Context API를 이용해 글로벌 상태를 관리하는 방식에 대해 깊이 있게 다룰 예정입니다.

profile
건들면 물어요

0개의 댓글

관련 채용 정보