티스토리 뷰

REACT

Tanstack Router 사용하기 02

송우든 2024. 7. 25. 11:20
728x90

오늘 포스팅에서는 Tanstack Router 설치 및 기본 설정, Router와 NotFound Error처리에 대해 정리해보려고 한다.

 

 

:D 설치 및 기본 설정

 

아래 명령어를 통해 설치할 수 있으며, 요구사항은 다음과 같다.

  • React v18.x.x
  • ReactDOM v18.x.x
  • Typescript v5.x.x (OPTIONAL)
npm install @tanstack/react-router

 

만약, Vite나 다른 번들러를 사용한다면 아래 명령어를 이용해 설치한다.

npm install --save-dev @tanstack/router-plugin @tanstack/router-devtools

 

설치 후에는 vite.config.ts 파일에 아래 내용을 추가한다. (해당 프로젝트에서도 Vite를 사용하고 있다)

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

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    TanStackRouterVite(),
    viteReact(),
    // ...
  ],
})

 

또한, 필요에 따라 tsr.config.json 파일을 정의하여 아래의 기본 설정을 변경할 수 있다. 이 외의 다른 옵션 정보는 링크를 통해 자세히 확인할 수 있다. #tsr.config.json Options

{
  "routesDirectory": "./src/routes",
  "generatedRouteTree": "./src/routeTree.gen.ts",
  "routeFileIgnorePrefix": "-",
  "quoteStyle": "single"
}

 


 

:D File Naming Conventions

 

기본적으로 설정된(tsr.config.json에 정의된) routeFileIngorePrefix- 이기 때문에 -로 시작하는 파일이나 디렉토리는 라우팅에 고려되지 않는다.

 

__root.tsx - 루트 라우트의 파일 이름은 __root.tsx로 정의된다.
- routesDirectory의 루트에 파일이 위치해야한다.
. 구분자 - . 문자를 사용해 중첩 라우트를 표현한다.
- blog.post라면 blog의 하위로 post가 생성됨을 의미한다.
$ 토큰 - URL 경로명에서 값을 추출하여 라우트 파라미터로 사용한다.
_ 접두사 - 레이아웃 라우트로 간주된다. (자체적 렌더링시 사용)
- 하위 자식 라우트들의 URL일치 여부에는 관여하지 않는다.
_ 접미사 - 부모 라우트 아래 중첩되지 않도록 제외된다.
index 토큰 - 부모 경로와 URL이 정확히 일치할 때, 부모 경로를 매치한다.
.route.tsx 파일 - route 접미사를 사용하여 디렉터리 경로에 라우트 파일을 생성한다.
- blog.post.route.tsx 또는 blog/post/route.tsx는 /blog/post 라우트의 파일로 사용될 수 있다.
.lazy.tsx 파일 - 라우트의 컴포넌트를 코드 분할 할 수 있다.

 

또한, routeFilePrefix와 routeFileIgnorePrefix 옵션을 사용하여 파일 기반 라우팅 설정 시 특정 접두사로 시작하는 파일과 디렉터리만 포함하거나 제외할 수 있다.

 


 

:D Router 생성

 

Router 클래스는 Tanstack Router의 핵심 기능을 담당하고 있다. 라우트 트리 관리, 라우트 매칭, 네비게이션과 라우트 전환을 조정하는 역할을 한다. 또한, 라우트 전역 설정을 관리하는 역할을 한다.

import { createRouter, RouterProvider } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen.ts';

const router = createRouter({ routeTree });


ReactDOM.createRoot(document.getElementById('root')!).render(
    <React.StrictMode>
        <RouterProvider router={router} />
    </React.StrictMode>
);

 

파일 기반 라우팅이든 코드 기반 라우팅이든 createRouter에 라우터 트리를 전달해야한다. 만약 파일 기반 라우팅을 사용했다면 일반적으로 생성된 라우트 트리 파일은 src/routeTree.gen.ts에 생성된다. (사용자 정의 위치를 사용했다면 해당 위치에서 라우트 트리를 가져와야 한다.)

 


 

:D Router Type Safety

 

TanStack Router provides amazing support for TypeScript, even for things you wouldn't expect like bare imports straight from the library!
To make this possible, you must register your router's types using TypeScripts' 
Declaration Merging feature. This is done by extending the 
Register interface on @tanstack/react-router with a router property that has the type of your router instance:

 

공식 문서를 따라 아래 타입을 정의하고 라우터를 등록하면 애플리케이션 내 라우팅 관련한 모든 것에 대해 타입 안전성을 확보할 수 있다고 한다.

declare module '@tanstack/react-router' {
  interface Register {
    // This infers the type of our router and registers it across your entire project
    router: typeof router
  }
}

 


 

:D Not-Found Route  #Not Found Route 

 

공식문서에 따르면 아래 방법을 통해 NotFoundRoute를 설정할 수 있다고 기재되어 있다. 

import { NotFoundRoute } from '@tanstack/react-router'
import { Route as rootRoute } from './routes/__root.tsx'

const notFoundRoute = new NotFoundRoute({
  getParentRoute: () => rootRoute,
  component: () => '404 Not Found',
})

const router = createRouter({
  routeTree,
  notFoundRoute,
})

 

