i18n 다국어 적용하기
프로젝트를 하다 보면 i18n(다국어 처리)은 늘 고민거리였다.. 특히 이전에는 서버에서 모든 언어를 관리하던 환경이었기에 프론트에서 다국어 처리를 직접 해보지 않았던 나로서는 이번 경험이 새롭고도 재밋는 경험이었다.
Jul 23, 2025
Contents
서버가 아닌 프론트에서 다국어를 처리해야 한다면?프로젝트를 하다 보면 i18n(다국어 처리)은 늘 고민거리였다.. 특히 이전에는 서버에서 모든 언어를 관리하던 환경이었기에 프론트에서 다국어 처리를 직접 해보지 않았던 나로서는 이번 경험이 새롭고도 재밋는 경험이었다.
서버가 아닌 프론트에서 다국어를 처리해야 한다면?
이전 회사의 프로젝트에서는 서버에서 언어 처리를 일괄 관리 해왔었다. 하지만 현재 회사에선 백엔드 리소스 부족으로 인해 프론트에서 직접 다국어 처리를 맡게 되었고, 이로 인해 언어 데이터를 정리하고 관리하는 구조에 대해 고민이 필요했다 어떻게 하면 효율적으로 처리할 수 있을지에 대한 고민
유지보수의 어려움
기존 프로젝트에서
$t('변수명')
처럼 단순하게 사용하던 방식은 다음과 같은 문제를 안고 있었다- 어떤 JSON 파일에 해당 문구가 저장되어 있는지 알기 어렵다.
- 유지보수 시 하나하나 검색해가며 수정해야 한다.
- 전체적인 구조가 명확하지 않다.
이번에는 새로운 후임이 들어온 시점도 겹쳐, 아예 i18n 구조를 세분화하여 다음과 같이 설계했다.
지원 언어
한국어, 영어, 일본어, 독일어, 중국어 (번체, 간체)
번역은 GPT를 활용해 초기 세팅하고, 실제 서비스 반영 전에는 반드시 검수하는 프로세스를 거치기로 했습니다 사내에 ㅋㅋ 번역가가 없기에

구조는 i18n/language 폴더를 사용하였고 그아래 각각의 폴더를 사용하여 언어를 세분화시켰다.

import login from '@/i18n/language/de/login.json';
import workMapAdd from '@/i18n/language/de/work-map-add.json';
import workArea from '@/i18n/language/de/work-area.json';
import tool from '@/i18n/language/de/tool.json';
import image from '@/i18n/language/de/image.json';
import process from '@/i18n/language/de/process.json';
import product from '@/i18n/language/de/product.json';
import common from '@/i18n/language/de/common.json';
import { mergeModules } from '@/i18n/utils/mergeModules';
const merged = mergeModules([
login,
workMapAdd,
workArea,
tool,
image,
process,
product,
common,
]);
export default merged;
단순히 폴더를 분리하는 것 만으로는 관리가 편해지지 않기 때문에, 다음과 같이 유틸리티 함수를 만들어 각 JSON 파일을 통합하는 방식을 사용했다. 일단 모든 키가 중복되기 때문…
// src/i18n/utils/mergeModules.ts
type AnyObject = Record<string, any>;
/**
* 같은 최상위 키가 있으면 객체를 병합하고,
* 그렇지 않으면 그냥 덮어쓰도록한다.
*/
export function mergeModules(modules: AnyObject[]): AnyObject {
return modules.reduce((acc, mod) => {
Object.entries(mod)
.forEach(([key, val]) => {
// val이 객체라면(acc[key]도 객체면) 병합, 아니면 대체
if (
typeof val === 'object'
&& val !== null
&& !Array.isArray(val)
&& typeof acc[key] === 'object'
&& acc[key] !== null
) {
acc[key] = { ...acc[key],
...val };
} else {
acc[key] = val;
}
});
return acc;
}, {} as AnyObject);
}
- 가독성: 기능별로 나누고 키 네이밍을 명확하게 하니, 유지보수가 쉬워졌다. 일단 부사수의 아이디어로 동일하게 생각했던 부분에 table, required등으로 공통으로 사용할 수 있는 부분을 통합하기 위해 함수를짯고 그러면서 균일하게 사용할 수 있도록 하였다.
- 재사용성: 동일한 키(
"button.addImage"
,"table.imageName"
등)를 다른 화면에서도 재사용할 수 있도록 common으로 재사용성을 주었다.
- 자동화 용이: 언어 추가 시 기존 구조를 그대로 복사해서 번역만 교체하면 되므로, 로컬라이징 작업이 훨씬 수월하게 되었다.
{
"title": {
"workImageList": "Arbeitsbildliste"
},
"button": {
"addImage": "Arbeitsbild hinzufügen"
},
"modalTitle": {
"addImage": "Arbeitsbild hinzufügen",
"editImage": "Arbeitsbild bearbeiten"
},
"confirm": {
"addImage": "Möchten Sie das Arbeitsbild registrieren?",
"editImage": "Möchten Sie das Arbeitsbild bearbeiten?"
},
"infoText": {
"addImage": "Arbeitsbild wird hinzugefügt.",
"editImage": "Arbeitsbild wird bearbeitet."
},
"placeholder": {
"imageName": "Bildnamen eingeben"
},
"message": {
"noWorkImage": "Es wurden keine Arbeitsbilder registriert."
},
"table": {
"imageName": "Bildname",
"previewImage": "IMG",
"uploadImage": "Bild hochladen"
},
"required": {
"image": "Arbeitsbild ist ein Pflichtfeld."
},
"validation": {
"remarks": "Bitte geben Sie die Bemerkungen mit maximal 20 Zeichen ein."
}
}
다국어 처리(i18n)는 단순히 텍스트를 번역하는 문제를 넘어, 구조화와 유지보수 전략이 중요하다고 생각한다. 이런 식으로 구조를 세분화하고, 통합 로직을 갖추면 후임자도 쉽게 구조를 파악하고, 새로운 언어 추가나 번역 수정도 일관성 있게 작업할 수 있을거라 생각해서 의의를 두었다~

