코드 분할
코드 분할과 지연 로딩은 애플리케이션의 번들 크기와 로드 성능을 개선하는 강력한 기법입니다.
- 초기 페이지 로드 시 로드해야 하는 코드의 양을 줄입니다.
- 필요한 시점에 코드를 필요에 따라 로드합니다.
- 더 작은 청크가 많아지므로 브라우저가 더 쉽게 캐시할 수 있습니다.
TanStack Router는 코드를 어떻게 분할하나요?
TanStack Router는 코드를 두 가지 범주로 나눕니다.
-
중요 라우트 설정 - 현재 라우트를 렌더링하고 데이터 로딩 프로세스를 가능한 한 일찍 시작하는 데 필요한 코드입니다.
- 경로 파싱/직렬화
- 검색 매개변수 검증
- 로더, 로드 전
- 라우트 컨텍스트
- 정적 데이터
- 링크
- 스크립트
- 스타일
- 아래에 나열되지 않은 기타 모든 라우트 설정
-
비중요/지연 라우트 설정 - 라우트를 매칭하는 데 필요하지 않아 필요에 따라 로드할 수 있는 코드입니다.
- 라우트 컴포넌트
- 오류 컴포넌트
- 대기 컴포넌트
- Not-found 컴포넌트
🧠 로더는 왜 분할하지 않나요?
로더는 이미 비동기 경계이므로 청크를 가져오는 동시에 로더가 실행되기를 기다리느라 비용을 두 번 지불하게 됩니다.
일반적으로 컴포넌트보다 큰 번들 크기를 유발할 가능성이 낮습니다.
로더는 라우트에서 프리로드할 수 있는 가장 중요한 에셋 중 하나입니다. 특히 링크 위에 마우스를 올리는 것과 같은 기본 프리로드 의도를 사용하는 경우 추가적인 비동기 오버헤드 없이 로더를 사용할 수 있어야 합니다.
로더를 분할할 때의 단점을 알고도 계속 진행하려면 데이터 로더 분할 섹션으로 이동합니다.
라우트 파일을 디렉터리로 캡슐화
TanStack Router의 파일 기반 라우팅 시스템은 평면 및 중첩 파일 구조를 모두 지원하도록 설계되었으므로 추가 설정 없이 라우트 파일을 단일 디렉터리로 캡슐화할 수 있습니다.
라우트 파일을 디렉터리로 캡슐화하려면 라우트 파일과 같은 이름의 디렉터리 안에 라우트 파일 자체를 .route 파일로 이동합니다.
예를 들어 posts.tsx라는 라우트 파일이 있다면 posts라는 새 디렉터리를 만들고 posts.tsx 파일을 해당 디렉터리로 이동한 뒤 route.tsx로 이름을 변경합니다.
이전
posts.tsx
이후
postsroute.tsx
코드 분할 접근 방식
TanStack Router는 여러 코드 분할 접근 방식을 지원합니다. 코드 기반 라우팅을 사용하는 경우 코드 기반 분할 섹션으로 건너뜁니다.
파일 기반 라우팅을 사용하는 경우 다음 코드 분할 접근 방식을 사용할 수 있습니다.
자동 코드 분할 사용✨
라우트 파일을 코드 분할하는 가장 쉽고 강력한 방법입니다.
autoCodeSplitting 기능을 사용하면 TanStack Router는 위에서 설명한 비중요 라우트 설정을 기준으로 라우트 파일을 자동으로 코드 분할합니다.
[!IMPORTANT] 자동 코드 분할 기능은 지원되는 번들러 중 하나와 함께 파일 기반 라우팅을 사용하는 경우에만 사용할 수 있습니다. CLI(
@tanstack/router-cli)만 사용하는 경우에는 작동하지 않습니다.
자동 코드 분할을 활성화하려면 TanStack Router Bundler Plugin 설정에 다음을 추가하면 됩니다.
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { tanstackRouter } from '@tanstack/router-plugin/vite'
export default defineConfig({
plugins: [
tanstackRouter({
// ...
autoCodeSplitting: true,
}),
react(), // Make sure to add this plugin after the TanStack Router Bundler plugin
],
})
이것으로 완료됩니다. TanStack Router는 중요 및 비중요 라우트 설정에 따라 모든 라우트 파일을 자동으로 코드 분할합니다.
코드 분할 프로세스를 더 세밀하게 제어하려면 자동 코드 분할 가이드에서 사용 가능한 옵션을 확인합니다.
.lazy.tsx 접미사 사용
자동 코드 분할 기능을 사용할 수 없어도 .lazy.tsx 접미사를 사용해 라우트 파일을 코드 분할할 수 있습니다. 코드를 .lazy.tsx 접미사가 붙은 별도 파일로 이동하고 createLazyFileRoute 함수를 사용해 createFileRoute를 대체하는 것만큼 쉽습니다.
[!IMPORTANT]
__root.tsx라우트 파일은createRootRoute또는createRootRouteWithContext를 사용하더라도 현재 라우트와 관계없이 항상 렌더링되므로 코드 분할을 지원하지 않습니다.
다음은 createLazyFileRoute가 지원하는 유일한 옵션입니다.
| 내보내기 이름 | 설명 |
|---|---|
component | 라우트에 렌더링할 컴포넌트입니다. |
errorComponent | 라우트를 로드하는 중 오류가 발생했을 때 렌더링할 컴포넌트입니다. |
pendingComponent | 라우트를 로드하는 동안 렌더링할 컴포넌트입니다. |
notFoundComponent | 찾을 수 없음 오류가 던져졌을 때 렌더링할 컴포넌트입니다. |
.lazy.tsx를 사용한 코드 분할 예시
.lazy.tsx를 사용하는 경우 코드 분할을 활성화하기 위해 라우트를 두 파일로 나눌 수 있습니다.
이전(단일 파일)
React
import { createFileRoute } from '@tanstack/react-router'
import { fetchPosts } from './api'
export const Route = createFileRoute('/posts')({
loader: fetchPosts,
component: Posts,
})
function Posts() {
// ...
}
Solid
import { createFileRoute } from '@tanstack/solid-router'
import { fetchPosts } from './api'
export const Route = createFileRoute('/posts')({
loader: fetchPosts,
component: Posts,
})
function Posts() {
// ...
}
이후(두 파일로 분할)
이 파일에는 중요 라우트 설정이 포함됩니다.
React
import { createFileRoute } from '@tanstack/react-router'
import { fetchPosts } from './api'
export const Route = createFileRoute('/posts')({
loader: fetchPosts,
})
Solid
import { createFileRoute } from '@tanstack/solid-router'
import { fetchPosts } from './api'
export const Route = createFileRoute('/posts')({
loader: fetchPosts,
})
비중요 라우트 설정은 .lazy.tsx 접미사가 붙은 파일에 넣습니다.
React
import { createLazyFileRoute } from '@tanstack/react-router'
export const Route = createLazyFileRoute('/posts')({
component: Posts,
})
function Posts() {
// ...
}
Solid
import { createLazyFileRoute } from '@tanstack/solid-router'
export const Route = createLazyFileRoute('/posts')({
component: Posts,
})
function Posts() {
// ...
}
가상 라우트 사용
라우트 파일에서 모든 내용을 분리해 파일이 비어버리는 상황이 발생할 수 있습니다. 이 경우 라우트 파일을 완전히 삭제하면 됩니다. 코드 분할 파일의 앵커 역할을 하는 가상 라우트가 자동으로 생성됩니다. 이 가상 라우트는 생성된 라우트 트리 파일에 직접 포함됩니다.
이전(가상 라우트)
React
import { createFileRoute } from '@tanstack/react-router'
export const Route = createFileRoute('/posts')({
// Hello?
})
import { createLazyFileRoute } from '@tanstack/react-router'
export const Route = createLazyFileRoute('/posts')({
component: Posts,
})
function Posts() {
// ...
}
Solid
import { createFileRoute } from '@tanstack/solid-router'
export const Route = createFileRoute('/posts')({
// Hello?
})
import { createLazyFileRoute } from '@tanstack/solid-router'
export const Route = createLazyFileRoute('/posts')({
component: Posts,
})
function Posts() {
// ...
}
이후(가상 라우트)
React
import { createLazyFileRoute } from '@tanstack/react-router'
export const Route = createLazyFileRoute('/posts')({
component: Posts,
})
function Posts() {
// ...
}
Solid
import { createLazyFileRoute } from '@tanstack/solid-router'
export const Route = createLazyFileRoute('/posts')({
component: Posts,
})
function Posts() {
// ...
}
짜잔! 🎉
코드 기반 분할
코드 기반 라우팅을 사용하는 경우에도 Route.lazy() 메서드와 createLazyRoute 함수를 사용해 라우트를 코드 분할할 수 있습니다. 라우트 설정을 두 부분으로 나눠야 합니다.
createLazyRoute 함수를 사용해 지연 라우트를 생성합니다.
export const Route = createLazyRoute('/posts')({
component: MyComponent,
})
function MyComponent() {
return <div>My Component</div>
}
그런 다음 라우트 정의에서 .lazy 메서드를 호출해 app.tsx에 비중요 라우트 설정과 함께 지연/코드 분할 라우트를 가져옵니다.
const postsRoute = createRoute({
getParentRoute: () => rootRoute,
path: '/posts',
}).lazy(() => import('./posts.lazy').then((d) => d.Route))
데이터 로더 분할
주의해야 합니다!!! 라우트 로더를 분할하는 것은 위험할 수 있습니다.
번들 크기를 줄이는 강력한 도구가 될 수 있지만, TanStack Router는 코드를 어떻게 분할하나요? 섹션에서 설명한 비용이 따릅니다.
Route의 loader 옵션을 사용해 데이터 로딩 로직을 코드 분할할 수 있습니다. 이 과정에서는 로더에 전달되는 매개변수의 타입 안전성을 유지하기 어려워지지만, 일반 LoaderContext 타입을 사용하면 대부분의 문제를 해결할 수 있습니다.
React
import { lazyFn } from '@tanstack/react-router'
const route = createRoute({
path: '/my-route',
component: MyComponent,
loader: lazyFn(() => import('./loader'), 'loader'),
})
// In another file...a
export const loader = async (context: LoaderContext) => {
/// ...
}
Solid
import { lazyFn } from '@tanstack/solid-router'
const route = createRoute({
path: '/my-route',
component: MyComponent,
loader: lazyFn(() => import('./loader'), 'loader'),
})
// In another file...a
export const loader = async (context: LoaderContext) => {
/// ...
}
파일 기반 라우팅을 사용하는 경우 사용자 지정 번들링 옵션과 함께 자동 코드 분할을 사용할 때만 loader를 분할할 수 있습니다.
getRouteApi 헬퍼로 다른 파일에서 라우트 API에 수동으로 접근
예상했겠지만 컴포넌트 코드를 라우트와 다른 파일에 배치하면 라우트 자체를 사용하기 어려울 수 있습니다. 이를 돕기 위해 TanStack Router는 라우트 자체를 가져오지 않고도 파일에서 라우트의 타입 안전 API에 접근할 수 있는 편리한 getRouteApi 함수를 내보냅니다.
React
import { createRoute } from '@tanstack/react-router'
import { MyComponent } from './MyComponent'
const route = createRoute({
path: '/my-route',
loader: () => ({
foo: 'bar',
}),
component: MyComponent,
})
import { getRouteApi } from '@tanstack/react-router'
const route = getRouteApi('/my-route')
export function MyComponent() {
const loaderData = route.useLoaderData()
// ^? { foo: string }
return <div>...</div>
}
Solid
import { createRoute } from '@tanstack/solid-router'
import { MyComponent } from './MyComponent'
const route = createRoute({
path: '/my-route',
loader: () => ({
foo: 'bar',
}),
component: MyComponent,
})
import { getRouteApi } from '@tanstack/solid-router'
const route = getRouteApi('/my-route')
export function MyComponent() {
const loaderData = route.useLoaderData()
// ^? { foo: string }
return <div>...</div>
}
getRouteApi 함수는 다음과 같은 다른 타입 안전 API에 접근할 때 유용합니다.
useLoaderDatauseLoaderDepsuseMatchuseParamsuseRouteContextuseSearch