본문으로 건너뛰기

코드 분할

코드 분할과 지연 로딩은 애플리케이션의 번들 크기와 로드 성능을 개선하는 강력한 기법입니다.

  • 초기 페이지 로드 시 로드해야 하는 코드의 양을 줄입니다.
  • 필요한 시점에 코드를 필요에 따라 로드합니다.
  • 더 작은 청크가 많아지므로 브라우저가 더 쉽게 캐시할 수 있습니다.

TanStack Router는 코드를 어떻게 분할하나요?​

TanStack Router는 코드를 두 가지 범주로 나눕니다.

  • 중요 라우트 설정 - 현재 라우트를 렌더링하고 데이터 로딩 프로세스를 가능한 한 일찍 시작하는 데 필요한 코드입니다.

    • 경로 파싱/직렬화
    • 검색 매개변수 검증
    • 로더, 로드 전
    • 라우트 컨텍스트
    • 정적 데이터
    • 링크
    • 스크립트
    • 스타일
    • 아래에 나열되지 않은 기타 모든 라우트 설정
  • 비중요/지연 라우트 설정 - 라우트를 매칭하는 데 필요하지 않아 필요에 따라 로드할 수 있는 코드입니다.

    • 라우트 컴포넌트
    • 오류 컴포넌트
    • 대기 컴포넌트
    • Not-found 컴포넌트

🧠 로더는 왜 분할하지 않나요?

  • 로더는 이미 비동기 경계이므로 청크를 가져오는 동시에 로더가 실행되기를 기다리느라 비용을 두 번 지불하게 됩니다.

  • 일반적으로 컴포넌트보다 큰 번들 크기를 유발할 가능성이 낮습니다.

  • 로더는 라우트에서 프리로드할 수 있는 가장 중요한 에셋 중 하나입니다. 특히 링크 위에 마우스를 올리는 것과 같은 기본 프리로드 의도를 사용하는 경우 추가적인 비동기 오버헤드 없이 로더를 사용할 수 있어야 합니다.

    로더를 분할할 때의 단점을 알고도 계속 진행하려면 데이터 로더 분할 섹션으로 이동합니다.

라우트 파일을 디렉터리로 캡슐화​

TanStack Router의 파일 기반 라우팅 시스템은 평면 및 중첩 파일 구조를 모두 지원하도록 설계되었으므로 추가 설정 없이 라우트 파일을 단일 디렉터리로 캡슐화할 수 있습니다.

라우트 파일을 디렉터리로 캡슐화하려면 라우트 파일과 같은 이름의 디렉터리 안에 라우트 파일 자체를 .route 파일로 이동합니다.

예를 들어 posts.tsx라는 라우트 파일이 있다면 posts라는 새 디렉터리를 만들고 posts.tsx 파일을 해당 디렉터리로 이동한 뒤 route.tsx로 이름을 변경합니다.

이전

  • posts.tsx

이후

  • posts
    • route.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를 사용하는 경우 코드 분할을 활성화하기 위해 라우트를 두 파일로 나눌 수 있습니다.

이전(단일 파일)

<!-- ::start:framework -->

React

src/routes/posts.tsx
import { createFileRoute } from '@tanstack/react-router'
import { fetchPosts } from './api'

export const Route = createFileRoute('/posts')({
loader: fetchPosts,
component: Posts,
})

function Posts() {
// ...
}

Solid

src/routes/posts.tsx
import { createFileRoute } from '@tanstack/solid-router'
import { fetchPosts } from './api'

export const Route = createFileRoute('/posts')({
loader: fetchPosts,
component: Posts,
})

function Posts() {
// ...
}

<!-- ::end:framework -->

이후(두 파일로 분할)

이 파일에는 중요 라우트 설정이 포함됩니다.

<!-- ::start:framework -->

React

src/routes/posts.tsx
import { createFileRoute } from '@tanstack/react-router'
import { fetchPosts } from './api'

export const Route = createFileRoute('/posts')({
loader: fetchPosts,
})

Solid

src/routes/posts.tsx
import { createFileRoute } from '@tanstack/solid-router'
import { fetchPosts } from './api'

export const Route = createFileRoute('/posts')({
loader: fetchPosts,
})

<!-- ::end:framework -->

비중요 라우트 설정은 .lazy.tsx 접미사가 붙은 파일에 넣습니다.

<!-- ::start:framework -->

React

src/routes/posts.lazy.tsx
import { createLazyFileRoute } from '@tanstack/react-router'

export const Route = createLazyFileRoute('/posts')({
component: Posts,
})

function Posts() {
// ...
}

Solid

src/routes/posts.lazy.tsx
import { createLazyFileRoute } from '@tanstack/solid-router'

export const Route = createLazyFileRoute('/posts')({
component: Posts,
})

function Posts() {
// ...
}

<!-- ::end:framework -->

가상 라우트 사용​

라우트 파일에서 모든 내용을 분리해 파일이 비어버리는 상황이 발생할 수 있습니다. 이 경우 라우트 파일을 완전히 삭제하면 됩니다. 코드 분할 파일의 앵커 역할을 하는 가상 라우트가 자동으로 생성됩니다. 이 가상 라우트는 생성된 라우트 트리 파일에 직접 포함됩니다.

