본문으로 건너뛰기

가상 파일 라우트

가상 파일 라우트 개념을 개척한 Remix 팀에 감사드립니다. Remix 팀의 작업에서 영감을 얻어 TanStack Router의 기존 파일 기반 라우트 트리 생성과 함께 작동하도록 적용했습니다.

가상 파일 라우트는 프로젝트의 실제 파일을 참조하는 코드로 라우트 트리를 프로그래밍 방식으로 만들 수 있는 강력한 개념입니다. 다음과 같은 경우에 유용합니다.

  • 유지하려는 기존 라우트 구성이 있습니다.
  • 라우트 파일의 위치를 사용자 지정하려고 합니다.
  • TanStack Router의 파일 기반 라우트 생성을 완전히 재정의하고 고유한 규칙을 만들려고 합니다.

다음은 가상 파일 라우트를 사용해 라우트 트리를 프로젝트의 실제 파일 집합에 매핑하는 간단한 예제입니다.

// routes.ts
import {
rootRoute,
route,
index,
layout,
physical,
} from '@tanstack/virtual-file-routes'

export const routes = rootRoute('root.tsx', [
index('index.tsx'),
layout('pathlessLayout.tsx', [
route('/dashboard', 'app/dashboard.tsx', [
index('app/dashboard-index.tsx'),
route('/invoices', 'app/dashboard-invoices.tsx', [
index('app/invoices-index.tsx'),
route('$id', 'app/invoice-detail.tsx'),
]),
]),
physical('/posts', 'posts'),
]),
])

구성

가상 파일 라우트는 다음 중 하나를 통해 구성할 수 있습니다.

  • Vite/Rspack/Webpack용 TanStackRouter 플러그인
  • TanStack Router CLI용 tsr.config.json 파일

TanStackRouter 플러그인을 통한 구성

Vite/Rspack/Webpack용 TanStackRouter 플러그인을 사용한다면 플러그인을 설정할 때 라우트 파일 경로를 virtualRoutesConfig 옵션으로 전달해 가상 파일 라우트를 구성할 수 있습니다.

React

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({
target: 'react',
virtualRouteConfig: './routes.ts',
}),
react(),
],
})

Solid

vite.config.ts
import { defineConfig } from 'vite'
import solid from 'vite-plugin-solid'
import { tanstackRouter } from '@tanstack/router-plugin/vite'

export default defineConfig({
plugins: [
tanstackRouter({
target: 'solid',
virtualRouteConfig: './routes.ts',
}),
solid(),
],
})

또는 구성에서 가상 라우트를 직접 정의할 수 있습니다.

// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { tanstackRouter } from '@tanstack/router-plugin/vite'
import { rootRoute } from '@tanstack/virtual-file-routes'

const routes = rootRoute('root.tsx', [
// ... the rest of your virtual route tree
])

export default defineConfig({
plugins: [tanstackRouter({ virtualRouteConfig: routes }), react()],
})

React

vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { tanstackRouter } from '@tanstack/router-plugin/vite'

const routes = rootRoute('root.tsx', [
// ... the rest of your virtual route tree
])

export default defineConfig({
plugins: [
tanstackRouter({ virtualRouteConfig: routes, target: 'react' }),
react(),
],
})

Solid

vite.config.ts
import { defineConfig } from 'vite'
import solid from 'vite-plugin-solid'
import { tanstackRouter } from '@tanstack/router-plugin/vite'

const routes = rootRoute('root.tsx', [
// ... the rest of your virtual route tree
])

export default defineConfig({
plugins: [
tanstackRouter({ virtualRouteConfig: routes, target: 'solid' }),
solid(),
],
})

가상 파일 라우트 만들기

가상 파일 라우트를 만들려면 @tanstack/virtual-file-routes 패키지를 import해야 합니다. 이 패키지는 프로젝트의 실제 파일을 참조하는 가상 라우트를 만들 수 있는 함수 집합을 제공합니다. 패키지에서 다음과 같은 유틸리티 함수를 export합니다.

  • rootRoute - 가상 루트 라우트를 만듭니다.
  • route - 가상 라우트를 만듭니다.
  • index - 가상 인덱스 라우트를 만듭니다.
  • layout - 가상 경로 없는 레이아웃 라우트를 만듭니다.
  • physical - 물리적 가상 라우트를 만듭니다(자세한 내용은 뒤에서 설명합니다).

가상 루트 라우트

rootRoute 함수는 가상 루트 라우트를 만드는 데 사용합니다. 파일 이름과 children 라우트 배열을 받습니다. 다음은 가상 루트 라우트의 예제입니다.

// routes.ts
import { rootRoute } from '@tanstack/virtual-file-routes'

export const routes = rootRoute('root.tsx', [
// ... children routes
])

가상 라우트

