본문 바로가기

React

[ React ] React Router 사용법 (v6)

반응형

 

이번 글에서는 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/  

 

반응형