자동 코드 분할
TanStack Router의 자동 코드 분할 기능을 사용하면 라우트 컴포넌트와 관련 데이터를 지연 로드하여 애플리케이션의 번들 크기를 최적화할 수 있습니다. 현재 라우트에 필요한 코드만 로드해 초기 로드 시간을 최소화하려는 대규모 애플리케이션에 특히 유용합니다.
이 기능을 켜려면 번들러 플러그인 구성에서 autoCodeSplitting 옵션을 true로 설정하면 됩니다. 그러면 추가 설정 없이 라우터가 라우트의 코드 분할을 자동으로 처리합니다.
// vite.config.ts
import { defineConfig } from 'vite'
import { tanstackRouter } from '@tanstack/router-plugin/vite'
export default defineConfig({
plugins: [
tanstackRouter({
autoCodeSplitting: true, // Enable automatic code splitting
}),
],
})
하지만 이것은 시작에 불과합니다. TanStack Router의 자동 코드 분할은 쉽게 활성화할 수 있을 뿐 아니라, 라우트를 청크로 분할하는 방식을 맞춤 설정할 수 있는 강력한 옵션도 제공합니다. 이를 통해 특정 요구 사항과 사용 패턴에 맞춰 애플리케이션 성능을 최적화할 수 있습니다.
어떻게 작동하나요?
TanStack Router의 자동 코드 분할은 '개발' 및 '빌드' 시점에 라우트 파일을 변환하여 작동합니다. 컴포넌트와 로더에 지연 로드 래퍼를 사용하도록 라우트 정의를 다시 작성하므로, 번들러가 이러한 속성을 별도의 청크로 그룹화할 수 있습니다.
[!TIP] 청크는 애플리케이션 코드의 일부를 포함하며 필요할 때 로드할 수 있는 파일입니다. 현재 라우트에 필요한 코드만 로드하므로 애플리케이션의 초기 로드 시간을 줄이는 데 도움이 됩니다.
따라서 애플리케이션이 로드될 때 모든 라우트의 코드가 포함되지 않습니다. 대신 처음에 필요한 라우트의 코드만 포함됩니다. 사용자가 애플리케이션을 탐색하면 추가 청크가 필요할 때 로드됩니다.
이 과정은 코드를 직접 분할하거나 지연 로드를 관리하지 않아도 원활하게 수행됩니다. TanStack Router 번들러 플러그인이 모든 작업을 처리하므로 라우트가 기본적으로 성능에 맞게 최적화됩니다.
변환 과정
자동 코드 분할을 활성화하면 번들러 플러그인이 정적 코드 분석을 사용해 라우트 파일의 코드를 검사하고 최적화된 출력으로 변환합니다.
각 라우트 파일을 처리할 때 이 변환 과정은 두 가지 주요 출력을 생성합니다.
- 참조 파일: 번들러 플러그인은 원래 라우트 파일(예:
posts.route.tsx)을 가져와component또는pendingComponent와 같은 속성의 값을 특수한 지연 로드 래퍼를 사용하도록 수정합니다. 이 래퍼는 나중에 번들러가 확인할 "가상" 파일을 가리킵니다. - 가상 파일: 번들러가 이러한 가상 파일 중 하나에 대한 요청(예:
posts.route.tsx?tsr-split=component)을 확인하면 요청된 속성의 코드(예:PostsComponent만)만 포함하는 새 최소 파일을 즉시 생성하도록 요청을 가로챕니다.
이 과정을 통해 원래 코드는 깔끔하고 읽기 쉬운 상태로 유지하면서 실제 번들 출력은 초기 번들 크기에 맞게 최적화됩니다.
무엇이 코드 분할되나요?
무엇을 별도의 청크로 분할할지 결정하는 것은 애플리케이션 성능 최적화에 매우 중요합니다. TanStack Router는 "분할 그룹"이라는 개념을 사용해 라우트의 여러 부분을 함께 번들링하는 방식을 결정합니다.
분할 그룹은 라우트의 여러 부분을 함께 번들링하는 방식을 TanStack Router에 알려주는 속성 배열입니다. 각 그룹은 하나의 지연 로드 청크로 함께 번들링할 속성 이름 목록입니다.
분할할 수 있는 속성은 다음과 같습니다.
componenterrorComponentpendingComponentnotFoundComponentloader
기본적으로 TanStack Router는 다음 분할 그룹을 사용합니다.
[
['component'],
['errorComponent'],
['notFoundComponent']
]
즉, 각 라우트에 대해 지연 로드되는 세 개의 별도 청크를 생성합니다. 결과는 다음과 같습니다.
- 기본 컴포넌트용 하나
- 오류 컴포넌트용 하나
- 찾을 수 없음 컴포넌트용 하나입니다.
분할 규칙
자동 코드 분할이 작동하려면 이 과정이 안정적이고 예측 가능하게 수행되도록 몇 가지 규칙을 따라야 합니다.
라우트 속성을 내보내지 않기
component, loader 등의 라우트 속성을 라우트 파일에서 내보내면 안 됩니다. 이러한 속성을 내보내면 기본 애플리케이션 번들에 번들링되어 코드 분할되지 않습니다.
export const Route = createRoute('/posts')({
// ...
notFoundComponent: PostsNotFoundComponent,
})
// ❌ Do NOT do this!
// Exporting the notFoundComponent will prevent it from being code-split
// and will be included in the main bundle.
export function PostsNotFoundComponent() {
// ❌
// ...
}
function PostsNotFoundComponent() {
// ✅
// ...
}
이것이 전부입니다. 다른 제한 사항은 없습니다. 평소처럼 라우트 파일에서 다른 JavaScript 또는 TypeScript 기능을 사용할 수 있습니다. 문제가 발생하면 GitHub에서 이슈를 열어 주세요.
세밀한 제어
대부분의 애플리케이션에서는 autoCodeSplitting: true의 기본 동작으로 충분합니다. 그러나 TanStack Router는 라우트를 청크로 분할하는 방식을 맞춤 설정하는 여러 옵션을 제공하므로, 특정 사용 사례나 성능 요구 사항에 맞게 최적화할 수 있습니다.
전역 코드 분할 동작(defaultBehavior)
번들러 플러그인 구성에서 defaultBehavior 옵션을 변경해 TanStack Router가 라우트를 분할하는 방식을 바꿀 수 있습니다. 이를 통해 라우트의 여러 속성을 함께 번들링하는 방식을 정의할 수 있습니다.
예를 들어 UI 관련 컴포넌트를 하나의 청크로 번들링하려면 다음과 같이 구성할 수 있습니다.
import { defineConfig } from 'vite'
import { tanstackRouter } from '@tanstack/router-plugin/vite'
export default defineConfig({
plugins: [
tanstackRouter({
autoCodeSplitting: true,
codeSplittingOptions: {
defaultBehavior: [
[
'component',
'pendingComponent',
'errorComponent',
'notFoundComponent',
], // Bundle all UI components together
],
},
}),
],
})
고급 프로그래밍 방식 제어(splitBehavior)
복잡한 규칙 집합에는 vite 구성의 splitBehavior 함수를 사용해 routeId를 기준으로 라우트를 청크로 분할하는 방식을 프로그래밍 방식으로 정의할 수 있습니다. 이 함수로 속성을 함께 그룹화하는 사용자 지정 로직을 구현하여 코드 분할 동작을 세밀하게 제어할 수 있습니다.
import { defineConfig } from 'vite'
import { tanstackRouter } from '@tanstack/router-plugin/vite'
export default defineConfig({
plugins: [
tanstackRouter({
autoCodeSplitting: true,
codeSplittingOptions: {
splitBehavior: ({ routeId }) => {
// For all routes under /posts, bundle the loader and component together
if (routeId.startsWith('/posts')) {
return [['loader', 'component']]
}
// All other routes will use the `defaultBehavior`
},
},
}),
],
})
라우트별 재정의(codeSplitGroupings)
가장 세밀하게 제어하려면 라우트 파일 안에 codeSplitGroupings 속성을 추가해 전역 구성을 직접 재정의할 수 있습니다. 고유한 최적화 요구 사항이 있는 라우트에 유용합니다.
import { loadPostsData } from './-heavy-posts-utils'
export const Route = createFileRoute('/posts')({
// For this specific route, bundle the loader and component together.
codeSplitGroupings: [['loader', 'component']],
loader: () => loadPostsData(),
component: PostsComponent,
})
function PostsComponent() {
// ...
}
이렇게 하면 특정 라우트의 loader와 component를 모두 포함하는 하나의 청크가 생성되며, 기본 동작과 번들러 구성에 정의된 프로그래밍 방식의 분할 동작이 모두 재정의됩니다.
구성 순서가 중요합니다
이 가이드에서는 지금까지 TanStack Router가 라우트를 청크로 분할하는 방식을 구성하는 세 가지 방법을 설명했습니다.
서로 다른 구성이 충돌하지 않도록 TanStack Router는 다음 우선순위를 사용합니다.
- 라우트별 재정의: 라우트 파일 내부의
codeSplitGroupings속성이 가장 높은 우선순위를 가집니다. 이를 통해 개별 라우트에 특정 분할 그룹을 정의할 수 있습니다. - 프로그래밍 방식의 분할 동작: 번들러 구성의
splitBehavior함수로routeId를 기준으로 라우트를 분할하는 사용자 지정 로직을 정의할 수 있습니다. - 기본 동작: 번들러 구성의
defaultBehavior옵션은 특정 재정의나 사용자 지정 로직이 정의되지 않은 라우트의 대체 동작입니다. 재정의하지 않는 한 모든 라우트에 적용되는 기본 구성입니다.
데이터 로더 분할
loader 함수는 라우트에 필요한 데이터를 가져옵니다. 기본적으로 "참조 파일"에 함께 번들링되어 초기 번들에서 로드됩니다. 하지만 더 최적화하려는 경우 loader를 자체 청크로 분할할 수도 있습니다.
[!CAUTION]
loader를 자체 청크로 옮기는 것은 성능상의 절충입니다. 데이터를 가져오기 전에 서버로 추가 요청이 발생하므로 초기 페이지 로드가 느려질 수 있습니다. 라우트가 컴포넌트를 렌더링하기 전에loader를 반드시 가져와 실행해야 하기 때문입니다. 따라서 분할해야 할 구체적인 이유가 없다면loader를 초기 번들에 유지하는 것이 좋습니다.
// vite.config.ts
import { defineConfig } from 'vite'
import { tanstackRouter } from '@tanstack/router-plugin/vite'
export default defineConfig({
plugins: [
tanstackRouter({
autoCodeSplitting: true,
codeSplittingOptions: {
defaultBehavior: [
['loader'], // The loader will be in its own chunk
['component'],
// ... other component groupings
],
},
}),
],
})
이를 필요로 하는 구체적인 사용 사례가 없다면 loader를 분할하지 않는 것을 강력히 권장합니다. 대부분의 경우 loader를 분리하지 않고 기본 번들에 유지하는 것이 성능에 가장 좋습니다.