그리고 적극적으로 VSCODE 확장 익스텐션인 i18n Ally를 사용하기로 결정하였다. 이유는?
다국어 구조와 병합 전략을 세운 이후에도 여전히 불편한 점은 있었다…ㅠ 바로 번역 키 관리와 실제 문서와의 연결성이 떨어진다는 점 이점이 기존 유지보수중 가장 빡치고 힘들었던 점이었는데…
- 어디서 어떤 키를 쓰고 있는지 확인하기 어렵고,
- 존재하지 않는 키를 실수로 작성해도 즉각적인 피드백이 없으며,
- 새로 추가한 번역 키가 실제 적용되었는지 일일이 확인해야 하는 상황이 반복됐다.
그래서 선택한 도구: i18n Ally
이러한 번역 관리의 불편함을 줄이기 위해 우리는 VS Code 확장 도구인 i18n Ally를 적극적으로 활용하기로 결정했습니다.


1. 실시간 번역 키 미리보기
코드에서
$t('login.title')
처럼 사용된 키를 마우스 오버만으로도 해당 언어의 실제 번역값을 바로 확인할 수 있었다. 이점이 가장큰 장점으로 와닿았다.👉 유지보수 시 오타나 누락된 키를 빠르게 파악할 수 있는 큰 장점
2. 사용되지 않는 키, 누락된 키 자동 탐지
i18n Ally는 다음과 같은 유용한 기능을 제공한다고 도큐먼트에 써져있다.
- 사용하지 않는 키 감지
- 번역 누락 키 하이라이트
- 실시간 번역 파일 간 비교 (예:
ko
에는 있지만en
에는 없는 키)
덕분에 번역 누락 여부나 정리되지 않은 키를 손쉽게 점검할 수 있고 개발자 입장에도 추후 다가올 문제도 사전에 차단할 수 있다고 생각된다.

4. 새로운 키 생성 시 자동 삽입
i18n Ally를 통해 새로운 키를 생성하면 모든 언어 파일에 자동으로 키가 추가되며, 기본값을 설정하거나 빈 값으로 빠르게 초기화할 수 있어 생산성이 크게 향상됫다.
마지막으로 정리하며, 지인에게 물어본 다국어 번역에 대한 방향성은 이랬다.
“UX에 직접 보이는 문구는 프론트에서, 데이터와 함께 오는 문구는 백엔드에서.”
그리고 조직 규모와 도구 여건에 따라 하이브리드 방식으로 유연하게 설계하는 것이 가장 현실적인 접근이다 근데 우리는 현재 백엔드에서 지원이 힘든 상황이었고 프론트에선 도전해볼만한 요소라 진행했다.
Share article