정의
Locale-aware routing은 사용자가 선택한 언어(ko/en/ja)가 모든 내부 URL과 클라이언트 네비게이션에 일관되게 반영되는 라우팅 방식이다.
next-intl에서 localePrefix: "always"를 쓰면 URL은 항상 /ko/til, /en/knowledge/... 형태가 되고, 프리픽스 없는 /til 같은 경로는 미들웨어가 기본 locale(예: /ko/til)로 301 리다이렉트한다.
왜 필요한가
콘텐츠는 en/ja로 늘었는데 헤더·사이트맵·카드가 plain next/link로 /til을 가리키면, /en에서 클릭할 때마다 미들웨어가 /ko/til로 보내 언어 선택이 매번 초기화된다. 번역 품질 문제가 아니라 라우팅 계약 위반이다.
반대로 @/i18n/navigation의 Link·useRouter는 현재 locale을 href에 자동 포함한다. SSR HTML의 href를 샘플링하면 bare path 회귀를 빠르게 잡을 수 있다.
동작 원리
- 미들웨어:
localePrefix: "always"→ bare pathname 감지 시 default locale로 redirect. - i18n Link:
href="/til"→ 렌더 시/en/til(현재 locale 기준). - plain next/link:
href="/til"그대로 → 미들웨어 301 →/ko/til. - 언어 선택기:
routing.locales단일 소스로 Dropdown·sitemap·메시지 키 동기화. - programmatic nav:
router.push,replace도 i18n router 사용.startTransition·view transition과 병행 가능. - 마크다운 본문 링크: remark 단계에서 locale prefix 변환(앱 shell Link와 별도 파이프라인).
실무 적용
- content-nav·footer·리스트 카드·빵부스러기를 grep해
next/link내부 사용을 제거한다. - detail 미번역 fallback은 페이지별 복붙 대신
getLocalizedDocumentOrRedirect헬퍼 + 테스트. - prod
next start후curl로/en/...HTML의 internal href가 en-prefixed인지 카운트(bare 0건 목표). - load-more·relations API에 활성 locale 쿼리 파라미터 또는 헤더 전달.
- ESLint custom rule 또는 codemod로
src/내부from "next/link"신규 사용 검토.
트레이드오프
| 선택 | 장점 | 단점 |
|---|---|---|
| localePrefix always | URL만 보고 언어 식별, 캐시·공유 명확 | 모든 내부 링크 i18n API 강제 |
| localePrefix as-needed | default locale URL 짧음 | en/ja와 ko URL 형태 불일치 |
| plain Link + 수동 prefix | 익숙함 | 누락 시 301·로케일 유실 |
| redirect fallback(ko) | 읽을 콘텐츠 보장 | URL locale 변경 |
사용하면 안 되는 경우
- 외부 사이트·
mailto:·절대 URL — plain<a>또는next/linkwith full URL은 locale과 무관. - API route·정적 asset — locale segment 없음.
localePrefix: "never"정책 — 이 문서의 always 전제와 다름.
흔한 실수
- 헤더만 i18n
Link로 바꾸고 content-nav 12곳은 plainnext/link로 남김. - 클라이언트에서만 locale prefix를 붙이고 SSR HTML은 bare → SEO·no-JS 환경에서 ko로 튐.
router.back()에 view transition 기대 — popstate는 동기라 VT 미지원(명시 URLpush필요).- markdown 본문
../knowledge/...링크는 locale 변환 파이프라인 없이 배포.
관련 개념
- multilingual-content-locale-suffix — 콘텐츠 locale 축
- hierarchical-document-navigation — 목록→상세 depth
- ssr-hydration-mismatch — SSR HTML과 클라이언트 href 일치