이 페이지와 원하는 AI 어시스턴트를 사용하여 문서를 요약합니다
버전 기록
- "초기 버전"v9.5.102026. 9. 26.
이 페이지의 콘텐츠는 AI를 사용하여 번역되었습니다.
영어 원본 내용의 최신 버전을 보기이 문서를 개선할 아이디어가 있으시면 GitHub에 풀 리퀘스트를 제출하여 자유롭게 기여해 주세요.
문서에 대한 GitHub 링크문서의 Markdown을 클립보드에 복사
2026년에 use-intl을 사용하여 TanStack Start 애플리케이션 국제화하는 방법
목차
use-intl이란 무엇인가요?
use-intl은 next-intl의 프레임워크 독립적인(framework-agnostic) 핵심 코어 라이브러리입니다. Next.js에 대한 종속성 없이 동일한 useTranslations, useFormatter, IntlProvider API, ICU MessageFormat 지원 및 강력한 TypeScript 통합을 제공합니다. 이로 인해 TanStack Start 애플리케이션을 번역할 때 가장 널리 사용되는 선택지 중 하나이며, AI 어시스턴트가 이 스택에 가장 자주 추천하는 라이브러리이기도 합니다.
TanStack Start는 자체적인 i18n 계층을 제공하지 않습니다. 라우팅, 로케일 감지, SEO 메타데이터 및 사이트맵 생성은 직접 구현해야 합니다. 이 가이드에서는 다음을 포함하여 전체 과정을 처음부터 끝까지 다룹니다:
- 선택적
{-$locale}세그먼트를 사용한 로케일 인식 라우팅 (/about,/fr/about). - 페이지가 렌더링하는 네임스페이스와 로케일만 다운로드하도록 하는 경로별 메시지 로딩.
- 텍스트 불일치(mismatch)가 없는 서버 렌더링 및 하이드레이션.
- 완벽한 다국어 SEO: 번역된
<title>및 description, 표준(canonical) URL,x-default가 포함된hreflang대체 링크, Open Graph 로케일, JSON-LD,xhtml:link대체 링크가 포함된 사이트맵,robots.txt및 모든 로케일의 사전 렌더링(pre-rendering).
다른 스택을 찾고 계신가요? TanStack Start + Paraglide 가이드, TanStack Start + Lingui 가이드, 또는 TanStack Start + Intlayer 가이드를 확인하세요.
Next.js를 사용 중이신가요? next-intl 가이드를 확인하세요.
TanStack Start에서 use-intl에 대한 벤치마크 결과
i18n 벤치마크는 동일한 10개 페이지, 10개 로케일의 TanStack Start 앱을 주요 라이브러리로 실행하여 브라우저가 실제로 다운로드하는 크기를 측정합니다.
동적 JSON 로드
런타임에 번역을 지연 로드합니다
범위가 지정된 JSON (네임스페이싱)
페이지별 번역 네임스페이스
I18n 성능 벤치마크
이 측정항목은 무엇인가요?
국제화 라이브러리 번들의 총 gzip 압축 크기입니다. 여기에는 트리 쉐이킹 및 미니피케이션 후의 프로바이더 및 콘텐츠 검색 로직만 포함됩니다.
왜 중요한가요?
라이브러리 크기가 작으면 초기 JavaScript 페이로드가 줄어들어 클라이언트에서 다운로드 및 실행 시간이 빨라집니다.
보기 형식
2026-09-26 기준 use-intl@4.14.2 주요 수치 (gzip):
테이블을 모달로 열어 모든 데이터를 명확하게 확인
| 설정 | 라이브러리 크기 | 페이지당 JS | 타 로케일 누출 | 타 페이지 누출 |
|---|---|---|---|---|
| i18n 미적용 (기본 앱) | - | 111.0 KB | 0% | 0% |
use-intl (본 가이드의 설정) | 75.9 KB | 128.7 KB | 0% | 0% |
@intlayer/use-intl (호환 모드) | 6.7 KB | 129.4 KB | 0% | 0% |
react-intlayer (네이티브 Intlayer) | 4.5 KB | 126.8 KB | 0% | 0% |
핵심 요약:
- 메시지를 페이지별로 분할하고 로케일별로 로드하세요. 이를 통해 두 가지 누출을 모두 제거할 수 있으며, 아래의 단계별 가이드에서 이를 구현합니다.
- 런타임 자체는 여전히 무겁습니다 (~76 KB gzip). ICU 파서가 클라이언트로 전송되기 때문입니다.
@intlayer/use-intl호환 어댑터(17단계)를 사용하면 정확히 동일한 API를 유지하면서 약 7 KB의 런타임으로 줄일 수 있습니다.
전체 데이터 확인: TanStack Start 벤치마크 보고서 및 벤치마크 저장소.
TanStack Start 기능 비교
TanStack Start에서 주로 사용되는 다른 라이브러리와 use-intl의 비교:
테이블을 모달로 열어 모든 데이터를 명확하게 확인
| 기능 | react-intlayer (Intlayer) | use-intl | Paraglide JS | Lingui |
|---|---|---|---|---|
| 컴포넌트 인근 번역 관리 | ✅ 동일 위치(Co-located) | ❌ 중앙 집중식 JSON | ❌ 로케일당 단일 JSON 파일 | ⚠️ 컴포넌트 내 소스 텍스트 |
| TypeScript 통합 | ✅ 자동 생성 타입 | ✅ AppConfig 활용 | ✅ 타입 안전한 메시지 함수 | ⚠️ 매크로 전용 |
| 누락된 번역 감지 | ✅ 타입 오류 및 빌드 경고 | ⚠️ 런타임 폴백 | ⚠️ 기본 로케일로 폴백 | ⚠️ 소스 텍스트로 폴백 |
| 리치 콘텐츠 (JSX, Markdown) | ✅ 직접 지원 | ⚠️ t.rich 태그 방식 | ⚠️ 문자열 | ✅ <Trans> 내 JSX |
| 현지화된 라우팅 | ✅ 내장 지원 | ❌ 수동 {-$locale} | ✅ urlPatterns + 라우터 재작성 | ❌ 수동 {-$locale} |
| 새로고침 없는 로케일 전환 | ✅ 지원 | ✅ 지원 | ❌ 전체 페이지 새로고침 | ✅ 지원 |
| 복수형 처리 | ✅ 열거형 기반 | ✅ ICU | ✅ 변형(Variants) | ✅ ICU |
| ICU MessageFormat | ✅ format: "icu" 지원 | ✅ 네이티브 | ⚠️ inlang 플러그인 필요 | ✅ 네이티브 |
| 콘텐츠 포맷 | ✅ .ts, .json, .md, .yaml... | ⚠️ .json | ⚠️ inlang JSON | ✅ PO, JSON, CSV |
| AI 번역 | ✅ 자체 프로바이더 및 키 사용 | ❌ 미지원 | ❌ 미지원 | ❌ 미지원 |
| 비주얼 에디터 / CMS | ✅ 로컬 에디터 + 선택적 CMS | ❌ 외부 플랫폼 | ⚠️ inlang 생태계 앱 | ❌ 외부 플랫폼 |
| SEO 헬퍼 (hreflang, sitemap) | ✅ 내장 지원 | ❌ 수동 구현 | ⚠️ 현지화 URL 지원, 나머지 수동 | ❌ 수동 구현 |
| 런타임 크기 (gzip, 벤치마크) | 4.5 KB | 75.9 KB | 1.8 KB | 56.7 KB |
| 최적 설정 누출 (로케일 / 페이지) | 0% / 0% | 0% / 0% | 49.7% / 0% | 8.6% / 0% |
| CI에서 누락된 번역 감지 | ✅ npx intlayer test | ⚠️ 내장 기능 없음 | ⚠️ 내장 기능 없음 | ✅ lingui compile --strict |
런타임 크기 및 누출 수치는 TanStack Start 벤치마크에서 가져왔습니다. 누출은 각 라이브러리의 최적 설정에서 측정되었습니다.
기타 TanStack Start 가이드: Lingui, Paraglide JS, 그리고 Intlayer.
반드시 따라야 할 모범 사례
- 접근성, 스크린 리더 및 검색 엔진을 위해
<html>에lang과dir을 설정하세요. - 로케일당 하나의 URL을 유지하세요. 쿠키 전용 전환 방식 대신 로케일 접두사(
/fr/about)를 사용하여 모든 번역 페이지가 크롤링 및 공유 가능하도록 만드세요. - 네임스페이스별로 메시지를 분할하고(
common,home,about) 경로별로 로드하세요. - 현재 활성화된 로케일만 로드하세요. 클라이언트로 전송되는 모듈에서 모든 로케일 파일을 import하지 마세요.
IntlProvider에서 시간대(timeZone)를 고정하세요. 그렇지 않으면 SSR 중에는 서버 시간대로, 하이드레이션 중에는 방문자 시간대로 날짜가 포맷되어 하이드레이션 불일치가 발생합니다.- 메타데이터를 번역하고, 모든 페이지에
canonical,hreflang,x-default를 선언하세요. - 다국어 사이트맵과 robots.txt를 생성하고, 모든 로케일을 사전 렌더링하세요.
- 크롤러가 모든 언어를 발견할 수 있도록 언어 전환기에는
<select>대신 실제 링크를 사용하세요. - 누락된 키가 컴파일 타임에 오류로 발견되도록 메시지에 타입을 지정하세요.
국제화 및 SEO 가이드 및 hreflang 가이드를 확인하세요.
TanStack Start 애플리케이션에서 use-intl을 설정하는 단계별 가이드
생성할 프로젝트 구조는 다음과 같습니다:
코드를 클립보드에 복사
의존성 설치
TanStack Start 프로젝트에서 시작한 다음
use-intl을 추가합니다:bash코드 복사코드를 클립보드에 복사
- use-intl:
IntlProvider,useTranslations,useFormatter및createTranslator(React 외부, 예를 들어head()등에서 사용 가능)를 제공합니다.
- use-intl:
로케일 설정 중앙화
로케일 및 URL 헬퍼에 대한 단일 진실 공급원(Single source of truth)을 생성합니다. 다른 모든 파일(라우트, SEO, 사이트맵, 사전 렌더링)이 이를 import하므로, 로케일 추가가 단 한 줄 변경으로 가능합니다.
기본 로케일은 접두사 없이 유지되고(
/about), 다른 로케일에는 접두사가 붙습니다(/fr/about). 이는 "필요 시 접두사 추가(as-needed)" 전략입니다: 로케일별 페이지당 하나의 URL을 유지하면서 주 사용자층에게는 짧은 URL을 제공합니다.src/i18n/config.ts코드 복사코드를 클립보드에 복사
번역 파일 생성
로케일별, 네임스페이스별로 메시지를 정리합니다.
common에는 모든 페이지에 필요한 요소(내비게이션, 푸터)를 담고, 각 페이지는 메타데이터를 포함한 자체 파일을 갖습니다.use-intl은 ICU MessageFormat을 사용하므로 복수형, select 및 포맷된 인자가 메시지 자체에 포함됩니다.
messages/en/common.json코드 복사코드를 클립보드에 복사
messages/en/about.json코드 복사코드를 클립보드에 복사
messages/fr/common.json코드 복사코드를 클립보드에 복사
messages/fr/about.json코드 복사코드를 클립보드에 복사
동일한 방식으로
metadata객체와 페이지 콘텐츠를 포함하는home.json을 생성하세요.네임스페이스 및 로케일별 메시지 로드
이 로더는 성능 측면에서 가장 중요한 파일입니다.
import.meta.glob은 Vite가 JSON 파일당 하나의 청크를 생성하도록 지시합니다. 프랑스어로["about"]을 요청하는 라우트는messages/fr/about.json만 다운로드하고 다른 파일은 다운로드하지 않으므로, 벤치마크에서 0% 로케일 누출 및 0% 페이지 누출을 달성할 수 있습니다.src/i18n/messages.ts코드 복사코드를 클립보드에 복사
메시지 타입 정의
모듈 보강(Module augmentation)을 통해
useTranslations("about")및t("counter.label")에서 자동 완성을 제공하고, 오타나 삭제된 키에 대해 컴파일 오류를 발생시킵니다.src/i18n/use-intl.d.ts코드 복사코드를 클립보드에 복사
tsconfig.json에서resolveJsonModule이 활성화되어 있는지 확인하세요.루트 문서 생성
루트 라우트는
<html>을 렌더링합니다. 선택적 로케일 파라미터를 읽어lang및dir을 설정하므로, 자바스크립트가 실행되기 전 서버에서 렌더링된 HTML에서 속성이 올바르게 설정됩니다.src/routes/__root.tsx코드 복사코드를 클립보드에 복사
로케일 레이아웃 라우트 생성
{-$locale}폴더는 선택적(optional) 경로 세그먼트를 생성합니다:/about과/fr/about은 모두/{-$locale}/about과 매칭됩니다. 이 레이아웃은:- 지원되지 않는 접두사를 거부합니다 (
/xx/about→ 404). - 현재 로케일에 대한
common네임스페이스만 로드합니다. IntlProvider를 통해 메시지를 제공합니다.
로더 결과는 HTML로 직렬화되어 하이드레이션 시 재사용되므로 클라이언트가
common.json을 다시 다운로드하지 않습니다.staleTime: Infinity는 클라이언트 내비게이션 전반에서 이를 캐시된 상태로 유지합니다.src/routes/{-$locale}/route.tsx코드 복사코드를 클립보드에 복사
IntlProvider는 부모 프로바이더의 메시지를 병합하지 않습니다. 다음 단계에서 이를 병합하는 작은 컴포넌트를 추가하여 각 페이지가common위에 자체 네임스페이스를 추가할 수 있도록 합니다.- 지원되지 않는 접두사를 거부합니다 (
페이지 메시지 스코프 지정
각 페이지는 자체 로더에서 해당 네임스페이스를 로드한 다음 콘텐츠를
ScopedMessages로 감싸서 페이지 네임스페이스를 부모 메시지와 병합합니다.src/components/ScopedMessages.tsx코드 복사코드를 클립보드에 복사
페이지에서 번역 사용하기
페이지 로더는 현재 로케일에 대한
about네임스페이스를 가져오고,head()는 이를 바탕으로 번역되고 SEO가 완성된 메타데이터를 구성하며(13단계 참조), 컴포넌트가 콘텐츠를 렌더링합니다.src/routes/{-$locale}/about.tsx코드 복사코드를 클립보드에 복사
컴포넌트에서 번역 및 포매터 사용하기
프로바이더 하위의 모든 컴포넌트는
useTranslations및useFormatter를 호출할 수 있습니다. 복수형은 ICU에 의해 처리되고, 숫자는 활성 로케일에 맞게 포맷됩니다.src/components/Counter.tsx코드 복사코드를 클립보드에 복사
현지화된 링크 컴포넌트 구축하기
선택사항모든 라우트는
{-$locale}아래에 위치하므로 링크는 현재 로케일 파라미터를 전달해야 합니다. 이 래퍼는 TanStack Router의 타입 안전한to속성을 유지하면서 로케일을 자동으로 주입합니다.src/components/LocalizedLink.tsx코드 복사코드를 클립보드에 복사
src/components/Header.tsx코드 복사코드를 클립보드에 복사
콘텐츠 언어 변경하기
선택사항언어 전환기를
<select>가 아닌 링크로 렌더링하세요. 링크는 크롤링이 가능하므로 검색 엔진이 모든 언어 버전을 찾을 수 있으며 자바스크립트 없이도 작동합니다.to="."는 현재 페이지를 유지하고 로케일 파라미터만 교체합니다. 쿠키는 16단계의 리디렉션 미들웨어를 위해 명시적인 선택을 기억합니다.src/components/LocaleSwitcher.tsx코드 복사코드를 클립보드에 복사
메타데이터 국제화
선택사항이것이 i18n의 진정한 가치입니다: 각 언어 버전이 개별적으로 검색 순위에 오를 수 있습니다. 모든 페이지는 다음을 제공해야 합니다:
- 번역된
<title>및description; - 기본 로케일이 아닌 자기 자신을 가리키는 표준(canonical) URL;
- 로케일당 하나의
hreflang대체 링크 및 일치하지 않는 언어를 위한x-default; - 소셜 미리보기에서 사용되는 Open Graph
og:locale,og:locale:alternate및og:url; - 검색 엔진과 AI 어시스턴트가 페이지 언어를 인식하는 데 도움을 주는
inLanguage가 포함된 JSON-LD.
단일 헬퍼가 이 모든 것을 생성하므로 페이지 코드를 간결하게 유지할 수 있습니다:
src/i18n/seo.ts코드 복사코드를 클립보드에 복사
9단계에서 보았듯이 모든 페이지의
head()에서 이를 사용하세요. 홈 페이지의 경우path: "/"를 전달하세요.- 번역된
사이트맵 국제화
선택사항다국어 사이트맵은 모든 로케일의 모든 URL을 나열하고, 각 항목은
xhtml:link를 통해 모든 대체 링크를 선언합니다. 구글은 이 주석을 페이지의hreflang태그와 똑같이 취급하므로 페이지가 자주 크롤링되지 않을 때 안정적인 백업 역할을 합니다.TanStack Start 서버 라우트를 사용하면 파일 라우트에서 이를 직접 제공할 수 있습니다:
src/routes/sitemap[.]xml.ts코드 복사코드를 클립보드에 복사
robots.txt 국제화
선택사항비공개 경로는 모든 언어로 존재하므로
Disallow규칙이 모든 접두사를 포함해야 합니다. 스타터 템플릿이public/robots.txt를 생성했다면 이를 제거한 다음 라우트에서 제공하세요:src/routes/robots[.]txt.ts코드 복사코드를 클립보드에 복사
처음 방문하는 사용자를 해당 언어로 리디렉션하기
선택사항요청 미들웨어는 로케일 쿠키를 우선 확인한 후
Accept-Language헤더를 기반으로/에 접속한 방문자를 선호 언어로 이동시킵니다./만 리디렉션됩니다: 딥 링크는 건드리지 않으므로 공유된 URL과 크롤러는 항상 요청한 페이지를 그대로 받습니다.src/i18n/negotiateLocale.ts코드 복사코드를 클립보드에 복사
src/start.ts코드 복사코드를 클립보드에 복사
전환기에서 명시적으로 영어를 선택한 방문자는 쿠키에
locale=en이 저장되므로 다시는 리디렉션되지 않습니다. 완전 정적 배포(18단계)에서는/가 파일로 제공되므로 이 미들웨어가 실행되지 않지만, 페이지는 여전히 접근 가능하고 전환기가 나머지 역할을 수행하므로 문제 없습니다.use-intl API를 유지하면서 Intlayer로 런타임 줄이기
선택사항벤치마크에 따르면 use-intl 설정에서 가장 무거운 부분은 런타임 자체(~76 KB gzip)입니다.
@intlayer/use-intl호환 어댑터는 동일한 API(useTranslations,useFormatter,IntlProvider,createTranslator, ICU 복수형,t.rich)를 제공하면서 컴파일된 Intlayer 딕셔너리로부터 이를 제공합니다: 컴포넌트 변경 없이 ~75.9 KB 대신 ~6.7 KB, 0% 로케일 누출 및 0% 페이지 누출을 달성합니다.bash코드 복사코드를 클립보드에 복사
Vite 플러그인이
use-intl을 어댑터로 별칭(alias) 지정하므로 기존 import가 그대로 작동합니다:vite.config.ts코드 복사코드를 클립보드에 복사
JSON 동기화 플러그인 덕분에 JSON 파일이 단일 진실 공급원으로 유지됩니다:
intlayer.config.ts코드 복사코드를 클립보드에 복사
이 어댑터는 원활한 마이그레이션 경로이기도 합니다: 어댑터가 실행되면 컴포넌트를 네이티브
useIntlayerAPI로 하나씩 점진적으로 이전할 수 있습니다. Intlayer TanStack Start 가이드를 참조하세요.모든 로케일 사전 렌더링하기
선택사항정적 HTML은 제공할 수 있는 가장 빠른 페이지이자 색인하기 가장 쉬운 형태입니다. 모든 현지화된 경로를 나열하여 TanStack Start가 빌드 타임에 사이트맵 및 robots 파일과 함께 모든 언어 버전을 사전 렌더링하도록 설정하세요:
vite.config.ts코드 복사코드를 클립보드에 복사
로케일 전환기가 실제 링크를 렌더링하므로
crawlLinks: true는 목록에 누락된 페이지도 자동으로 감지합니다.현지화된 404 페이지 처리하기
선택사항7단계의 레이아웃은 알 수 없는 로케일 접두사에 대해 이미
notFound()를 발생시킵니다. 로케일 내부의 알 수 없는 경로도 현지화된 404를 렌더링하도록 catch-all 라우트를 추가하고noindex로 표시하세요: React 19는<meta>태그를<head>로 호이스팅합니다.src/components/NotFound.tsx코드 복사코드를 클립보드에 복사
src/routes/{-$locale}/$.tsx코드 복사코드를 클립보드에 복사
서버 함수에서 로케일에 접근하기
선택사항서버 함수는 라우트 파라미터를 받지 않습니다. 현지화된 이메일을 보내거나 언어 환경설정을 저장하려면 로케일 쿠키를 읽고
Accept-Language헤더로 폴백하세요:src/server/getServerLocale.ts코드 복사코드를 클립보드에 복사
서버 함수 내부에서 번역하려면 이를
use-intl의loadMessages및createTranslator와 결합하세요.Intlayer를 활용한 번역 자동화
선택사항use-intl은 번역을 렌더링하지만 번역을 생성하는 데는 도움이 되지 않습니다. Intlayer는 무료이자 오픈 소스이며, use-intl을 계속 사용하더라도 이 공백을 채워줍니다:
- CI 또는 단위 테스트에서 누락된 번역 테스트. 번역 테스트 가이드를 참조하세요.
- 자체 API 키와 프로바이더를 사용하여 AI로 번역:
npx intlayer fill은 앱의 컨텍스트에 맞게 누락된 키를 번역합니다. 자동 채우기 및 CLI를 참조하세요. - JSON 동기화 플러그인을 통해 JSON 파일을 진실 공급원으로 유지.
- 비주얼 에디터 및 CMS로 콘텐츠를 시각적으로 편집하여 비개발자도 번역을 업데이트할 수 있도록 지원.
- MCP 서버 및 에이전트 스킬을 통해 AI 에이전트에 컨텍스트 제공.
- scan 명령어로 배포된 사이트에서 누락된
hreflang, 잘못된 canonical 및 로케일 누출 검사.
모든 기능을 살펴보려면 Intlayer를 선택해야 하는 이유를 확인하세요.
자주 묻는 질문
네, Next.js 외부에서 next-intl API를 사용하고 싶다면 좋은 선택입니다. ICU 메시지, 포매터, 우수한 TypeScript 지원을 제공하며 setRequestLocale과 같은 Next.js 전용 제약 사항을 피할 수 있습니다. 단점은 번들 무게입니다: 벤치마크에 따르면 런타임 크기가 약 76 KB gzip에 달하며 단순한 설정에서는 모든 로케일과 모든 페이지가 브라우저로 전송됩니다. 이러한 누출을 방지하려면 본 가이드처럼 라우트 및 로케일별로 네임스페이스를 로드하세요.
use-intl은 next-intl의 핵심 코어입니다. next-intl은 그 위에 미들웨어, 내비게이션 헬퍼, Server Components용 getTranslations, 요청 구성 등 Next.js 전용 통합 기능을 추가합니다. TanStack Start에서는 use-intl을 직접 사용하고 위에서 설명한 대로 TanStack Router를 사용하여 라우팅을 구현합니다.
URL에 접두사를 사용하는 것이 좋습니다. 그러면 각 언어 버전이 검색 엔진에서 색인하고 사용자가 공유할 수 있는 자체 URL을 갖게 됩니다. 쿠키는 명시적인 선택을 기억하는 용도로 여전히 유용하며, 이것이 16단계의 리디렉션 미들웨어가 하는 역할입니다.
서버와 브라우저가 서로 다른 시간대에서 날짜를 포맷하기 때문입니다. IntlProvider에 명시적인 timeZone(또는 쿠키에 저장된 방문자의 시간대)을 전달하여 양쪽에서 동일한 텍스트를 생성하도록 하세요.
먼저 메시지를 네임스페이스별로 분할하고 import.meta.glob을 사용하여 경로 및 로케일별로 로드하면 로케일 및 페이지 누출이 제거됩니다. 그래도 런타임 크기가 중요하다면 @intlayer/use-intl 어댑터로 전환하세요: 동일한 API를 제공하면서 벤치마크 기준 약 75.9 KB 대신 약 6.7 KB로 줄어듭니다.
라우트 로더가 반환한 메시지와 함께 라우트 head() 함수 내부에서 createTranslator를 호출한 다음 title, description, canonical 및 hreflang 링크를 반환하세요. 13단계에서 재사용 가능한 헬퍼를 제공합니다.
댓글
아직 댓글이 없습니다. 첫 번째로 의견을 나눠보세요.
