티스토리 뷰
최근에 진행했던 프로젝트에서 React Router와 Tanstack Query를 사용하여 애플리케이션 라우터를 설정했다. 오늘은 이를 적용하며 공부한 내용 중 React Router를 위주로 정리해보려고 한다.
React Router v6.4에는 아래와 같은 라우터들이 추가되었다. 아래 라우터와 함께 action / errorElement / lazy / loader 등 data API도 함께 제공되고 있다. (공식 문서에서는 새롭게 추가된 라우터 중 하나로 업데이트 하는 것을 권장하고 있다. 추가적으로 data API는 현재 React Native에서는 지원하고 있지 않다.)
- createBrowserRouter - DOM History API를 사용하여 URL을 업데이트하고 히스토리 스택을 관리한다.
- createMemoryRouter - 메모리 내에서 히스토리를 관리하며, 주로 테스트나 서버 측 렌더링에 사용된다.
- createHashRouter - 해시(#)를 사용하여 페이지를 관리하며, 해시 기반 라우팅을 지원한다.
- createStaticRouter - 정적 라우팅을 지원하며 서버 측 렌더링 및 정적 사이트 생성에 사용된다.
createBrowserRouter는 DOM History API를 사용하여 URL을 업데이트하고 히스토리 스택을 관리한다. 라우트 객체는 children속성을 통해 중첩된 경로를 정의할 수 있다. (DOMRouterOpts의 자세한 설명은 공식문서를 참고하길 바란다.)
declare function createBrowserRouter(routes: RouteObject[], opts?: DOMRouterOpts): RemixRouter;
interface DOMRouterOpts {
basename?: string;
future?: Partial<Omit<RouterFutureConfig, "v7_prependBasename">>;
hydrationData?: HydrationState;
unstable_dataStrategy?: unstable_DataStrategyFunction;
unstable_patchRoutesOnMiss?: unstable_PatchRoutesOnMissFunction;
window?: Window;
}
라우트 객체는 아래와 같이 타입이 정의되어 있다. index 값에 따라 크게 IndexRouteObject 또는 NonIndexRouteObject로 구분할 수 있다.
interface RouteObject {
path?: string;
index?: boolean;
children?: RouteObject[];
caseSensitive?: boolean;
id?: string;
loader?: LoaderFunction;
action?: ActionFunction;
element?: React.ReactNode | null;
hydrateFallbackElement?: React.ReactNode | null;
errorElement?: React.ReactNode | null;
Component?: React.ComponentType | null;
HydrateFallback?: React.ComponentType | null;
ErrorBoundary?: React.ComponentType | null;
handle?: RouteObject["handle"];
shouldRevalidate?: ShouldRevalidateFunction;
lazy?: LazyRouteFunction<RouteObject>;
}
각 속성에 대해 정리해보면 다음과 같다.
- path - URL, 링크의 href, 또는 폼의 액션과 일치하는지 판단하기 위한 경로 패턴을 의미한다.
- index - 해당 라우트가 인덱스 라우트인지 여부를 의미한다. (default : false)
- children - 중첩 라우트를 사용할 경우, 하위 라우트를 의미한다.
- caseSensitive : 경로가 정확히 일치해야 하는지 여부를 설정한다.(default : false)
- loader - 경로에 등록된 컴포넌트가 렌더링되기 전에 호출되며, useLoaderData 훅을 통해 요소에 대한 데이터를 가져올 수 있다.
- action - 경로에 폼(Form), fetcher, submission이 발생한 경우 호출된다.
아래와 같이 loader함수를 정의하여 사용할 수 있다. 나는 프로젝트에서 데이터가 로드되어야 하는 페이지에서는 로더함수를 따로 두어 구현하였다.(또한, tanstackQuery를 함께 사용하여 loader만을 사용했을 때 단점을 해결할 수 있었다. 이 부분은 다음 포스팅에서 다루려고 한다.)
export async function loader() { // Response 객체 반환(이 외에도 다른 데이터도 타입도 반환 가능) const response = await fetch('/api/data'); return response; }
이렇게 로더 함수를 정의하고 컴포넌트 내부에서 useLoaderData 훅을 사용하여 데이터를 가져올 수 있다.
import { useLoaderData } from 'react-router-dom'; const initialData = useLoaderData();
- element / Component - 렌더링할 React 요소 / 컴포넌트를 의미한다.
- errorElement / ErrorBoundary - 경로 렌더링 중 발생한 오류 상황(loader / action 등)에서 대신 렌더링된다.
- lazy - 애플리케이션 번들을 작게 유지하고 라우트 코드 스플릿을 지원하기 위해 사용한다.
💡 위 action 속성에서 언급한 폼(Form) 컴포넌트는 우리가 생각하는 HTML의 form태그가 아니다. 이를 감싸고 있는 wrapper 컴포넌트 역할을 한다. 이 컴포넌트는 폼 제출 시 페이지를 새로고침하지 않고 처리하고 라우팅 할 수 있게 도와준다.(클라이언트 사이드에서 라우팅과 데이터 수정을 처리할 수 있게 해 준다.)
import { Form } from "react-router-dom"; function NewEvent() { return ( <Form method="post" action="/events"> <input type="text" name="title" /> <input type="text" name="description" /> <button type="submit">Create</button> </Form> ); }
실제 구현 과정에서 Form 컴포넌트를 사용하며 이슈가 있었다. 공식문서를 꼼꼼히 읽어보지 않아서 발생한 문제였다. ㅠ.ㅠ
createBrowserRouter를 이용해 아래와 같은 라우터를 생성할 수 있다.
import ErrorBoundary from '../ErrorBoundary';
import withSuspense from '@/shared/ui/withSuspense';
import { createBrowserRouter } from 'react-router-dom';
import RootLayout from '@/shared/ui/Layout/RootLayout';
import { QueryClient } from '@tanstack/react-query';
/** 애플리케이션 라우터 */
export const createRouter = (queryClient: QueryClient) => {
const router = createBrowserRouter([
{
path: '/',
element: <RootLayout />,
errorElement: <ErrorBoundary />,
children: [
{
index: true /** 메인 페이지 */,
async lazy() {
const Main = await import('@/pages/main/index');
return { Component: withSuspense(Main.default) };
}
},
{
path: 'about/:userId' /** 사용자 상세 정보 */,
async lazy() {
const About = await import('@/pages/about/index');
return { Component: withSuspense(About.default), loader: About.loader(queryClient) };
}
},
]
}
]);
return router;
};
declare function RouterProvider({ fallbackElement, router, future, }: RouterProviderProps): React.ReactElement;
ReactDOM.createRoot(document.getElementById("root")).render(
<RouterProvider
router={router}
fallbackElement={<BigSpinner />}
/>
);
'REACT' 카테고리의 다른 글
big-calendar 라이브러리 사용하기 02 (1) | 2024.11.28 |
---|---|
big-calendar 라이브러리 사용하기 01 (2) | 2024.10.28 |
Tanstack Router 사용하기 03 (0) | 2024.08.04 |
Tanstack Router 사용하기 02 (0) | 2024.07.25 |
Tanstack Router 사용하기 01 (6) | 2024.07.24 |
- Total
- Today
- Yesterday