← 목록으로

프론트엔드에서 다국어 처리하기

YEJI · 2024. 11. 20. 22:43 · 2분
목차

어떤 방식으로 할지 논의

프로젝트에서 다국어(i18n) 설정이 필요한 상황이 생겼고, 백엔드와 협업하여 어떻게 처리할지를 논의했습니다.

두 가지 선택지

  1. 서버에서 현재 언어에 맞춰 텍스트 자체를 내려주는 방식
  2. 서버는 코드값만 주고, 프론트에서 언어 설정에 따라 번역 처리하는 방식

팀에서는 두 번째 방식을 채택했습니다. 이유는 아래에서 자세히 설명합니다.

서버는 "APPLE" 같은 코드값만 응답하고, 프론트는 클라이언트에 설정된 언어에 따라 해당 코드를 "사과" 혹은 "apple"로 매핑하는 구조입니다.

🤔 왜 첫 번째 방식이 어려운가?

1. 서버의 로직 복잡도 증가

  • 서버 API가 모든 응답을 언어에 따라 분기 처리해야 함
  • 각 필드별로 언어를 고려한 응답 템플릿 구성 필요
  • 테스트 및 유지보수가 훨씬 어려워짐

2. 언어 리소스 관리의 비효율

  • 백엔드에서 모든 다국어 리소스를 직접 보유/관리해야 함
  • 단순 UI 문구 변경도 서버 배포가 필요함 (프론트 민첩성 저하)

3. UX 및 캐싱 문제

  • 언어 변경 시 새 API 호출이 필요해짐 → 화면 깜빡임, UX 지연 발생
  • 프론트에서 번역을 처리하면 즉시 전환 가능 (클라이언트 주도 i18n의 장점)

우리가 채택한 방식: 코드 기반 다국어 매핑

서버는 **영어 코드값(또는 숫자 ID 등)**만 내려주고, 프론트는 해당 코드를 클라이언트 언어 상태에 따라 텍스트로 매핑합니다.

예시:

js
export const userRoleType = {
ADMIN: {
code: "ADMIN",
label_kr: "관리자",
label_en: "Administrator",
},
USER: {
code: "USER",
label_kr: "일반 사용자",
label_en: "User",
},
GUEST: {
code: "GUEST",
label_kr: "게스트",
label_en: "Guest",
},
MODERATOR: {
code: "MODERATOR",
label_kr: "중재자",
label_en: "Moderator",
},
};

프론트에서는 useLanguagesStore의 상태값을 통해 현재 언어를 가져오고, 아래와 같은 유틸 훅을 통해 매핑합니다:

js
const useCodeMap = () => {
const language = useLanguagesStore((state) => state.language);
 
const codeMapFn = useMemo(() => {
const codeToLabel = (code, e) => {
try {
return (
Object.values(e).find((v) => v.code === code)?.[
`label_${language.toLowerCase()}`
] ?? ""
);
} catch {
return "";
}
};
 
const labelToCode = (label, e) => {
try {
return (
Object.values(e).find(
(v) => v[`label_${language.toLowerCase()}`] === label
)?.code ?? ""
);
} catch {
return "";
}
};
 
const getList = (e) =>
Object.values(e).map((v) => v[`label_${language.toLowerCase()}`]);
 
const getCountryCodeList = (e) =>
Object.values(e).map(
(v) => `${v.code} (${v[`label_${language.toLowerCase()}`]})`
);
 
return {
getCode: labelToCode,
getLabel: codeToLabel,
getList,
getCountryCodeList,
};
}, [language]);
 
return codeMapFn;
};

이 구조 덕분에 서버에서는 언어 관련 로직이 완전히 제거되었고, 프론트는 빠르게 텍스트를 전환할 수 있게 되었습니다.

💡 장점 요약

항목설명
프론트 중심 i18n클라이언트에서만 언어 전환 처리 → 서버 로직 단순화
빠른 텍스트 변경배포 없이 언어 파일만 수정 가능
유연한 확장성언어 추가 시 서버 수정 없이 대응 가능
즉각 전환 가능React 상태 변경만으로 실시간 언어 변경 지원

개선할 수 있는 점

현재는 언어별 텍스트를 객체에 하드코딩하고 있는데, 아래처럼 개선할 수 있습니다.

개선 예시: i18next + JSON 리소스 방식

1. locales/kr/translation.json

json
{
"userRole": {
"ADMIN": "관리자",
"USER": "일반 사용자",
"GUEST": "게스트",
"MODERATOR": "중재자"
}
}

2. locales/en/translation.json

json
{
"userRole": {
"ADMIN": "Administrator",
"USER": "User",
"GUEST": "Guest",
"MODERATOR": "Moderator"
}
}

3. i18next 설정 (i18n.js)

js
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import en from "./locales/en/translation.json";
import kr from "./locales/kr/translation.json";
 
i18n.use(initReactI18next).init({
resources: {
en: { translation: en },
kr: { translation: kr },
},
lng: "kr",
fallbackLng: "en",
interpolation: { escapeValue: false },
});
 
export default i18n;

4. 실제 사용 코드

jsx
import { useTranslation } from "react-i18next";
 
const { t } = useTranslation();
const roleCode = "ADMIN";
 
return <div>{t(`userRole.${roleCode}`)}</div>; // => 관리자 / Administrator