i18next를 통해 다국어 지원 처리하기

hannah·2025년 10월 24일

React

목록 보기
11/13

몇년 전에 다국어 채팅 서비스 스타트업에 재직할 때 어영부영 사용해보았던 i18next를 사이드 프로젝트를 진행하던 중에 최근 다시 구현하며 학습한 내용을 정리해보고자 한다.

당시에는 사수 개발자분이 세팅해둔 환경에서 단순히 JSON 번역 파일에 텍스트만 추가하는 식으로 작업했었다. 그래서 정작 초기 설정은 어떻게 하는지, useTranslation 훅이 내부적으로 어떻게 동작하는지 깊게 고민해볼 기회가 없었다. 이번에 세팅부터 직접 구현해보면서 근본적인 원리나 사용법에 대해 깨달은 점들이 많았다.

❓ 왜 i18next인가?

처음엔 "그냥 전역 상태 관리에 언어 변수 하나 두고, if (lang === 'en') 처럼 분기 처리하면 안 되나?"라는 생각을 했었다. 하지만 서비스 규모가 커지면 이런 방식은 지옥이 된다는 것을 일찌감치 깨달았다.

이유는 다음과 같다.

  • 복수형 처리: 영어는 단수/복수(apple/apples)가 있지만 한국어는 모호하다. 언어마다 다른 문법 규칙을 하드코딩으로 해결할 수 없다.
  • 보간: "Hello, {{name}}!" 처럼 문장 중간에 변수가 들어갈 때, 언어마다 어순이 다르면 단순 문자열 합치기로는 대응이 불가능하다.
  • 파일 관리: 수천 개의 텍스트를 컴포넌트 파일 안에 두면 코드가 지저분해진다.

i18next는 자바스크립트 생태계에서 가장 강력하고 검증된 국제화(i18n) 프레임워크이기 때문에 위와 같은 문제들을 해결할 수 있다.

📌 설치 및 기본 세팅

우선 필요한 라이브러리들을 설치해야 한다. react-i18next는 i18next를 리액트에서 편하게 쓸 수 있게 해주는 래퍼(Wrapper) 라이브러리다.

# npm
npm install i18next react-i18next i18next-browser-languagedetector

# yarn
yarn add i18next react-i18next i18next-browser-languagedetector
  • i18next: 다국어 처리의 핵심 로직.
  • react-i18next: 리액트 컴포넌트와 훅 지원.
  • i18next-browser-languagedetector: 사용자의 브라우저 언어 설정을 감지해서 자동으로 초기 언어를 세팅해준다. (이게 없으면 무조건 기본 언어로만 뜬다!)

초기화 설정 (i18n/index.ts)

프로젝트 루트나 src 폴더 하위에 i18n 폴더를 만들고 설정 파일을 작성한다. 나의 경우, 보통 src/i18n/index.ts에 둔다.

import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';

// 번역 파일 import (후에는 서버에서 받아오는 방식으로 변경 가능함)
import ko from './locales/ko.json';
import en from './locales/en.json';

i18n
 .use(LanguageDetector) // 사용자 언어 감지
 .use(initReactI18next) // 리액트와 연결
 .init({
    resources: {
      ko: { translation: ko },
      en: { translation: en },
    },
    fallbackLng: 'en',
    debug: true, // (디버깅용)
    
    interpolation: {
      escapeValue: false, // 리액트는 XSS 보호가 기본이라 false로 설정해야 함!
    },
  });

export default i18n;

여기서 escapeValue: false 부분이 중요하다. i18next는 기본적으로 XSS 공격 방지를 위해 HTML 태그를 이스케이프 처리하는데, 리액트는 렌더링 시점에 이미 이를 처리해주므로 이중으로 처리되지 않도록 꺼줘야 한다. 이걸 몰라서 초반에 <br/> 태그가 화면에 글자 그대로 출력되는 뼈아픈 기억이 있다..ㅎ

🧩 번역 파일(JSON) 관리하기

이제 실제 번역할 텍스트들을 JSON 파일로 관리한다.

src/i18n/locales/ko.json

{
  "welcome": "환영합니다!",
  "description": "이것은 다국어 테스트입니다.",
  "greeting": "안녕하세요, {{name}}님"
}

src/i18n/locales/en.json

{
  "welcome": "Welcome!",
  "description": "This is a multilingual test.",
  "greeting": "Hello, {{name}}"
}

Tip: Key 값인 "welcome", "description" 등은 개발자가 코드에서 사용하는 식별자이므로 의미를 명확하게 짓는 것이 좋다.

🤸 실전 적용하기

import React from 'react';
import { useTranslation } from 'react-i18next';

const MainPage = () => {
  const { t, i18n } = useTranslation();

  const changeLanguage = (lang: string) => {
    i18n.changeLanguage(lang); // 언어 변경 함수
  };

  return (
    <div>
      <h1>{t('welcome')}</h1>
      <p>{t('description')}</p>
      
      {/* 동적 데이터 바인딩 */}
      <p>{t('greeting', { name: '철수' })}</p>

      <button onClick={() => changeLanguage('ko')}>한국어</button>
      <button onClick={() => changeLanguage('en')}>English</button>
    </div>
  );
};

export default MainPage;

위와 같이 구현하여 코드를 실행하고 버튼을 누르면 페이지 새로고침 없이 텍스트가 '샤라락' 바뀐다. 전역 상태 관리를 복잡하게 설정하지 않아도 i18n.changeLanguage() 하나로 앱 전체의 언어가 즉시 반응형으로 바뀌는 건 볼 때마다 짜릿하다.

🤖 알게된 것들...

1) 구조의 중요성
처음엔 단순히 파일을 하나로 관리했는데, 페이지가 많아지니 JSON 파일이 1000줄이 넘어가기 시작했다. common.json, auth.json, mypage.json 처럼 네임스페이스(Namespace)를 나눠서 관리하는 것이 유지보수에 훨씬 유리하다는 것을 배웠다.

2) 개발자와 번역가의 협업
다국어 처리는 코드만 짠다고 끝나는 게 아니었다. "Key를 어떻게 지을 것인가?"가 생각보다 중요했다. btn_ok라고 지으면 나중에 "확인"인지 "승인"인지 헷갈린다. 문맥을 알 수 있게 modal_confirm_btn처럼 구체적으로 짓는 습관을 들여야겠다.

3) 툴의 도움
VS Code 익스텐션 중에 i18n Ally라는 것이 있는데, 이걸 쓰면 코드 상에서 t('welcome') 옆에 실제 번역된 텍스트를 미리 보여준다. 혹시 i18next를 쓴다면 이 익스텐션을 사용하는 것을 추천한다!! 생산성이 2배는 오른다^^

0개의 댓글