이번 글에서는 React Router의 개념 및 사용법에 대해 알아보자.
React Router의 기본 구조
React Router는 크게 아래 세 파트로 나뉜다.
<BrowserRouter>
<Routes>
<Route path="" element={<Component />}/>
</Routes>
</BrowserRouter>
- BrowserRouter: History 객체를 생성하고, 초기 위치를 상태로 만들고, URL을 참조한다.
- Routes: Route Config라는 경로 객체 트리를 생성한다. Route는 Routes 안에서만 유효하다.
- Route: path가 현재 URL과 일치하면 element가 렌더링된다.
여기서 생소한 단어 두 개가 등장한다.
먼저, History 객체란? History 객체는 History Stack을 조작할 수 있도록 API를 제공하는 객체다. History Stack은 접속 이력 스택으로 생각하면 된다. 브라우저의 뒤로 가기 및 앞으로 가기 버튼을 클릭할 때마다 참조하는 스택이다.
다음으로, Route Config란? 모든 Route에 대한 설정을 정리해놓은 경로 객체 트리다. 곧 어떻게 생겼는지 구경할 수 있으니 우선은 넘어가도록 하자.
한가지 개념만 더 짚고 넘어가자. Location 객체란? 브라우저의 빌트인 객체 'window.location'을 기반으로 하는 React Router의 객체다. Location은 현재 페이지에 대한 정보를 저장하고 있는데 형태는 아래와 같다. 참고로, useLocation()이라는 훅으로 Location 객체에 접근 가능하다.
{
pathname: "/bbq/pig-pickins",
search: "?campaign=instagram",
hash: "#menu",
state: null,
key: "aefz24ie"
}
아래처럼 { pathname, search, hash } 를 합치면 페이지 URL이 된다. 이 세 속성은 window.location과 완전히 일치한다.
location.pathname + location.search + location.hash;
window.location과 다른 점은 { state, key } 다.
state는 이전 페이지로부터 전달받은 값으로 생각하면 된다. Location에 state를 설정하는 방법으로는 <Link>를 사용하는 방법과 useNavigate 훅을 사용하는 방법이 있다.
<Link to="/pins/123" state={{ fromDashboard: true }} />;
let navigate = useNavigate();
navigate("/users/123", { state: partialUser });
key는 Location 객체의 고유값이다. 이를 잘 활용하면 뒤로 가기를 클릭했을 때 서버에 데이터를 요청하는 과정을 건너뛸 수 있다. 자세한 과정은 모르겠고 그렇다고 한다.. 참조 링크에 관련 코드가 있으니 참고하길 바란다.
라우팅 예제
예제를 통해 직관적으로 이해해보자. 이게 빠르다.
<Routes>
<Route path="/" element={<App />}>
<Route index element={<Home />} />
<Route path="teams" element={<Teams />}>
<Route path=":teamId" element={<Team />} />
<Route path=":teamId/edit" element={<EditTeam />} />
<Route path="new" element={<NewTeamForm />} />
<Route index element={<LeagueStandings />} />
</Route>
</Route>
<Route element={<PageLayout />}>
<Route path="/privacy" element={<Privacy />} />
<Route path="/tos" element={<Tos />} />
</Route>
<Route path="contact-us" element={<Contact />} />
</Routes>
이 자체를 Route Config라고 부르기도 한다. 하지만 이보다 Routes가 생성하는 객체가 Route Config로 부르기에 더 적합하다고 생각한다. Routes 컴포넌트는 위 코드를 기반으로 아래와 같은 객체를 생성한다. 코드가 좀 길어서 접은글 안에 넣어놨으니 궁금하면 보도록 하자.
let routes = [
{
element: <App />,
path: "/",
children: [
{
index: true,
element: <Home />,
},
{
path: "teams",
element: <Teams />,
children: [
{
index: true,
element: <LeagueStandings />,
},
{
path: ":teamId",
element: <Team />,
},
{
path: ":teamId/edit",
element: <EditTeam />,
},
{
path: "new",
element: <NewTeamForm />,
},
],
},
],
},
{
element: <PageLayout />,
children: [
{
element: <Privacy />,
path: "/privacy",
},
{
element: <Tos />,
path: "/tos",
},
],
},
{
element: <Contact />,
path: "/contact-us",
},
];
Router Config에서 경로만 따로 정리하면 위 애플리케이션이 응답할 수 있는 경로 패턴은 아래와 같다.
[
"/",
"/teams",
"/teams/:teamId",
"/teams/:teamId/edit",
"/teams/new",
"/privacy",
"/tos",
"/contact-us",
];
여기서 흥미로운 점을 하나 발견할 수 있다. '/teams/new' 라는 URL은 어떤 경로 패턴에 속할까? 정답은 두 개다.
/teams/new
/teams/:teamId
React Router는 경로 매칭 과정에서 segment의 개수, static segment, dynamic segment 등에 따라 우선순위를 매긴 뒤에 가장 일치하는 경로를 선택하기 때문에 경로 순서를 고려하지 않아도 된다. 여기서 segment란 '/'를 기준으로 분리되는 URL 단위를 말한다. 예를 들어, '/teams/new'는 두 개의 segment가 있다.
이제 URL 패턴마다 어떻게 렌더링되는지 살펴보자. path가 없는, 약간 특이한 컴포넌트를 중점으로 본다.
1) /teams
<Route path="/" element={<App />}>
<Route index element={<Home />} />
<Route path="teams" element={<Teams />}>
<Route path=":teamId" element={<Team />} />
<Route path=":teamId/edit" element={<EditTeam />} />
<Route path="new" element={<NewTeamForm />} />
<Route index element={<LeagueStandings />} />
</Route>
</Route>
<App>
<Teams>
<LeagueStandings />
</Teams>
</App>
우선 위와 같은 라우팅 방식을 '중첩 라우팅'이라고 한다. 쉽게 말해서 URL segment 별로 렌더링하는 컴포넌트를 선택하는 방식이다. '/teams' 는 '/'과 'teams'로 분리되어 각각 <App>, <Teams> 컴포넌트를 렌더링한다. 그러면 <LeagueStandings> 컴포넌트는 어디서 튀어 나왔을까? 이는 index Route 때문에 렌더링 되는 것이다. index Route는 경로를 설정하지 않는 대신에 기본 하위 경로로 자동 설정된다. 즉, '/teams'의 하위 경로가 없어서 자동으로 렌더링된 것이다.
그러면 '/' 는 어떻게 렌더링 될까? 하위 경로가 없고, index Routes가 있으므로 아래와 같이 렌더링 될 것으로 추측할 수 있다.
<App>
<Home />
</App>
2) /privacy
<Route pat"/" element={<App />}>
...
</Route>
<Route element={<PageLayout />}>
<Route path="/privacy" element={<Privacy />} />
<Route path="/tos" element={<Tos />} />
</Route>
<PageLayout>
<Privacy />
</PageLayout>
<PageLayout> 컴포넌트는 경로와 상관없는 컴포넌트로 생각하면 된다. 단지 레이아웃용 컴포넌트로, 중복되는 레이아웃이 있는 경우에 사용한다. 이러한 라우트 컴포넌트를 'Layout Routes'라고 부른다.
Navigating
URL이 바뀌는 것을 'Navigation'이라고 부른다. React Router에서는 두 가지 방법으로 네이게이팅을 할 수 있다.
1) <Link>
<Link to="path" />
2) Navigation Function
// useNavigate() 훅 사용
let navigate = useNavigate();
useEffect(() => {
setTimeout(() => {
navigate("/logout");
}, 30000);
}, []);
<li onClick={() => navigate("/somewhere")} />
useParams, useSearchParams
URL의 params와 쿼리스트링을 쉽게 받아올 수 있는 훅이다. 사용 방법은 아주아주 간단하다. 예시를 통해 알아보자.
1) useParams
// App.jsx
<Route path="/register/:id/:name" element={ <Component /> }/>
// Component.jsx
import { useParams } from 'react-router-dom'
function Component() {
const params = useParams();
console.log(params);
...
}
코드는 위와 같다. 이제 '/register/1/Charles' 으로 접속하면 콘솔창에 아래와 같은 객체가 찍힌다.
{
id: 1,
name: 'Charles'
}
2) useSearchParams
// App.jsx
<Route path="/register" element={ <Component /> }/>
// Component.jsx
import { useSearchParams } from 'react-router-dom'
function Component() {
const [ searchParams, setSearchParams ] = useSearchParams();
console.log(searchParams.get('id');
console.log(searchParams.get('name');
...
}
useSearchParams는 두 개의 함수를 반환한다. 먼저 searchParams는 get을 통해 쉽게 쿼리스트링 값을 가져오도록 한다. setSearchParams는 URL의 복잡도가 높아졌을때 쿼리스트링을 쉽게 적용시킬 수 있는 함수라고 생각하면 된다. 자세한 내용은 여기에 잘 정리되어 있으니 참고하길. 이
코드가 위와 같을때, '/register?id=1&name=Charles' 로 접속하면 콘솔에 '1' 과 'Charles' 이 출력될 것이다.
<참조>
https://reactrouter.com/docs/en/v6/getting-started/concepts
https://dohoons.com/blog/2117/
'React' 카테고리의 다른 글
[ React ] React Redux API (with 카운터 예제) (0) | 2022.06.25 |
---|---|
[ React ] useReducer를 알아보자 (0) | 2022.06.23 |
[ React ] useRef를 알아보자 (0) | 2022.06.23 |
[ React ] useEffect와 컴포넌트 생명주기 (0) | 2022.06.12 |
[ React ] Hook - 공식문서 뜯어보기 (0) | 2022.06.11 |