React-Router-Dom을 사용하다보면 필요할 때마다 문서에서 가져다쓰는 경우가 많다. 왜 써야하는지 모르고, 쓰라고하니 쓰는 것과 같은 수동적인 개발 방식은 좋지 않은 학습 방법이다. 또한 그러다보면 유용한 기능들이 있는데 놓치게되어 쓰던 것만 쓰게되는 상황이 발생한다.
주도적으로 내가 자주 사용했던 React-Router-Dom 기능이 구체적으로 어떤 것이고, 또 다른 유용한 기능들은 어떤 것들이 있는지 정리하며 살펴보고자 한다.
참고로 React-Router-Dom v6.15.0 기준으로 작성했다.
Route 정보를 통해 현재 url, 히스토리 관리 등을 수행한다.
이전 버전에서는 BrowserRouter, HashRouter 등을 이용해 라우터를 정의할 수 있는데, createBrowserRouter, createHashRouter를 사용하게되면 Data API를 통해 Route에 loaders, actions, fetchers와 같은 유용한 기능을 사용해, 필요한 요구사항을 빠르게 구현할 수 있다.
createBrowserRouter
사용이 권고된다createBrowserRouter의 매개변수로, route객체를 주입할 수 있다.
browser router 말고도 HashRouter, MemoryRouter 등이 있다. 다른 Router들은 어디 사용되는 걸까?
그 중 MemoryRouter는 createMemoryRouter
를 통해 생성된다. 브라우저 history를 사용하지 않고 memory 내부에서 별개의 history stack을 관리한다. 테스트 및 컴포넌트 개발 도구에 유용하며 브라우저가 아닌 환경에서 React 라우터 실행을 위해 사용될 수 있다.
컴포넌트 그려낼 URL 지정한다. :
는 dynamic segment
를 나타낸다. path가teams/:teamsId
일 경우 teamsId에는 여러 값들이 허용되고 teams/1
teams/5
등의 입력이 해당 경로에 매핑된다. 매핑된 컴포넌트에서 teamsId에 대한 값을 얻기위해서 useParams
훅을 이용해 접근한다.
*(star) segmenet를 사용할 경우 하위 경로가 어떻게 되든지 모든 경로에 대응된다. path가 teams/*
일 경우 teams/3
, teams/team/3
등의 경로에 매핑된다.
dynamic segmenet와 동일하게 useParams
를 사용해 param에 접근한다.
컴포넌트를 렌더링 하기 이전에 수행되고, 수행결과를 컴포넌트에 전달한다.
예를 들어 화면이 렌더링 되기 전에 인증된 사용자인지를 검사한 후, 다른 페이지로 리다이렉트할지 또는 화면을 렌더링할지 동작할 수 있다.
url에 대응하여 렌더링할 컴포넌트를 지정한다.
컴포넌트 렌더링 중 에러가 발생하면 정상적인 컴포넌트 대신 그려질 컴포넌트를 지정한다.
위 element / Component 처럼 엘리먼트를 생성하는 주체가 개발자인지 React Router인지에 차이가 있다.
라우트를 기준으로 code-splitting을 제공한다.
lazy 사용을 위해 Route의 lazy 속성에 아래처럼 모듈을 import한다. 그리고 그려낼 컴포넌트, 이외에 필요한 loader, error boundary등 기능을 a 모듈내에서 export 한다.
<Route path="/" element={<Layout />}>
<Route path="a" lazy={() => import("./a")} />
<Route path="b" lazy={() => import("./b")} />
</Route>
// ./a
export async function loader({ request }) {
let data = await fetchData(request);
return json(data);
}
export function Component() {
let data = useLoaderData();
return (
<>
<h1>You made it!</h1>
<p>{data}</p>
</>
);
}
Parent route element에 outlet을 선언하면 그곳에 child route elements들이 그려지게된다. 이때 path
는 전달하지않는다.
여러 페이지에서 공통적으로 사용되는 헤더, 목차 등을 Parent에 그려주고 Outlet을 선언하면 유지보수성이 개선된 코드를 작성할 수 있다.
const router = createBrowserRouter([
{
path: '/',
element: <PageLayout />,
children: [
{
path: ROUTE_FORM_URL,
element: <RequestFormPage />
},
{
path: ROUTE_REQUESTS_URL,
element: <RequestsPage />
}
]
},
]);
// PageLayout.tsx
export default function PageLayout() {
return (
<StyledPageLayout>
<StyledTOC> {/* 목차 컴포넌트*/}
<StyledLink to={ROUTE_FORM_URL}>요청</StyledLink>
<StyledLink to={ROUTE_REQUESTS_URL}>요청 목록</StyledLink>
</StyledTOC>
<Outlet /> {/* 이곳에 하위 컴포넌트가 주입된다*/}
</StyledPageLayout>
);
}
컴포넌트를 반환하면 현재 location을 변경한다.
<Naviagte to=`${URL}` />
🪄TIP
useNavigate훅을 사용해서 경로이동이 가능한 navigate함수를 사용할 수 있다.
history 스택의 현재 경로를 대체, 대체당하는 경로는 스택에 기록되지 않게 된다. (Link에 포함된 속성과 동일)
클릭 시 새로운 페이지로 이동하는 동작을 수행. 내부적으로 anchor 요소 사용하는 요소다.
<Link to={ROUTE_LOGIN_URL}>로그인하기</Link>
history 스택의 현재 경로를 대체, 대체당하는 경로는 스택에 기록되지 않게 된다.
active, pending 에 따라 스타일을 지정한다. Navigation 바, 탭 등을 구현할 때 유용하다.
예를 들어 현재 선택된 페이지의 링크를 하이라이팅하는 것에 쓰인다.
<StyledLink to={ROUTE_FORM_URL}>요청</StyledLink>
const StyledLink = styled(NavLink)`
color: white;
text-decoration: none;
padding: 10px;
border-radius: 10px;
&:hover {
background-color: #4898b3;
}
&.active {
background-color: #4898b3;
}
`;
현재 링크가 active한 상태이면, active 클래스명이 컴포넌트에 자동으로 추가됨, 따라서 컴포넌트에 active 클래스에 대한 스타일만 지정하기만 하면된다.