
"글로벌 시장으로 진출합니다!"
기획팀의 야심 찬 선언과 함께 제 자리엔 '다국어 지원(i18n)'이라는 묵직한 과제가 떨어졌습니다. 새로 만드는 프로젝트라면 참 좋았겠지만, 현실은 이미 수많은 유저들이 매일 사용하고 있는 **'라이브 서비스'**였습니다.
개발자들 사이에서 가장 악명 높은 작업 중 하나인 "달리는 기차의 바퀴 바꾸기"가 시작된 순간이었죠.
기차를 멈추지 않고(무중단), 승객들이 멀미를 느끼지 않게(SEO 및 기존 URL 유지), 그러면서도 바퀴를 튼튼한 다국어용으로 교체해야 했습니다. 이 험난했던 여정, 그리고 라이브러리의 밑바닥을 파헤치며 깨달았던 사투의 기록을 공유합니다.
새 바퀴를 갈아 끼우기 전에 절대로 건드려선 안 될 성역이 있었습니다.
처음엔 간단하게 ?lang=en 같은 쿼리 파라미터 방식을 생각했습니다. 기존 URL을 건드리지 않으니까요. 하지만 검색 엔진은 URL 경로(/en/...) 형태를 훨씬 선호하며, 그래야 각 언어별 페이지로 명확히 인지하기 때문입니다.

구글의 공식 문서를 기반하여 결국 Sub-path 방식(/[locale]/...)을 택했습니다. 달리는 기차 아래로 기어들어 갈 준비를 마쳤습니다.

경로 구조를 잡고 나니 프론트엔드 개발자로서 근본적인 의문이 들었습니다.
"엥? 결국 JSON 파일에서 다국어 텍스트를 가져와서 전체 화면에 뿌려주려면, Zustand나 React Context 같은 전역 상태(Global State)로 현재 언어(
lang)를 관리해야 하는 거 아닌가?"
기존 React (SPA) 방식에 익숙했던 저의 오판이었습니다. 만약 최상단에서 Context Provider로 언어 상태를 관리하게 되면, 앱 전체가 클라이언트 사이드 렌더링(CSR)으로 동작하게 되어 기껏 지키려 했던 SEO와 초기 로딩 속도(TTFB)가 다 박살 나버리기 때문입니다.
이 딜레마를 안고 next-intl 라이브러리의 동작 원리를 파헤치기 시작했고, 이 녀석이 전역 상태 대신 '쿠키(Cookie)'와 '미들웨어(Middleware)'를 기가 막히게 활용한다는 사실을 깨달았습니다.