이전(가상 라우트)

<!-- ::start:framework -->

React

<!-- ::start:tabs variant="files" -->

src/routes/posts.tsx
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/posts')({
// Hello?
})
src/routes/posts.lazy.tsx
import { createLazyFileRoute } from '@tanstack/react-router'

export const Route = createLazyFileRoute('/posts')({
component: Posts,
})

function Posts() {
// ...
}

<!-- ::end:tabs -->

Solid

<!-- ::start:tabs variant="files" -->

src/routes/posts.tsx
import { createFileRoute } from '@tanstack/solid-router'

export const Route = createFileRoute('/posts')({
// Hello?
})
src/routes/posts.lazy.tsx
import { createLazyFileRoute } from '@tanstack/solid-router'

export const Route = createLazyFileRoute('/posts')({
component: Posts,
})

function Posts() {
// ...
}

<!-- ::end:tabs -->

<!-- ::end:framework -->

이후(가상 라우트)

<!-- ::start:framework -->

React

src/routes/posts.lazy.tsx
import { createLazyFileRoute } from '@tanstack/react-router'

export const Route = createLazyFileRoute('/posts')({
component: Posts,
})

function Posts() {
// ...
}

Solid

src/routes/posts.lazy.tsx
import { createLazyFileRoute } from '@tanstack/solid-router'

export const Route = createLazyFileRoute('/posts')({
component: Posts,
})

function Posts() {
// ...
}

<!-- ::end:framework -->

짜잔! 🎉

코드 기반 분할​

코드 기반 라우팅을 사용하는 경우에도 Route.lazy() 메서드와 createLazyRoute 함수를 사용해 라우트를 코드 분할할 수 있습니다. 라우트 설정을 두 부분으로 나눠야 합니다.

createLazyRoute 함수를 사용해 지연 라우트를 생성합니다.

src/posts.lazy.tsx
export const Route = createLazyRoute('/posts')({
component: MyComponent,
})

function MyComponent() {
return <div>My Component</div>
}

그런 다음 라우트 정의에서 .lazy 메서드를 호출해 app.tsx에 비중요 라우트 설정과 함께 지연/코드 분할 라우트를 가져옵니다.

src/app.tsx
const postsRoute = createRoute({
getParentRoute: () => rootRoute,
path: '/posts',
}).lazy(() => import('./posts.lazy').then((d) => d.Route))

데이터 로더 분할​

주의해야 합니다!!! 라우트 로더를 분할하는 것은 위험할 수 있습니다.

번들 크기를 줄이는 강력한 도구가 될 수 있지만, TanStack Router는 코드를 어떻게 분할하나요? 섹션에서 설명한 비용이 따릅니다.

Route의 loader 옵션을 사용해 데이터 로딩 로직을 코드 분할할 수 있습니다. 이 과정에서는 로더에 전달되는 매개변수의 타입 안전성을 유지하기 어려워지지만, 일반 LoaderContext 타입을 사용하면 대부분의 문제를 해결할 수 있습니다.

<!-- ::start:framework -->

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) => {
/// ...
}

<!-- ::end:framework -->

파일 기반 라우팅을 사용하는 경우 사용자 지정 번들링 옵션과 함께 자동 코드 분할을 사용할 때만 loader를 분할할 수 있습니다.

getRouteApi 헬퍼로 다른 파일에서 라우트 API에 수동으로 접근​

예상했겠지만 컴포넌트 코드를 라우트와 다른 파일에 배치하면 라우트 자체를 사용하기 어려울 수 있습니다. 이를 돕기 위해 TanStack Router는 라우트 자체를 가져오지 않고도 파일에서 라우트의 타입 안전 API에 접근할 수 있는 편리한 getRouteApi 함수를 내보냅니다.

<!-- ::start:framework -->

React

<!-- ::start:tabs variant="files" -->

src/my-route.tsx
import { createRoute } from '@tanstack/react-router'
import { MyComponent } from './MyComponent'

const route = createRoute({
path: '/my-route',
loader: () => ({
foo: 'bar',
}),
component: MyComponent,
})
src/MyComponent.tsx
import { getRouteApi } from '@tanstack/react-router'

const route = getRouteApi('/my-route')

export function MyComponent() {
const loaderData = route.useLoaderData()
// ^? { foo: string }

return <div>...</div>
}

<!-- ::end:tabs -->

Solid

<!-- ::start:tabs variant="files" -->

src/my-route.tsx
import { createRoute } from '@tanstack/solid-router'
import { MyComponent } from './MyComponent'

const route = createRoute({
path: '/my-route',
loader: () => ({
foo: 'bar',
}),
component: MyComponent,
})
src/MyComponent.tsx
import { getRouteApi } from '@tanstack/solid-router'

const route = getRouteApi('/my-route')

export function MyComponent() {
const loaderData = route.useLoaderData()
// ^? { foo: string }

return <div>...</div>
}

<!-- ::end:tabs -->

<!-- ::end:framework -->

getRouteApi 함수는 다음과 같은 다른 타입 안전 API에 접근할 때 유용합니다.

  • useLoaderData
  • useLoaderDeps
  • useMatch
  • useParams
  • useRouteContext
  • useSearch