next-intl은 정말 구식일까? 실무에서 마주한 한계와 최적화 방법
Next.js가 Pages Router에서 App Router로 넘어가며 기본 i18n 라우팅을 없앴을 때, next-intl은 커뮤니티의 가장 확실한 대안이었습니다. 메인테이너의 빠른 대응과 깔끔한 문서 덕분에 많은 팀들이 글로벌 서비스를 만들 때 자연스럽게 이 라이브러리를 선택했습니다.

저 역시 오랫동안 다국어 Next.js 애플리케이션의 성능을 분석해왔습니다. 지난 몇 년간 React 생태계는 서버 컴포넌트(RSC)와 컴파일 타임 최적화 중심으로 빠르게 재편되었지만, next-intl의 실제 프로덕션 번들을 뜯어보면 몇 가지 구조적인 병목이 그대로 드러납니다.
가장 큰 문제는 단순히 런타임 용량(클라이언트 프로바이더와 ICU 파서만으로 텍스트를 렌더링하기도 전에 gzip 기준 약 12KB가 듭니다)에 그치지 않습니다. 더 치명적인 것은 ‘다른 페이지의 번역 데이터 유출’입니다.
대다수의 프로젝트는 공식 가이드대로 루트 레이아웃에 NextIntlClientProvider를 두고 getMessages()로 사전을 한 번에 주입합니다. 그 결과, 사용자가 단순한 문의 페이지/contact)에 접속했을 뿐인데 브라우저는 대시보드/dashboard), 요금제/pricing), 설정 등 사이트 전체의 번역 텍스트를 통째로 내려받게 됩니다. 10개 화면을 기준으로 테스트해봤을 때, 특정 페이지로 전달된 번역 데이터의 약 90%가 해당 페이지와 전혀 무관한 텍스트였습니다.
여기에 더해 t("key")는 런타임에 문자열을 동적으로 평가하는 구조라 Webpack이나 Turbopack이 어떤 키가 실제로 쓰이는지 알 수 없습니다. 결과적으로 사용하지 않는 텍스트를 트리쉐이킹(Tree-shaking)으로 덜어내는 것도 불가능합니다.
서버 컴포넌트(RSC)를 다룰 때의 DX(개발자 경험) 문제도 만만치 않습니다. App Router에서 정적 렌더링(SSG)을 동작시키기 위해 next-intl은 setRequestLocale(locale)(과거 unstable_setRequestLocale)이라는 보일러플레이트를 도입해야만 했습니다. 모든 레이아웃과 서브 레이아웃, 페이지 최상단에서 이를 수동으로 호출해야 하는데, 단 한 곳이라도 빼먹으면 Next.js는 조용히 정적 생성을 포기하고 매 요청마다 동적 서버 렌더링을 돌립니다. 게다가 공통 디자인 시스템이나 독립된 클라이언트 컴포넌트에서는 번역을 동기적으로 깔끔하게 가져올 방법이 없어, 컨텍스트 프로바이더로 감싸거나 트리 전체에 locale을 수동으로 prop-drilling해야만 합니다.
## 그렇다면 이 구조를 어떻게 최적화해야 할까?
이미 프로덕션에서 next-intl을 운영하고 있다면 다음과 같은 방식으로 점진적 최적화를 적용할 수 있습니다.
첫 번째로, 루트 레이아웃에서 모든 번역을 한꺼번에 주입하는 것을 멈춰야 합니다. JSON 파일을 라우트별 네임스페이스로 분리하고, 개별 페이지나 하위 레이아웃 단위에서 getMessages()를 호출해 꼭 필요한 키만 내려주는 것입니다. 페이지와 네임스페이스 간의 매핑을 수동 관리해야 하는 번거로움은 있지만, 다른 페이지의 텍스트가 번들에 끼어드는 것을 즉시 줄일 수 있습니다.
두 번째로, 가능한 한 많은 번역을 React Server Components(RSC)로 밀어내는 것입니다. 클라이언트 컴포넌트에서 useTranslations()를 쓰는 대신 서버 컴포넌트에서 getTranslations()를 통해 텍스트를 순수 HTML로 렌더링하면, 클라이언트 런타임 비용도 하이드레이션 부담도 0으로 만들 수 있습니다.
세 번째로, 빌드 타임 정적 분석 도입을 검토해보는 방법이 있습니다. 최근 다국어 처리의 트렌드는 거대한 raw JSON 사전을 브라우저로 직접 보내는 방식을 벗어나는 것입니다. Intlayer 같은 접근법은 빌드 시점에 컴포넌트 임포트를 분석해 라우트별로 실제 쓰이는 텍스트만 정적으로 추출해줍니다. 네임스페이스를 일일이 손으로 쪼갤 필요도 없고, setRequestLocale 보일러플레이트나 디자인 시스템의 prop-drilling도 사라집니다. 이미 큰 규모의 코드베이스라면 @intlayer/next-intl 같은 호환 패키지를 통해 기존 useTranslations 호출 코드를 그대로 유지하면서 내부 번들링 엔진만 교체하는 것도 가능합니다.
현재 다국어 Next.js 앱을 서비스 중이라면 개발자 도구의 Network 탭을 열고 초기 진입 시 불필요한 번역 데이터가 얼마나 전송되고 있는지 한 번쯤 점검해보시길 권합니다. 생각보다 쉽게 챙길 수 있는 성능 개선 포인트가 보일 것입니다.
상세한 벤치마크 수치와 테스트 환경에 대한 전체 내용은 원문에서 확인하실 수 있습니다: