보통 웹사이트에서는 주소를 직접 입력하거나, 링크를 클릭하면 새로운 페이지를 보여준다. 리액트에서는 이를 만들기 위해서 따로 추가적인 개발이 필요하다.
이 때, 'React Router'라는 걸 사용하면 이를 편리하게 할 수 있는데, React Router는 리액트 컴포넌트로 페이지를 나누고, 이동 가능하게 해주는 라이브러리이다.
이번 글을 통해 리액트 라우터에 대해 자세히 알아보며, 리액트 라우터의 핵심 컴포넌트들과 이를 통해 페이지를 나누고, 동적 경로를 나누는 방법 등에 대해 알아보자.
리액트 라우터란 리액트에서 경로에 따라 페이지를 나누도록 해주는 라이브러리로, 리액트스러운 방법으로 컴포넌트를 사용해서 페이지를 나누는 것이 특징이다.
이 때, 리액트 라우터를 사용하면 여러 페이지를 나누고 이동하는 것을 '컴포넌트'로 할 수 있다는 것이 핵심이다.
리액트 라우터의 핵심 컴포넌트로는 Router, Routes, Route, 그리고 Link 이렇게 네 가지가 있다.
Router는 리액트 라우터에서 사용하는 데이터들을 모두 가지고 있는 녀석으로, 대표적으로 현재 주소라던지 페이지 기록같은 데이터를 가지고 있다.
주의할 점으로, Router 컴포넌트는 내부적으로 Context Provider이기 때문에, 리액트 라우터에서 제공하는 기능을 사용하려면 반드시 Router 컴포넌트 안에서 사용해야 한다.
이에, 보통은 프로젝트 전체에서 리액트 라우터를 사용하기 위해서 최상위 컴포넌트에서 Router를 감싸고 사용하는 경우가 많다는 점도 참고하자.
import { BrowserRouter } from 'react-router-dom';
function App() {
return <BrowserRouter> ... </BrowserRouter>;
}
cf> Context Provider에 관한 내용은 이전 글을 통해 자세히 확인 가능하다: React에서 전역 데이터를 다루는 법: Context
Routes와 Route는 주로 같이 사용되는데, Routes 컴포넌트 안에다가 Route 컴포넌트를 배치하여 페이지의 경로랑 보여줄 컴포넌트를 지정함으로써 각 페이지를 나눠줄 수 있다.
이때 Routes 안에서는 위에서부터 차례대로 Route를 검사하여 현재 경로와 path prop이 일치하는 Route를 찾는다.
더 쉽게 설명해보자면, 사용자가 Routes에 해당하는 경로로 들어왔을 때, Route들을 검사하며 차례대로 경로를 살피다가 맞는 경로를 찾으면 해당 Route 안에 element prop으로 지정한 컴포넌트를 보여줌으로써 페이지를 나눠줄 수 있다.
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="posts" element={<PostListPage />} />
<Route path="posts/:postId" element={<PostPage />} />
</Routes>
리액트 라우터에서는 <a>
태그 대신에 Link 컴포넌트를 사용한다. 이때, to라는 prop으로 이동할 경로를 지정해주면 된다.
추가적으로, NavLink라는 컴포넌트도 존재하는데, NavLink는 메뉴에서 사용하는 링크로 Link랑 마찬가지로 사용할 수 있지만, 한 가지 다른 점은 Style prop을 통해 함수를 지정 가능하다.
<Link to="/posts">포스트</Link>
페이지를 나누기 위해서는 앞서 언급하였듯이 기본적으로 Route 컴포넌트 안에다가 Route 컴포넌트를 배치하면 된다.
하위 페이지를 나타내기 위해서 리액트 라우터에서는 Route를 중첩해서 사용할 수 있게 해준다. 이때 하위 페이지에서 최상위 경로에 해당하는 경로(index에 해당하는 Route)는 path
prop이 아니라 index
라는 prop을 지정해주면 된다.
<Routes>
<Route path="/"><HomePage /></Route>
<Route path="posts" element={<PostLayout />} >
<Route index element={<PostListPage />} /> {/* /posts */}
<Route path="1" element={<PostPage />} /> {/* /posts/1 */}
</Route>
</Routes>
Route를 중첩해서 만들 때, 간혹 특정 path 안에서는 공통된 디자인을 보여주고 싶을 때가 있다. 이럴 때 Outlet
컴포넌트를 사용할 수 있다.
공통된 레이아웃을 설정하고 싶을 때는, 설정하고 싶은 부분을 element prop으로 지정하고, 지정된 컴포넌트에서 Outlet
컴포넌트를 사용하면 된다.
아래는 Outlet을 사용하여 공통 레이아웃을 설정한 예이다. 이 구조를 통해, PostLayout은 모든 포스트 페이지에 공통된 레이아웃(예: 포스트 제목, 구분선 등)을 제공할 수 있게 된다.
import { Outlet } from 'react-router-dom';
function PostLayout() {
return (
<div>
<h1>포스트</h1>
<Outlet />
</div>
);
}
export default PostLayout;
리액트 라우터에서는 경로(path)에 콜론(:)을 사용하여 파라미터(경로에서 사용하는 동적인 값)를 지정할 수 있다. 이 때, 파라미터에 접근하기 위해서 리액트 라우터는 useParams
라는 훅을 제공하는데, useParams
를 사용하면 현재 URL 경로에 포함된 파라미터들을 객체 형태로 접근 가능하다.
결론적으로, useParams
와 콜론(:)을 이용하면 리액트 라우터를 통해 유연하게 경로를 설정하고, 해당 경로의 파라미터를 쉽게 추출할 수 있게 된다.
예를 들어 아래에서처럼 동적인 경로를 다루게 되면, /posts/:postId
라는 경로를 통해 /posts/123
이라는 경로로 접근 한다던지, /posts/abc
라는 주소로 접속하면 123 이나 abc 라는 값을 postId 라는 파라미터로 받는 등의 작업이 가능하다.
function PostPage() {
const { postId } = useParams();
// ...
}
<Routes>
<Route path="/"><HomePage /></Route>
<Route path="posts" element={<PostLayout />} >
<Route index element={<PostListPage />} />
<Route path=":postId" element={<PostPage />} />
</Route>
</Routes>
이번 글을 통해 리액트 라우터에 대해 알아보며 리액트에서 애플리케이션을 구축하는 기본 틀에 대해 알아보며 리액트 라우터의 개념과 활용 방법에 대해 더욱 깊이 있게 알 수 있었다.
특히, 리액트 라우터는 단순한 페이지 네비게이션을 넘어서, 동적인 경로 처리, 중첩 라우팅, 공통 레이아웃 설정 등 다양한 고급 기능을 제공함으로써 애플리케이션 구조를 보다 효율적이고 유연하게 만들어준다는 장점을 알 수 있었다.
다음 글을 통해서는, 여기서 더 나아가 리액트 라우터를 통해 쿼리를 사용하고, 페이지를 이동하는 방법 등에 대해 알아보며 더욱 심화된 내용에 대해 학습해보고자 한다.