route 함수는 가상 라우트를 만드는 데 사용합니다. 경로, 파일 이름, children 라우트 배열을 받습니다. 다음은 가상 라우트의 예제입니다.

// routes.ts
import { route } from '@tanstack/virtual-file-routes'

export const routes = rootRoute('root.tsx', [
route('/about', 'about.tsx', [
// ... children routes
]),
])

route는 명시적인 URL 경로를 받으므로 앞뒤 밑줄을 문자 그대로 처리합니다. 경로 없는 레이아웃 라우트를 만들려면 layout을 사용합니다.

파일 이름 없이 가상 라우트를 정의할 수도 있습니다. 이렇게 하면 children에 공통 경로 접두사를 설정할 수 있습니다.

// routes.ts
import { route } from '@tanstack/virtual-file-routes'

export const routes = rootRoute('root.tsx', [
route('/hello', [
route('/world', 'world.tsx'), // full path will be "/hello/world"
route('/universe', 'universe.tsx'), // full path will be "/hello/universe"
]),
])

가상 인덱스 라우트

index 함수는 가상 인덱스 라우트를 만드는 데 사용합니다. 파일 이름을 받습니다. 다음은 가상 인덱스 라우트의 예제입니다.

import { index } from '@tanstack/virtual-file-routes'

const routes = rootRoute('root.tsx', [index('index.tsx')])

가상 경로 없는 라우트

layout 함수는 가상 경로 없는 라우트를 만드는 데 사용합니다. 파일 이름, children 라우트 배열, 선택적인 경로 없는 ID를 받습니다. 다음은 가상 경로 없는 라우트의 예제입니다.

// routes.ts
import { layout } from '@tanstack/virtual-file-routes'

export const routes = rootRoute('root.tsx', [
layout('pathlessLayout.tsx', [
// ... children routes
]),
])

경로 없는 ID를 지정해 파일 이름과 다른 고유 식별자를 라우트에 부여할 수도 있습니다.

// routes.ts
import { layout } from '@tanstack/virtual-file-routes'

export const routes = rootRoute('root.tsx', [
layout('my-pathless-layout-id', 'pathlessLayout.tsx', [
// ... children routes
]),
])

물리적 가상 라우트

물리적 가상 라우트는 오래된 TanStack Router 파일 기반 라우팅 규칙의 디렉터리를 특정 URL 경로 아래에 "마운트"하는 방법입니다. 라우트 트리 상위의 작은 부분을 가상 라우트로 사용자 지정하면서 하위 라우트와 디렉터리에는 표준 파일 기반 라우팅 규칙을 사용하려는 경우에 유용합니다.

다음 파일 구조를 살펴보겠습니다.

/routes
├── root.tsx
├── index.tsx
├── pathlessLayout.tsx
├── app
│ ├── dashboard.tsx
│ ├── dashboard-index.tsx
│ ├── dashboard-invoices.tsx
│ ├── invoices-index.tsx
│ ├── invoice-detail.tsx
└── posts
├── index.tsx
├── $postId.tsx
├── $postId.edit.tsx
├── comments/
│ ├── index.tsx
│ ├── $commentId.tsx
└── likes/
├── index.tsx
├── $likeId.tsx

posts를 제외한 모든 항목에 가상 라우트를 사용해 라우트 트리를 사용자 지정한 다음, 물리적 가상 라우트를 사용해 /posts 경로 아래에 posts 디렉터리를 마운트하겠습니다.

// routes.ts
export const routes = rootRoute('root.tsx', [
// Set up your virtual routes as normal
index('index.tsx'),
layout('pathlessLayout.tsx', [
route('/dashboard', 'app/dashboard.tsx', [
index('app/dashboard-index.tsx'),
route('/invoices', 'app/dashboard-invoices.tsx', [
index('app/invoices-index.tsx'),
route('$id', 'app/invoice-detail.tsx'),
]),
]),
// Mount the `posts` directory under the `/posts` path
physical('/posts', 'posts'),
]),
])

현재 수준에서 물리적 라우트 병합

빈 경로 접두사(또는 인수 하나)와 함께 physical을 사용해 경로 접두사를 추가하지 않고 물리적 디렉터리의 라우트를 현재 수준에서 직접 병합할 수도 있습니다. 라우트를 별도의 디렉터리로 구성하면서 동일한 URL 수준에 표시하려는 경우에 유용합니다.

다음 파일 구조를 살펴보겠습니다.

/routes
├── __root.tsx
├── about.tsx
└── features
├── index.tsx
└── contact.tsx

features 디렉터리의 라우트를 루트 수준에서 병합할 수 있습니다.

// routes.ts
import { physical, rootRoute, route } from '@tanstack/virtual-file-routes'

export const routes = rootRoute('__root.tsx', [
route('/about', 'about.tsx'),
// Merge features/ routes at root level (no path prefix)
physical('features'),
// Or equivalently: physical('', 'features')
])

