---
createdAt: 2025-11-24
updatedAt: 2025-11-24
title: 컴파일러 기반 i18n 대 선언적 i18n
description: "매직" 컴파일러 기반 국제화와 명시적 선언적 콘텐츠 관리 간의 아키텍처적 트레이드오프 탐구.
keywords:
  - Intlayer
  - 국제화
  - 블로그
  - Next.js
  - JavaScript
  - React
  - i18n
  - 컴파일러
  - 선언적
slugs:
  - compiler-vs-declarative-i18n
  - blog
  - i18n
---

# 컴파일러 기반 i18n에 대한 찬반 논쟁

10년 이상 웹 애플리케이션을 개발해왔다면, 국제화(i18n)가 항상 마찰 지점이었음을 알 것입니다. 문자열 추출, JSON 파일 관리, 복수형 규칙 처리 등 아무도 하고 싶어 하지 않는 작업인 경우가 많습니다.

최근에 "컴파일러 기반" i18n 도구의 새로운 물결이 등장하여 이 고통을 사라지게 하겠다고 약속하고 있습니다. 그 제안은 매력적입니다: **컴포넌트에 텍스트만 작성하면 빌드 도구가 나머지를 처리해줍니다.** 키도, 임포트도 없이, 그저 마법처럼요.

하지만 소프트웨어 공학의 모든 추상화가 그렇듯, 마법에는 대가가 따릅니다.

이 블로그 글에서는 선언적 라이브러리에서 컴파일러 기반 접근법으로의 전환, 그들이 도입하는 숨겨진 아키텍처 부채, 그리고 왜 "지루한" 방식이 여전히 전문 애플리케이션에 가장 좋은 방법일 수 있는지 탐구할 것입니다.

## 번역의 간략한 역사

우리가 현재 어디에 있는지 이해하려면, 우리가 어디서 시작했는지 돌아봐야 합니다.

2011년에서 2012년경, JavaScript 환경은 지금과 매우 달랐습니다. 우리가 알고 있는 번들러들(Webpack, Vite)은 존재하지 않았거나 초기 단계에 불과했습니다. 우리는 브라우저에서 스크립트를 붙여서 사용하고 있었습니다. 이 시기에 **i18next**와 같은 라이브러리가 탄생했습니다.

이들은 당시 가능한 유일한 방법으로 문제를 해결했습니다: **런타임 사전(Runtime Dictionaries)**. 거대한 JSON 객체를 메모리에 로드하고, 함수가 실행 중에 키를 찾아내는 방식이었습니다. 이는 신뢰할 수 있고 명확하며 어디서나 작동했습니다.

오늘날로 빠르게 넘어가 보면, 우리는 밀리초 단위로 추상 구문 트리(AST)를 파싱할 수 있는 강력한 컴파일러들(SWC, Rust 기반 번들러)을 갖추고 있습니다. 이 강력한 기술은 새로운 아이디어를 탄생시켰습니다: _왜 우리가 수동으로 키를 관리해야 할까? 컴파일러가 "Hello World"라는 텍스트를 보고 바로 교체해주면 안 될까?_

이렇게 해서 컴파일러 기반 i18n이 탄생했습니다.

## 컴파일러의 매력 (일명 "마법" 접근법)

이 새로운 접근법이 유행하는 데는 이유가 있습니다. 개발자 입장에서 경험이 놀랍도록 좋기 때문입니다.

### 1. 속도와 "흐름"

작업에 몰입해 있을 때 변수 이름(`home_hero_title_v2`)을 생각하느라 멈추면 흐름이 끊깁니다. 컴파일러 접근법에서는 `<p>Welcome back</p>`를 입력하고 계속 진행할 수 있습니다. 마찰이 전혀 없습니다.

### 2. 레거시 구조 구출 작전

