❓ 리액트는 왜 파일 기반 라우팅을 지양할까?
- 타입 안정성
명시적으로 라우트 트리를 정의하면 TypeScript와의 통합이 훨씬 수월해집니다. 각 라우트에 대해 명확한 타입 정보를 제공할 수 있어, 잘못된 경로나 컴포넌트 참조로 인한 런타임 오류를 예방할 수 있습니다.
- 구조적 명시
파일 기반 라우팅은 폴더 및 파일 구조에 의존하지만, 명시적인 라우트 정의는 라우팅 계층 구조와 레이아웃을 한눈에 파악할 수 있도록 도와줍니다. 이를 통해 복잡한 중첩 라우팅이나 에러 처리 로직 등을 체계적으로 관리할 수 있습니다.
- 유연한 구성
라우터를 코드 상에서 직접 관리하면 라우트 간의 복잡한 관계나 동적 라우팅, 권한 체크 등 추가 로직을 쉽게 삽입하고 유연하게 대처할 수 있습니다.
❓ 그럼에도 불구하고 탄스택리액트라우터를 사용하는 이유는 무엇일까?
- 빠른 개발 및 자동화
파일 시스템의 디렉토리 구조가 라우트 경로와 직접적으로 매핑되므로, 개발자가 별도의 라우트 정의 코드를 작성하지 않아도 됩니다. 이는 개발 속도를 높이고 보일러플레이트 코드를 줄여줍니다.
- 컨벤션 기반 설정
파일 기반 라우팅은 '관습에 의한 구성(convention over configuration)' 철학을 따릅니다. 규칙만 잘 정해지면 팀 전체가 일관된 방식으로 라우트를 관리할 수 있어 대규모 팀이나 프로젝트에서 유지보수가 용이합니다.
- 단순한 프로젝트 구조
모든 프로젝트가 복잡한 라우팅 로직이나 중첩, 동적 라우팅 등을 필요로 하지 않습니다. 규모가 작거나 구조가 단순한 애플리케이션의 경우 파일 기반 라우팅이 오히려 빠르고 직관적일 수 있습니다.
- 커뮤니티 및 생태계의 지원
Next.js와 같은 여러 프레임워크와 도구들이 파일 기반 라우팅을 지원합니다. 이를 통해 관련 플러그인이나 확장 기능을 쉽게 활용할 수 있으며, TypeScript 통합 등 다양한 기능도 제공합니다.
2. 본론
등장개요
tanstack-react-router는 TanStack 팀에서 개발한 React 기반 라우팅 라이브러리로, 선언적이고 타입 안전한 방식으로 라우트와 레이아웃을 구성할 수 있도록 도와줍니다.
이 라이브러리를 사용할 때 보통 두 가지 주요 파일이 등장합니다.
- __root.tsx
애플리케이션의 최상위(root) 컴포넌트로, 라우터 설정 및 전역 레이아웃(예: 네비게이션 바, 푸터, 에러 처리 등)을 정의합니다.
- routeTree.gen.ts
라우트 트리(route tree)를 자동으로 생성하는 파일입니다. 애플리케이션의 각 라우트(페이지)와 그 계층 구조를 선언적으로 구성한 결과물을 담고 있으며, 빌드 과정이나 CLI 도구를 통해 자동 생성됩니다.
각 파일 및 라이브러리의 의의와 역할
1. tanstack-react-router
- 역할: React 애플리케이션 내에서 복잡한 라우팅 로직을 쉽게 관리할 수 있도록 도와줍니다.
- 특징
- 선언적 API와 TypeScript 지원을 통해 코드 안정성과 유지보수성을 높임.
- 중첩 라우트, 동적 라우팅, 레이아웃 관리 등 다양한 기능 제공.
2. __root.tsx
- 역할: 애플리케이션의 "루트" 컴포넌트로, 라우터를 감싸는 전역 레이아웃을 설정합니다.
- 주요 내용
- 보통 RouterProvider 컴포넌트를 사용하여 생성된 라우터 인스턴스를 하위 컴포넌트에 전달합니다.
- 전역 상태, 에러 경계, 로딩 처리 등의 공통 요소들을 포함할 수 있습니다.
// __root.tsx
import React from 'react';
import { RouterProvider } from '@tanstack/react-router';
import { router } from './router'; // 라우터 설정 파일
function Root() {
return <RouterProvider router={router} />;
}
export default Root;
필수조건
- 최상위 컴포넌트로 사용되어야 하며, 올바른 라우터 인스턴스가 전달되어야 합니다.
- 전역 레이아웃이나 에러 처리 로직을 포함할 때 하위 라우트와의 충돌에 주의해야 합니다.
3. routeTree.gen.ts
- 역할: 라우트들의 계층 구조를 선언적으로 구성한 결과를 담은 자동 생성 파일입니다.
- 주요 내용
- CLI 도구나 빌드 스크립트를 통해 자동 생성되며, 라우터가 어떤 경로와 컴포넌트를 렌더링할지 결정합니다.
// routeTree.gen.ts
import { createRouteTree } from '@tanstack/react-router';
export const routeTree = createRouteTree([
{
path: '/',
element: <HomePage />,
errorElement: <ErrorPage />,
children: [
{
path: 'about',
element: <AboutPage />,
},
// 추가 라우트 ...
],
},
]);
필수조건
- 각 라우트에 필요한 element와 (선택적으로) errorElement를 올바르게 지정해야 합니다.
- 프로젝트의 라우트 정의와 일치하도록 파일 구조 및 설정이 이루어져야 합니다.
tanstack-react-router와 __root.tsx, routeTree.gen.ts 간의 관계
- 라우트 정의와 구성
- routeTree.gen.ts 파일에 모든 라우트(페이지)와 그 계층 구조가 선언됩니다.
- 이를 기반으로 별도의 파일(예: router.ts)에서 createReactRouter 함수를 사용해 라우터 인스턴스를 생성합니다.
// router.ts
import { createReactRouter } from '@tanstack/react-router';
import { routeTree } from './routeTree.gen';
export const router = createReactRouter({ routeTree });
- 애플리케이션 최상위 구성
- __root.tsx는 생성된 router 인스턴스를 RouterProvider에 전달하여 전역 라우팅을 활성화합니다.
- 상호 보완적 역할
- routeTree.gen.ts는 라우트 데이터(경로, 계층 구조)를 제공하고,
- tanstack-react-router는 이 데이터를 기반으로 실제 라우팅 로직을 구현하며,
- __root.tsx는 이 시스템을 애플리케이션에 통합해 사용자에게 제공하는 역할을 합니다.
3. 사용법 및 문법
패키지 설치
npm install @tanstack/react-router
자동 생성 설정
- 프로젝트에 맞게 routeTree를 자동 생성하는 스크립트나 CLI 도구를 설정합니다. (자세한 내용은 TanStack Router 공식문서 참고)
코드 예시
- 라우트 트리 생성 (routeTree.gen.ts)
// routeTree.gen.ts
import { createRouteTree } from '@tanstack/react-router';
import HomePage from './pages/HomePage';
import AboutPage from './pages/AboutPage';
import ErrorPage from './pages/ErrorPage';
export const routeTree = createRouteTree([
{
path: '/',
element: <HomePage />,
errorElement: <ErrorPage />,
children: [
{
path: 'about',
element: <AboutPage />,
},
// 추가 라우트들...
],
},
]);
- 라우터 인스턴스 생성 (router.ts)
// router.ts
import { createReactRouter } from '@tanstack/react-router';
import { routeTree } from './routeTree.gen';
export const router = createReactRouter({ routeTree });
- 최상위 컴포넌트 구성 (__root.tsx)
// __root.tsx
import React from 'react';
import { RouterProvider } from '@tanstack/react-router';
import { router } from './router';
function Root() {
return (
<RouterProvider router={router}>
{/* 전역 레이아웃 컴포넌트나 추가 Provider 등을 여기에 추가 */}
</RouterProvider>
);
}
export default Root;
추가 사항
- 라우트 정의 시 각 라우트에 element와 (필요한 경우) errorElement를 반드시 지정해야 합니다.
- 중첩 라우트를 사용할 경우, 부모 컴포넌트 내에 <Outlet /> 또는 하위 라우트를 렌더링할 컴포넌트를 포함해야 합니다.
- 자동 생성 도구를 사용할 경우, 설정 파일(예: tanstack-router.config.js)에서 올바른 경로와 옵션을 지정해야 합니다.
- 주의사항
- 필수 요소(element)가 누락되면 라우터가 올바르게 동작하지 않아 런타임 에러가 발생할 수 있습니다.
- 중첩 라우트가 충돌하거나 중복 선언되면 의도치 않은 렌더링 결과가 나타날 수 있으므로 주의해야 합니다.
- TypeScript 사용 시 타입 정의가 올바르게 되어 있지 않으면 컴파일 단계에서 오류가 발생할 수 있습니다.
4. 이해를 돕기 위한 개발 코드와 공식 문서 비교
1. routeTree.gen.ts (자동 생성 파일)
목적
- 파일 시스템 구조에 따라 라우트를 자동으로 생성하고, 각 라우트를 업데이트 및 연결하는 역할을 합니다.
주요 구성 요소
- 라우트 임포트
import { Route as AboutImport } from './app/about'; // ... 다른 라우트 임포트
- 각 라우트 컴포넌트를 개별 파일에서 가져옵니다.
- 라우트 업데이트
const AboutRoute = AboutImport.update({ id: '/about', path: '/about', getParentRoute: () => rootRoute, } as any);
- .update() 메서드를 사용하여 각 라우트에 id, path, 부모 라우트를 지정합니다.
- 타입 선언
declare module '@tanstack/react-router' { interface FileRoutesByPath { '/about': { id: '/about'; path: '/about'; fullPath: '/about'; preLoaderRoute: typeof AboutImport; parentRoute: typeof rootRoute; } // ... 다른 라우트 타입 정의 } }
- FileRoutesByPath, FileRoutesByTo, FileRoutesById 인터페이스를 통해 타입 안정성을 보장합니다.
- 라우트 트리 생성
const rootRouteChildren = { PageRoute: PageRoute, AboutRoute: AboutRoute, // ... 다른 라우트들 }; export const routeTree = rootRoute._addFileChildren(rootRouteChildren) ._addFileTypes<FileRouteTypes>();
- 루트 라우트에 자식 라우트를 추가하여 전체 라우트 트리를 구성합니다.
2. __root.tsx (루트 컴포넌트 파일)
목적
- 애플리케이션 최상위에서 라우터를 초기화하고, 전역 레이아웃(헤더, 푸터 등)과 컨텍스트를 설정합니다.
주요 구성 요소
- 라우트 생성
export const Route = createRootRouteWithContext<RouterContext>()({ component: RootComponent, beforeLoad: async ({ context, location }) => { // 인증 상태와 경로에 따라 리다이렉션 처리 const authenticated = context.auth.authenticated; const pathname = location.pathname; if (authenticated && matchRoute(noAuthRoutes, pathname)) return redirect({ to: '/' }); if (!authenticated && !(matchRoute(publicRoutes, pathname) || matchRoute(noAuthRoutes, pathname))) { return redirect({ to: '/auth', search: { redirect: pathname } }); } }, });
- createRootRouteWithContext<RouterContext>()를 사용하여 루트 라우트를 생성합니다.
- 전역 컴포넌트 구성
function RootComponent() { const navigate = Route.useNavigate(); const { user, logout } = useAuth(); const router = useRouter(); const handleLogout = async () => { await logout(); await router.invalidate(); await navigate({ to: '/auth' }); }; return ( <div className="flex min-h-screen w-full flex-col"> <Header username={user?.name} onClickLogout={handleLogout} /> <main className="mx-auto flex w-full flex-1 justify-center"> <Outlet /> </main> <Footer /> <TanStackRouterDevtools position="bottom-left" /> <ReactQueryDevtools buttonPosition="bottom-right" /> </div> ); }
- RootComponent 내에서 <Outlet />을 사용해 하위 라우트를 렌더링하며, Header, Footer, DevTools 등을 포함합니다.
핵심 포인트
- beforeLoad 훅: 라우트 접근 전에 인증 상태를 체크하고, 필요한 경우 리다이렉션을 수행합니다.
- Outlet: 하위 라우트를 렌더링하기 위한 자리 표시자 역할을 합니다.
- 전역 컨텍스트: RouterContext를 통해 QueryClient와 auth 객체를 하위 컴포넌트에 제공합니다.
공식문서와의 비교
(1) 라우트 정의 방식
- 공식문서
- 보통 개발자가 직접 createReactRouter와 createRouteTree를 사용해 라우트를 선언적으로 구성합니다.
- 예시:
- import { createReactRouter, createRouteTree } from '@tanstack/react-router'; const routeTree = createRouteTree([ { path: '/', element: <HomePage />, children: [ { path: 'about', element: <AboutPage /> }, // 추가 라우트 ], }, ]); export const router = createReactRouter({ routeTree });
- 제공된 코드
- 파일 기반 라우팅을 통해 CLI나 빌드 도구가 자동으로 routeTree.gen.ts 파일을 생성합니다.
- 각 파일의 위치가 라우트 경로와 매핑되며, .update() 메서드로 라우트 정보와 타입을 자동으로 업데이트합니다.
(2) 루트 컴포넌트 구성
- 공식문서
- 일반적으로 단순하게 RouterProvider를 사용하여 생성된 라우터 인스턴스를 애플리케이션 최상위에 주입합니다.
- import { RouterProvider } from '@tanstack/react-router'; import { router } from './router'; function Root() { return <RouterProvider router={router} />; }
- 제공된 코드
- createRootRouteWithContext를 사용해 루트 라우트를 생성하고, beforeLoad 훅으로 인증 등 추가 로직을 수행합니다.
- 헤더, 푸터, DevTools 등 다양한 전역 UI 컴포넌트를 포함한 더 복잡한 레이아웃 구성이 이루어집니다.
(3) 라우트 자동 생성 vs. 수동 정의
- 공식문서
- 주로 수동으로 라우트 트리를 선언하는 방식을 설명합니다.
- 파일 기반 라우팅은 별도의 가이드나 플러그인 형태로 제공되는 경우가 있습니다.
- 제공된 코드
- 파일 기반 라우팅 방식을 채택해, 파일 경로와 라우트 경로를 자동으로 연결합니다.
- 자동 생성 파일(routeTree.gen.ts)에서 타입 선언, 라우트 업데이트, 라우트 트리 구성 등이 자동으로 처리되어, 개발자가 별도의 라우트 구성을 작성할 필요가 없습니다.
- 이 방식은 대규모 프로젝트나 일관된 규칙에 따라 라우트를 관리할 때 유용합니다.
5. 정리
- 기본 문법
- 라우트 생성: 각 라우트 파일에서 Route 객체를 가져와 .update()를 통해 id, path, 부모 라우트를 설정합니다.
- 타입 선언: 자동 생성된 인터페이스를 통해 파일 기반 라우트의 타입 안전성을 보장합니다.
- 라우트 트리 구성: 루트 라우트에 자식 라우트를 추가하여 전체 트리를 구성합니다.
- 루트 컴포넌트 구성: createRootRouteWithContext를 사용하여 전역 컨텍스트와 함께 루트 컴포넌트를 정의하고, <Outlet />으로 하위 라우트를 렌더링하며, beforeLoad 훅으로 추가 로직(예: 인증 체크)을 처리합니다.
- 공식문서와의 차이점:
- 수동 정의 vs. 파일 기반 자동 생성: 공식 문서는 수동으로 라우트를 선언하는 방법을 설명하지만, 제공된 코드는 파일 기반 라우팅을 통해 자동으로 라우트 구성을 생성합니다.
- 루트 컴포넌트: 공식 예제에서는 주로 RouterProvider를 사용하는 단순한 구성을 보여주지만, 제공된 코드는 커스텀 beforeLoad 훅과 전역 레이아웃(헤더, 푸터, DevTools 등)을 포함하는 더 복잡한 구성을 보여줍니다.
- 타입 안전성 강화: 자동 생성 파일에서는 파일 경로와 라우트 매핑을 위한 상세한 타입 선언이 포함되어 있어, 컴파일 단계에서의 타입 검증이 더욱 철저합니다.
'프론트엔드 역량 > CS 기초, 알고리즘, 자료구조, 시스템 디자인' 카테고리의 다른 글
[HTML5] 시맨틱 태그란? (0) | 2025.04.02 |
---|---|
[라이브러리] shadcn-Accodion(토글ui) (0) | 2025.04.02 |
[CS 기초] 기본 재귀 (Recursion) (0) | 2025.04.02 |
[CS 기초] 정렬 및 탐색 알고리즘 (0) | 2025.04.02 |
[CS 기초] 자료구조 기본 (0) | 2025.04.02 |