그러면 다음 라우트가 생성됩니다.

  • /about - about.tsx에서 생성
  • / - features/index.tsx에서 생성
  • /contact - features/contact.tsx에서 생성

참고: 동일한 수준에서 병합할 때 가상 라우트와 물리적 디렉터리 라우트 사이에 충돌하는 라우트 경로가 없는지 확인합니다. 충돌이 발생하면(예: 둘 다 /about 라우트를 가지는 경우) 생성기가 오류를 throw합니다.

TanStack Router 파일 기반 라우팅 내부의 가상 라우트

앞 섹션에서는 가상 라우트 구성 내부에서 TanStack Router의 파일 기반 라우팅 규칙을 사용하는 방법을 살펴보았습니다. 반대의 구성도 가능합니다.
TanStack Router의 파일 기반 라우팅 규칙으로 앱 라우트 트리의 주요 부분을 구성하고 특정 하위 트리에는 가상 라우트 구성을 선택해 사용할 수 있습니다.

다음 파일 구조를 살펴보겠습니다.

/routes
├── __root.tsx
├── foo
│ ├── bar
│ │ ├── __virtual.ts
│ │ ├── details.tsx
│ │ ├── home.tsx
│ │ └── route.ts
│ └── bar.tsx
└── index.tsx

__virtual.ts라는 특수 파일이 있는 bar 디렉터리를 살펴보겠습니다. 이 파일은 생성기에 이 디렉터리(및 하위 디렉터리)의 가상 파일 라우트 구성으로 전환하도록 지시합니다.

__virtual.ts는 라우트 트리의 해당 하위 트리에 대한 가상 라우트를 구성합니다. 위에서 설명한 것과 동일한 API를 사용하지만, 해당 하위 트리에는 rootRoute를 정의하지 않는다는 차이점이 있습니다.

// routes/foo/bar/__virtual.ts
import {
defineVirtualSubtreeConfig,
index,
route,
} from '@tanstack/virtual-file-routes'

export default defineVirtualSubtreeConfig([
index('home.tsx'),
route('$id', 'details.tsx'),
])

헬퍼 함수 defineVirtualSubtreeConfig는 Vite의 defineConfig를 본떠 만들었으며 default export로 하위 트리 구성을 정의할 수 있습니다. default export는 다음 중 하나일 수 있습니다.

  • 하위 트리 구성 객체
  • 하위 트리 구성 객체를 반환하는 함수
  • 하위 트리 구성 객체를 반환하는 비동기 함수

인셉션

TanStack Router의 파일 기반 라우팅 규칙과 가상 라우트 구성을 원하는 방식으로 조합할 수 있습니다.
더 깊이 살펴보겠습니다!
다음 예제는 파일 기반 라우팅 규칙으로 시작해 /posts에는 가상 라우트 구성으로 전환하고, /posts/lets-go에는 다시 파일 기반 라우팅 규칙으로 전환한 다음 /posts/lets-go/deeper에는 다시 가상 라우트 구성으로 전환합니다.

├── __root.tsx
├── index.tsx
├── posts
│ ├── __virtual.ts
│ ├── details.tsx
│ ├── home.tsx
│ └── lets-go
│ ├── deeper
│ │ ├── __virtual.ts
│ │ └── home.tsx
│ └── index.tsx
└── posts.tsx

TanStack Router CLI를 통한 구성

TanStack Router CLI를 사용한다면 tsr.config.json 파일에 라우트 파일 경로를 정의해 가상 파일 라우트를 구성할 수 있습니다.

// tsr.config.json
{
"virtualRouteConfig": "./routes.ts"
}

또는 구성에서 가상 라우트를 직접 정의할 수 있습니다. 덜 일반적인 방법이지만 tsr.config.json 파일에 virtualRouteConfig 객체를 추가하고 가상 라우트를 정의한 다음, @tanstack/virtual-file-routes 패키지의 실제 rootRoute/route/index/etc 함수를 호출해 생성된 JSON을 전달하면 TanStack Router CLI를 통해 구성할 수 있습니다.

// tsr.config.json
{
"virtualRouteConfig": {
"type": "root",
"file": "root.tsx",
"children": [
{
"type": "index",
"file": "home.tsx"
},
{
"type": "route",
"file": "posts/posts.tsx",
"path": "/posts",
"children": [
{
"type": "index",
"file": "posts/posts-home.tsx"
},
{
"type": "route",
"file": "posts/posts-detail.tsx",
"path": "$postId"
}
]
},
{
"type": "layout",
"id": "first",
"file": "layout/first-pathless-layout.tsx",
"children": [
{
"type": "layout",
"id": "second",
"file": "layout/second-pathless-layout.tsx",
"children": [
{
"type": "route",
"file": "a.tsx",
"path": "/route-a"
},
{
"type": "route",
"file": "b.tsx",
"path": "/route-b"
}
]
}
]
}
]
}
}