5,000개의 컴포넌트가 있지만 번역이 전혀 없는 거대한 코드베이스를 상속받았다고 상상해 보세요. 수동 키 기반 시스템으로 이를 개조하는 것은 몇 달이 걸리는 악몽 같은 작업입니다. 컴파일러 기반 도구는 구출 전략으로 작동하여, 단 한 파일도 수동으로 건드리지 않고 수천 개의 문자열을 즉시 추출합니다.

### 3. AI 시대

이것은 우리가 간과해서는 안 될 현대적인 이점입니다. AI 코딩 어시스턴트(예: Copilot 또는 ChatGPT)는 자연스럽게 표준 JSX/HTML을 생성합니다. 이들은 여러분의 특정 번역 키 스키마를 알지 못합니다.

- **선언적 방식(Declarative):** AI의 출력을 텍스트에서 키로 교체하기 위해 다시 작성해야 합니다.
- **컴파일러 방식(Compiler):** AI의 코드를 복사-붙여넣기만 하면 바로 작동합니다.

## 현실 점검: 왜 "마법"이 위험한가

"마법"은 매력적이지만, 추상화가 새어나갑니다. 빌드 도구가 인간의 의도를 이해하도록 의존하는 것은 아키텍처적 취약성을 초래합니다.

### 1. 휴리스틱 취약성 (추측 게임)

컴파일러는 무엇이 콘텐츠이고 무엇이 코드인지 추측해야 합니다.

- `className="active"`는 번역되나요? 이것은 문자열입니다.
- `status="pending"`는 번역되나요?
- `<MyComponent errorMessage="An error occurred" />`가 번역되나요?
- `"AX-99"`와 같은 제품 ID가 번역되나요?

결국 컴파일러와 "싸우게" 되며, 애플리케이션 로직이 깨지지 않도록 특정 주석(예: `// ignore-translation`)을 추가하게 됩니다.

### 2. 동적 데이터의 한계

컴파일러 추출은 **정적 분석**에 의존합니다. 안정적인 ID를 생성하려면 코드 내에 리터럴 문자열이 보여야 합니다.
API가 `server_error`와 같은 에러 코드 문자열을 반환하면, 컴파일러가 빌드 시점에 해당 문자열이 존재하는지 알지 못하기 때문에 번역할 수 없습니다. 동적 데이터 전용의 별도 "런타임 전용" 시스템을 구축해야만 합니다.

### 3. "청크 폭발"과 네트워크 워터폴

트리 쉐이킹을 허용하기 위해, 컴파일러 도구는 종종 컴포넌트별로 번역을 분할합니다.

- **결과:** 50개의 작은 컴포넌트가 있는 단일 페이지 뷰는 작은 번역 조각에 대해 **50개의 별도 HTTP 요청**을 발생시킬 수 있습니다. HTTP/2를 사용하더라도, 이는 네트워크 워터폴을 생성하여 단일 최적화된 언어 번들을 로드하는 것보다 애플리케이션이 느리게 느껴지게 만듭니다.

### 4. 런타임 성능 오버헤드

번역을 반응형으로 만들어(언어를 전환할 때 즉시 업데이트되도록) 컴파일러는 종종 _모든_ 컴포넌트에 상태 관리 훅을 주입합니다.

- **비용:** 5,000개의 아이템 리스트를 렌더링하면, 텍스트를 위해 5,000개의 `useState`와 `useEffect` 훅을 초기화하게 됩니다. 이는 선언형 라이브러리(일반적으로 단일 Context 프로바이더를 사용하는)가 절약하는 메모리와 CPU 사이클을 낭비하는 셈입니다.

## 함정: 공급업체 종속성 (Vendor Lock-in)

이는 컴파일러 기반 i18n에서 가장 위험한 측면이라고 할 수 있습니다.

선언형 라이브러리에서는 소스 코드에 명확한 의도가 포함되어 있습니다. 키를 직접 소유합니다. 라이브러리를 변경할 때는 단순히 import만 바꾸면 됩니다.

