이 페이지와 원하는 AI 어시스턴트를 사용하여 문서를 요약합니다
이 페이지의 콘텐츠는 AI를 사용하여 번역되었습니다.
영어 원본 내용의 최신 버전을 보기이 문서를 개선할 아이디어가 있으시면 GitHub에 풀 리퀘스트를 제출하여 자유롭게 기여해 주세요.
문서에 대한 GitHub 링크문서의 Markdown을 클립보드에 복사
next-intl VS Intlayer | React & Next.js 국제화(i18n) 벤치마크
next-intl은 오늘날 Next.js App Router의 기본 선택지입니다. 라우팅과의 긴밀한 통합, 완전한 ICU MessageFormat 지원, 전통적인 i18n 시스템을 사용해 본 사람이라면 누구나 익숙한 개발자 경험을 제공합니다.
반면 Intlayer는 문제를 근본적으로 재구성합니다. 중앙 집중식 사전이 없으며, 네임스페이스와 라우트를 수동으로 일치시킬 필요가 없습니다. 콘텐츠는 각 컴포넌트 바로 옆에 선언되며, 빌드 타임 컴파일러가 각 페이지에 필요한 콘텐츠만 자동으로 번들링합니다.
이 글은 각 라이브러리로 동일한 애플리케이션을 빌드하고 브라우저가 실제로 다운로드하고 실행하는 항목을 기록하는 오픈 소스 테스트 스위트인 Benchmark Bloom의 데이터를 바탕으로 두 라이브러리를 비교합니다.
요약 (tl;dr):next-intl은 런타임 비용만으로도 페이지당 최소 +12.6 KB gzip을 추가하며, 표준 설정(static및dynamic)에서 다른 페이지 문자열의 약 90%를 누출합니다. 이 누출을 제거하려면 카탈로그를 네임스페이스로 분할하고 페이지별로 수동 선택해야 하는 번거로운 작업이 필요합니다. 반면Intlayer컴파일러는 추가 설정 없이도 누출 0%, 3배 더 작은 컴포넌트 크기, 기본 앱 대비 단 +0.3 KB 증가만을 보장합니다.
한눈에 보기
- next-intl - Next.js 커뮤니티의 표준. 언어별 중앙 집중식 JSON 사전, 완전한 ICU MessageFormat 지원, Next.js 요청 처리 및 라우팅과의 긴밀한 통합.
- Intlayer - 컴포넌트 중심 콘텐츠 모델.
.content.ts파일이 담당 컴포넌트 바로 옆에 위치하며, 빌드 타임 컴파일러가 컴포넌트 및 로케일별로 트리 셰이킹과 지연 로딩을 수행하고 엄격한 TypeScript 타입을 자동으로 생성합니다.
테이블을 모달로 열어 모든 데이터를 명확하게 확인
배지는 자동으로 업데이트됩니다.
기능별 직접 비교
테이블을 모달로 열어 모든 데이터를 명확하게 확인
| 기능 | Intlayer (react-intlayer / next-intlayer) | next-intl (next-intl / use-intl) |
|---|---|---|
| 컴포넌트 인근 번역 파일 배치 | ✅ 예, 각 컴포넌트와 함께 .content.ts 위치 | ❌ messages/ 폴더 내 중앙 집중식 JSON 사전 |
| TypeScript 통합 | ✅ 콘텐츠로부터 자동 생성되는 엄격한 타입 | ⚠️ 메시지 경로에 대한 수동 global.d.ts 설정을 통해 지원 |
| 누락된 번역 감지 | ✅ TypeScript 오류 + 빌드 타임 오류/경고 | ⚠️ 런타임에서 누락된 키를 반환하거나 설정에 따라 오류 발생 |
| 풍부한 콘텐츠 (JSX / Markdown / 컴포넌트) | ✅ 직접 지원 | ⚠️ 컴포넌트 매핑을 포함한 t.rich()를 통해 지원 |
| ICU MessageFormat 지원 | ⚠️ 개발 중 | ✅ 예, 완전한 ICU 지원 |
| 동기식 서버 컴포넌트 지원 | ✅ next-intlayer/server의 useIntlayer가 자식 서버 컴포넌트에서 즉시 작동 | ❌ 비동기 서버 부모로부터 props를 통해 번역을 전달해야 함 |
| 트리 셰이킹 (Tree-shaking) | ✅ 컴포넌트 및 로케일별로 컴파일러가 자동 수행 | ⚠️ 네임스페이스를 수동 분할하고 pick()을 사용해야 함 |
| 지연 로딩 (Lazy loading) | ✅ 단 한 줄의 설정 (importMode: 'dynamic') | ⚠️ getRequestConfig에서 수동 동적 import 필요 |
| 비주얼 에디터 / CMS | ✅ 무료 비주얼 에디터 + 선택적 CMS | ❌ 없음 |
| AI 기반 자동 번역 | ✅ 내장 기능, 자체 API 키 사용 | ❌ 없음 |
| MCP 서버 및 에이전트 스킬 | ✅ 지원 | ❌ 없음 |
벤치마크
측정 항목
Benchmark Bloom 테스트 스위트는 각 라이브러리로 동일한 애플리케이션을 빌드합니다. 10개 페이지(home, about, blog, careers, contact, FAQ, pricing, products, settings, team), 10개 언어(en, fr, es, de, it, pt, zh, ja, ko, ru), 동일한 컴포넌트 및 동일한 콘텐츠로 구성됩니다. 페이지는 en과 fr로 측정됩니다. 각 라이브러리는 최대 4가지 로딩 전략으로 구현되었습니다.
테이블을 모달로 열어 모든 데이터를 명확하게 확인
| 전략 | 설명 | 대상 사용자 |
|---|---|---|
| static | 모든 로케일과 모든 페이지를 시작 시 함께 번들링하여 로드 | 빠른 프로토타입, AI 생성 코드 |
| dynamic | 활성 로케일만 로드하지만, 모든 페이지를 한 번에 가져옴 | 대부분의 프로젝트 |
| scoped-static | 라우트별 네임스페이스 분할, 지연 로딩 없음 | 드문 경우 |
| scoped-dynamic | 라우트별 네임스페이스 + 지연 로딩. 현재 로케일의 현재 페이지만 전송 | 엄격한 성능 예산이 필요한 앱 |
Intlayer에는 "scoped" 변형이 없습니다. 컴파일러가 콘텐츠 범위를 컴포넌트별로 자동 제한하므로 static 및 dynamic 행이 이미 스코프화되어 있습니다.
각 빌드에 대해 스위트는 다음을 기록합니다:
- 라이브러리 크기 (Lib size): i18n 라이브러리만 가져오는 빈 컴포넌트의 gzip 크기.
- 페이지 JS (Page JS): 페이지당 다운로드된 gzip JavaScript 크기.
- 로케일 누출률 (Locale leak %): 사용자가 보지 않는 로케일에 속한 문자열의 비율.
- 페이지 누출률 (Page leak %): 사용자가 머물고 있지 않은 페이지에 속한 문자열의 비율.
- 컴포넌트 평균 크기 (Component avg): 격리되어 컴파일된 각 컴포넌트의 평균 gzip 크기.
- E2E 반응성: 새 언어 선택부터 DOM의
html[lang]업데이트까지의 시간. - 하이드레이션 (Hydration): React 하이드레이션 단계의 소요 시간.
아래 수치는next-intl4.14.2 및intlayer9.5.1을 사용한 2026-09-12 실행 결과입니다.
Next.js (App Router) 결과
테이블을 모달로 열어 모든 데이터를 명확하게 확인
| 라이브러리 | 전략 | 라이브러리 크기 (gz) | 평균 페이지 JS (gz) | 로케일 누출 | 페이지 누출 | 평균 컴포넌트 (gz) | E2E 반응성 | 하이드레이션 |
|---|---|---|---|---|---|---|---|---|
| 기본 앱 (i18n 없음) | - | 0.0 KB | 141.0 KB | 0.0% | 0.0% | 0.9 KB | 13.4 ms | 11.8 ms |
next-intl | static | 14.7 KB | 153.6 KB | 4.2% | 89.8% | 21.8 KB | 16.0 ms | 14.7 ms |
next-intl | dynamic | 14.7 KB | 153.6 KB | 9.7% | 89.9% | 21.8 KB | 15.6 ms | 14.8 ms |
next-intl | scoped-static | 14.7 KB | 153.6 KB | 0.0% | 0.0% | 80.1 KB | 17.9 ms | 17.4 ms |
next-intl | scoped-dynamic | 14.7 KB | 153.6 KB | 0.0% | 0.0% | 22.9 KB | 17.8 ms | 16.8 ms |
next-intlayer | static | 5.5 KB | 141.3 KB | 0.0% | 0.0% | 8.5 KB | 15.5 ms | 16.9 ms |
next-intlayer | dynamic | 5.5 KB | 141.3 KB | 0.0% | 0.0% | 6.9 KB | 15.3 ms | 15.9 ms |
@intlayer/next-intl (호환) | static | 8.0 KB | 147.5 KB | 0.0% | 0.0% | 8.1 KB | 14.5 ms | 12.8 ms |
@intlayer/next-intl (호환) | dynamic | 8.0 KB | 148.7 KB | 0.0% | 0.0% | 8.1 KB | 11.7 ms | 12.8 ms |
결과 해석
- 런타임 비용. 기본 앱은 페이지당 141.0 KB입니다.
next-intl은 이를 153.6 KB(모든 페이지에서 +12.6 KB gzip)로 늘리지만, Intlayer는 141.3 KB(+0.3 KB)로 거의 차이가 없습니다. - 콘텐츠 누출. 가장 흔히 사용되는 설정(
static및dynamic)에서next-intl은 전체en.json이 클라이언트 공급자에 들어가기 때문에 페이지마다 다른 페이지 문자열의 약 90%를 전송합니다. 이를 0%로 만들려면 복잡한 수동 네임스페이스 분할이 필요하지만, Intlayer는 기본적으로 0%입니다. - 컴포넌트 크기.
useTranslations()를 호출하는 컴포넌트는 평균 21.8 KB로 컴파일되지만,useIntlayer()를 사용하는 동일한 컴포넌트는 단 6.9 KB입니다.
TanStack Start (use-intl) 결과
테이블을 모달로 열어 모든 데이터를 명확하게 확인
| 라이브러리 | 전략 | 라이브러리 크기 (gz) | 평균 페이지 JS (gz) | 로케일 누출 | 페이지 누출 | 평균 컴포넌트 (gz) | E2E 반응성 |
|---|---|---|---|---|---|---|---|
| 기본 앱 (i18n 없음) | - | 0.0 KB | 111.0 KB | 0.0% | 0.0% | 0.7 KB | 8.1 ms |
use-intl | static | 14.1 KB | 179.8 KB | 50.0% | 89.8% | 76.0 KB | 6.7 ms |
use-intl | dynamic | 14.1 KB | 119.4 KB | 0.0% | 89.8% | 75.9 KB | 7.0 ms |
use-intl | scoped-static | 14.1 KB | 128.7 KB | 0.0% | 0.0% | 87.1 KB | 20.9 ms |
use-intl | scoped-dynamic | 14.1 KB | 128.7 KB | 0.0% | 0.0% | 87.1 KB | 13.3 ms |
intlayer | static | 5.0 KB | 125.8 KB | 50.0% | 0.0% | 8.1 KB | 3.2 ms |
intlayer | dynamic | 5.0 KB | 118.6 KB | 0.0% | 0.0% | 6.3 KB | 3.6 ms |
@intlayer/use-intl (호환) | dynamic | 7.3 KB | 129.7 KB | 0.0% | 0.0% | 9.3 KB | 8.7 ms |
결과 해석
- 단순한
use-intl설정은 기본 앱보다 페이지당 68.8 KB 더 많은 JS를 전송합니다. dynamic모드에서도use-intl은 119.4 KB에 달하며 여전히 89.8%의 페이지 누출을 유지합니다.- 아키텍처의 차이는 컴포넌트 크기에서 명확히 드러납니다.
use-intl의 76-87 KB 대비 Intlayer는 6-8 KB에 불과합니다. - 로케일 전환 속도는 Intlayer가 2~4배 더 빠릅니다(3 ms vs 7-21 ms).
왜 이런 차이가 발생하는가? 중앙 집중식 카탈로그 vs 컴파일된 사전
next-intl은 전통적인 방식을 따릅니다. 로케일당 하나의 JSON 파일이 getRequestConfig에서 로드되고, NextIntlClientProvider에 전달되며, t("namespace.key")로 읽힙니다.
코드를 클립보드에 복사
런타임은 페이지에서 어떤 키가 사용될지 미리 알 수 없으므로 전체 카탈로그를 전송하는 것이 유일하게 안전한 기본값입니다.
Intlayer는 이 구조를 완전히 뒤집습니다. 콘텐츠는 컴포넌트 바로 옆에 선언됩니다.
코드를 클립보드에 복사
빌드 시 컴파일러는 어떤 컴포넌트가 어떤 사전을 가져오는지 감지하여 활성 로케일에 필요한 사전만 번들링합니다.
dynamic행의 수치를 얻으려면intlayer.config.ts에서dictionary.importMode: 'dynamic'을 설정하세요. 번들 최적화 문서를 참조하세요.
개발자 경험
클라이언트 컴포넌트
next-intl
코드를 클립보드에 복사
코드를 클립보드에 복사
Intlayer
코드를 클립보드에 복사
코드를 클립보드에 복사
동기식 서버 컴포넌트
디자인 시스템 요소(내비게이션 바, 바닥글, 카드 등)는 클라이언트 컴포넌트의 자식으로 렌더링되는 서버 컴포넌트인 경우가 많으므로 비동기(async)일 수 없습니다.
next-intl
코드를 클립보드에 복사
Intlayer
코드를 클립보드에 복사
메타데이터
next-intl
코드를 클립보드에 복사
Intlayer
코드를 클립보드에 복사
next-intl API 유지 및 Intlayer 출력 획득
위의 벤치마크 결과를 얻기 위해 기존 컴포넌트를 모두 다시 작성할 필요는 없습니다. @intlayer/next-intl은 드롭인 어댑터입니다. useTranslations, getTranslations, useFormatter, t.rich(), ICU 복수형을 그대로 유지하면서 Intlayer 컴파일러로 컴파일된 Intlayer 사전에서 콘텐츠를 제공합니다.
코드를 클립보드에 복사
벤치마크에서 동일한 앱의 호환 빌드는 애플리케이션 코드를 전혀 수정하지 않고도 페이지당 153.6 KB에서 147.5 KB로, 컴포넌트당 21.8 KB에서 8.1 KB로 줄었으며, 페이지 누출은 약 90%에서 0%로 개선되었습니다. 기존 messages/{locale}.json 파일은 JSON 동기화 플러그인을 통해 계속 단일 소스로 유지할 수 있습니다.
자세한 단계는 next-intl 마이그레이션 가이드를 참조하세요.
언제 무엇을 선택해야 할까요?
- next-intl 선택: Next.js의 광범위한 생태계 표준을 원하거나, ICU MessageFormat에 크게 의존하거나, 앱이 중소 규모이거나, 중앙 집중식 번역 플랫폼(Crowdin, Phrase, Lokalise 등)과 연동하는 경우.
- Intlayer 선택: 컴포넌트 단위 콘텐츠 관리, 엄격한 TypeScript 지원, 빌드 타임 누락 키 감지, 설정 없는 자동 트리 셰이킹 및 지연 로딩, 동기식 서버 컴포넌트, 내장 편집 도구(비주얼 에디터, CMS, AI 번역, MCP 서버)를 원하는 경우. 모듈식 코드베이스나 대규모 디자인 시스템에 특히 유용합니다.
@intlayer/next-intl선택: 이미next-intl을 사용 중이며 코드 재작성 없이 번들 최적화 이점을 얻고 싶은 경우.
관련 비교
- i18next vs Intlayer (동일 벤치마크)
- Lingui vs Intlayer (동일 벤치마크)
- vue-i18n vs Intlayer 벤치마크 (동일 벤치마크)
- next-i18next vs next-intl vs Intlayer
- next-intl은 구식인가요?
GitHub STARS
GitHub 스타 수는 프로젝트의 인기, 커뮤니티의 신뢰 및 장기적인 지속 가능성을 보여주는 강력한 지표입니다.
결론
next-intl은 탄탄하고 잘 유지 관리되는 라이브러리이며, 벤치마크에서도 Next.js 환경에서 결코 나쁜 선택이 아님을 확인할 수 있습니다. 하지만 중앙 집중식 카탈로그 모델은 최적화의 모든 부담을 개발자에게 전가합니다. 단순한 설정에서는 다른 페이지 콘텐츠가 약 90% 누출되며, 런타임 자체만으로도 모든 페이지에서 +12.6 KB gzip의 비용이 발생합니다.
Intlayer는 이 모든 작업을 컴파일러로 이전합니다. 컴포넌트별 사전, 로케일별 지연 로딩, 미사용 콘텐츠 정리는 규칙이 아닌 자동 빌드 결과물입니다. 동일한 앱에서 얻은 결과는 페이지당 +0.3 KB, 누출 0%, 3배 더 작은 컴포넌트, TanStack Start에서 2~4배 더 빠른 로케일 전환이었습니다.
모든 원시 데이터, 테스트 앱 및 스크립트는 Benchmark Bloom 저장소에 공개되어 있습니다. 직접 실행해 보세요.
자세한 내용은 'Why Intlayer?' 문서를 참조하세요.
댓글
아직 댓글이 없습니다. 첫 번째로 의견을 나눠보세요.