실제 예제를 따라 작업하며 notFoundRoute가 depredcated됐다는 사실을 알게되었다.

/**
     * @deprecated
     * Use `notFoundComponent` instead.
     * See https://tanstack.com/router/v1/docs/guide/not-found-errors#migrating-from-notfoundroute for more info.
     * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#notfoundroute-property)
     */
    notFoundRoute?: AnyRoute;

Tanstack Router에서 notFound 오류를 두 가지 방법(NotFoundComponent APInotFound()를 사용)으로 처리할 수 있다. 보통 아래와 같은 상황에 오류가 발생한다. #Not Found Errors

 

첫 번째, Non-matching route paths (경로가 일치하지 않는 경우)일 때, 자동으로 notFound오류가 발생한다. 보통 이 경우에는 NotFoundComponent API를 이용해 처리할 수 있다.

 

이 경우에는 NotFoundMode 설정을 통해 오류 처리 방식을 결정할 수 있다. NotFoundMode에는 두 가지 모드가 존재한다.

  • fuzzy : 가장 가까운 부모 라우트 중에서 notFoundComponent가 있는 라우트가 오류를 처리한다.
  • root : 루트 라우트가 오류를 처리한다.

 

해당 설정은 Router에서 변경할 수 있다. (공식 문서에 따르면 기본 설정은 "fuzzy"이다.) 

const router = createRouter({
    routeTree,
    notFoundMode: 'fuzzy',
});

아래 코드와 같이 route를 설정했을 때, /learning/1과 같은 접근을 시도하면 fuzzy인 경우, 부모 라우트인 LearningRoute에서 notFoundComponent 설정을 확인하고, LearningRoute의 notFoundComponent를 렌더링한다. 반대로, root인 경우, 같은 접근에 대해 root라우트의 notFoundComponent가 렌더링된다.

import { createFileRoute } from '@tanstack/react-router';

/** learningRoute **/
export const LearningRoute = createFileRoute('/learning')({
    component: () => <div>학습 관리 페이지</div>,
    notFoundComponent: () => <div> NOT FOUND ERROR </div>,
});


/** root route **/
export const Route = createRootRoute({
    component: () => (
        <div>
            <Link to='/'>Home</Link>
            <Link to='/untact'>Untact</Link>
            <hr />
            <Outlet />
            <TanStackRouterDevtools />
        </div>
    ),
    notFoundComponent: () => <div>NOT FOUND ERROR - root에서 처리</div>,
});

또 다른 방법으로는 createRouter에서 defaultNotFoundComponent를 설정하여 전역적으로 관리할 수 있다. (Leaf-node 경로들은 아울렛을 렌더링하지 않기 때문에 '경로 없음' 오류를 처리할 수 있는 구조가 아니기 때문에 아래 같은 방법을 적용해 처리할 수 있다)

const router = createRouter({
  defaultNotFoundComponent: () => {
    return (
      <div>
        <p>Not found!</p>
        <Link to="/">Go home</Link>
      </div>
    )
  },
})

두 번째, Missing resources(누락된 리소스가 있는 경우)일 때, 특정 ID를 가진 포스트가 없거나 존재하지 않는 비동기 데이터를 찾을 때 발생한다. 예를 들어, ID가 1인 포스트가 존재하지 않을 때, /post/1과 같은 접근을 시도한 경우, notFound오류가 발생한다. 

이 때에는 notFound()를 사용해 해당 오류를 처리할 수 있다.

 

notFound()는 redirect와 유사하게 동작한다. 또한, 같은 라우트 또는 가장 가까운 부모 라우트에서 오류를 처리한다. 이를 위해서 defaultNotFoundComponent를 설정하거나 notFoundComponent 설정을 권장하고 있다.

 

아래 코드는 notFound 사용 예시이다. (각각 routeId를 지정해 사용하거나 rootRouteId를 이용해 처리할 수 있다.)

// 1. notFound()사용하여 처리
export const Route = createFileRoute('/posts/$postId')({
  loader: async ({ params: { postId } }) => {
    // Returns `null` if the post doesn't exist
    const post = await getPost(postId)
    if (!post) {
      throw notFound()
      // Alternatively, you can make the notFound function throw:
      // notFound({ throw: true })
    }
    // Post is guaranteed to be defined here because we threw an error
    return { post }
  },
})


// 2. layoutId를 지정하여 처리 
// _layout/a.tsx
export const Route = createFileRoute('/_layout/a')({
  loader: async () => {
    // This will make LayoutRoute handle the not-found error
    throw notFound({ routeId: '/_layout' })
    //                      ^^^^^^^^^ This will autocomplete from the registered router
  },
  // This WILL NOT render
  notFoundComponent: () => {
    return <p>Not found (in _layout/a)</p>
  },
})

// 3. rootRouteId 변수를 전달하여 root의 notFoundComponent로 설정하여 처리
import { rootRouteId } from '@tanstack/react-router'

export const Route = createFileRoute('/posts/$postId')({
  loader: async ({ params: { postId } }) => {
    const post = await getPost(postId)
    if (!post) throw notFound({ routeId: rootRouteId })
    return { post }
  },
728x90
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크