컴파일러 기반 접근법에서는 **소스 코드가 단지 영어 텍스트일 뿐입니다.** "번역 로직"은 빌드 플러그인의 설정 안에만 존재합니다.
만약 그 라이브러리가 더 이상 유지되지 않거나, 사용자가 그 라이브러리를 벗어나고 싶어도, 당신은 꼼짝할 수 없습니다. 소스 코드에 번역 키가 전혀 없기 때문에 쉽게 "eject"할 수 없습니다. 마이그레이션을 위해 애플리케이션 전체를 수동으로 다시 작성해야 할 것입니다.

## 다른 측면: 선언적 접근 방식의 위험

공정하게 말하면, 전통적인 선언적 방식도 완벽하지 않습니다. 자체적인 "함정"이 존재합니다.

1.  **네임스페이스 지옥:** 어떤 JSON 파일을 로드할지 (`common.json`, `dashboard.json`, `footer.json`) 수동으로 관리해야 하는 경우가 많습니다. 하나라도 잊으면 사용자는 원시 키를 보게 됩니다.
2.  **과도한 페칭:** 신중한 설정 없이 초기 로드 시 모든 페이지의 모든 번역 키를 실수로 불러와 번들 크기가 불필요하게 커질 수 있습니다.
3.  **동기화 이탈(Sync Drift):** 컴포넌트가 삭제된 후에도 해당 키가 JSON 파일에 남아 있는 경우가 흔합니다. 이로 인해 번역 파일이 무한히 커지며 "좀비 키"로 가득 차게 됩니다.

## Intlayer의 중간 지점

이것이 바로 **Intlayer**와 같은 도구들이 혁신을 시도하는 부분입니다. Intlayer는 컴파일러가 강력하지만, 암묵적인 마법은 위험하다는 점을 이해합니다.

Intlayer는 독특한 **`transform` 명령어**를 제공합니다. 숨겨진 빌드 단계에서 마법처럼 처리하는 대신, 실제로 **컴포넌트 코드를 재작성**할 수 있습니다. 텍스트를 스캔하여 코드베이스 내에서 명시적인 콘텐츠 선언으로 대체합니다.

이로써 두 세계의 장점을 모두 누릴 수 있습니다:

1.  **세분성(Granularity):** 번역을 컴포넌트 가까이에 유지하여 모듈화와 트리 쉐이킹(tree-shaking)을 개선합니다.
2.  **안전성:** 번역이 숨겨진 빌드 타임 마법이 아니라 명시적인 코드가 됩니다.
3.  **락인 방지:** 코드가 리포지토리 내에서 표준 선언적 구조로 변환되기 때문에 webpack 플러그인에 로직을 숨기지 않습니다.

## 결론

그렇다면 어떤 방식을 선택해야 할까요?

**당신이 주니어 개발자이거나, 1인 창업자이거나, MVP를 만들고 있다면:**
컴파일러 기반 접근법도 유효한 선택입니다. 매우 빠르게 개발할 수 있습니다. 파일 구조나 키에 대해 걱정할 필요가 없습니다. 그냥 개발하면 됩니다. 기술 부채는 "미래의 당신"이 해결할 문제입니다.

**당신이 전문적이고 엔터프라이즈급 애플리케이션을 개발 중이라면:**
마법 같은 접근법은 일반적으로 좋지 않습니다. 제어가 필요합니다.

- 백엔드에서 오는 동적 데이터를 처리해야 합니다.
- 저사양 기기에서의 성능을 보장해야 합니다 (hook 폭발 방지).
- 특정 빌드 도구에 영원히 종속되지 않도록 해야 합니다.

전문적인 애플리케이션의 경우, **선언적 콘텐츠 관리**(Intlayer나 검증된 라이브러리와 같은)는 여전히 최고의 표준입니다. 이는 관심사를 분리하고 아키텍처를 깔끔하게 유지하며, 애플리케이션이 여러 언어를 지원하는 능력이 "블랙박스" 컴파일러가 의도를 추측하는 것에 의존하지 않도록 보장합니다.
