React Router DOM을 Suspense와 같이 작성해야 하는 이유
React 프로젝트에서 React Router DOM으로 라우팅을 관리할 때 Suspense, lazy까지 활용해야 하는 이유 및 과정을 작성했습니다.
Table of contents
React Router DOM
React 프로젝트에서는 React Router DOM으로 페이지 라우팅을 관리할 수 있다.
공식문서에서는 Data API를 활용할 수 있는 createBrowserRouter
를 통해 라우팅을 구성하는 예제를 보여준다.
각
RouteObject
마다 렌더링할 컴포넌트를 명시할 수 있다.예제 프론트엔드에는
/home
,/detail
,/settings
주소가 존재한다.각 주소마다
Home
,Detail
,Settings
페이지 컴포넌트가 작성되어있다.
하나의 라우팅에 여러 개의 하위 라우팅을
children
형태로 적용할 수 있다.Home, Children 컴포넌트 모두 Layout 컴포넌트 내부에 배치될 수 있다.
const routes: RouteObject[] = [
{
path: "/",
children: [
{
path: "/",
element: <Layout />,
children: [
{
path: "/home",
element: <Home />,
},
{
path: "/detail",
element: <Detail />,
},
],
},
{
path: "/settings",
element: <Setting />,
},
],
},
];
export default routes;
이슈
개발 도중 네트워크 요청이 예상보다 많이 발생한다는 이슈가 있어 확인 결과 Home 컴포넌트에서 Detail 컴포넌트 또한 렌더링되는 것을 확인할 수 있었다.
Detail 컴포넌트가 렌더링되면 컴포넌트 내부에 작성된 Hooks가 호출되면서 관련 네트워크 요청이 실행되는 것이었다.
각 라우팅에 걸려있는 컴포넌트마다 Suspense 컴포넌트를 적용해서 특정 페이지에 접속하면 해당하는 컴포넌트만 실행되도록 리팩토링 작업을 거쳤다.
우선 페이지 컴포넌트를 export default
로 모듈을 내보낼 수 있어야 한다.
lazy
에는 컴포넌트를 동적으로 불러오는 함수를 인자로 전달한다.동적 Import로 반환되는 컴포넌트는 Promise 형태이다.
프론트엔드가 컴포넌트를 렌더링할 때 Promise가 완료될 때까지 대기한 다음
default
속성의 값을 리액트 컴포넌트로 렌더링하기 때문이다.
// export function Home()
export default function Home() {
const location = useLocation();
const { data, error, isLoading } = useHomeData();
return (
<div>
<h1>Home page</h1>
</div>
);
}
각 컴포넌트를 React.lazy
로 모듈을 불러오게 변경한다.
lazy
는 컴포넌트가 첫번째로 렌더링될 때까지 컴포넌트 실행을 연기시킨다.각 페이지 컴포넌트를
Suspense
의 하위 컴포넌트 형태로 작성한다.- Suspense는 컴포넌트가 렌더링 완료될 때까지
fallback
으로 지정된 컴포넌트를 보여줄 수 있다.
- Suspense는 컴포넌트가 렌더링 완료될 때까지
const Home = lazy(() => import("./pages/Home"));
const routes: RouteObject[] = [
{
path: "/",
children: [
{
path: "/",
element: <Layout />,
children: [
{
path: "/home",
element: (
<Suspense>
<Home />
</Suspense>
),
},
{
path: "/detail",
element: (
<Suspense>
<Detail />
</Suspense>
),
},
],
},
{
path: "/settings",
element: (
<Suspense>
<Setting />
</Suspense>
),
},
],
},
];
export default routes;
결과
페이지를 이동할 때마다 각 페이지에 해당하는 컴포넌트가 필요한 네트워크 요청만 실행하기 때문에 네트워크 요청 수를 절약할 수 있었다.
추가적인 컴포넌트를 작성해야 하는 것도 알 수 있었다.
lazy
함수 호출하는 과정에서 프로미스가 실패할 것을 대비해서ErrorBoundary
역할의 컴포넌트lazy
함수 호출 후 컴포넌트를 렌더링하는 동안 대신 렌더링할fallback
역할의 컴포넌트