리액트

react-router-dom

dohpark 2021. 9. 15. 19:12

리액트는 안타깝게도 라우팅 기능을 내장 되어있지 않아 이를 도와줄 수 있는 라이브러리를 사용해야 합니다. 많고많은 리액트 라우터 라이브러리 중에 사람들이 가장 많이 사용하는 react-router-dom에 대하여 알아보겠습니다.  

설치

먼저 새로운 프로젝트 생성 후  해당 프로젝트 디렉토리로 이동하겠습니다.

npx create-react-app router-practice
cd router-practice

 

그 후 react-router-dom 라이브러리를 설치하겠습니다.

npm install react-router-dom

예시 1 :  기본 라우팅

import React from "react";
import {
  BrowserRouter as Router,
  Switch,
  Route,
  Link
} from "react-router-dom";

export default function App() {
  return (
    <Router>
      <div>
        <nav>
          <ul>
            <li>
              <Link to="/">Home</Link>
            </li>
            <li>
              <Link to="/about">About</Link>
            </li>
            <li>
              <Link to="/users">Users</Link>
            </li>
          </ul>
        </nav>

        {/* A <Switch> looks through its children <Route>s and
            renders the first one that matches the current URL. */}
        <Switch>
          <Route path="/about">
            <About />
          </Route>
          <Route path="/users">
            <Users />
          </Route>
          <Route path="/">
            <Home />
          </Route>
        </Switch>
      </div>
    </Router>
  );
}

function Home() {
  return <h2>Home</h2>;
}

function About() {
  return <h2>About</h2>;
}

function Users() {
  return <h2>Users</h2>;
}

 

위의 예시를 보시면 라우터가 3개의 페이지(home 페이지, about 페이지, users 페이지)를 처리하고 있습니다. 세개의 다른 <Links>를 클릭해보면 라우터는 <Route>에 맞는 페이지를 랜더링합니다.

 

위의 예시에 사용된 컴포넌트들을 자세히 보겠습니다.

Primary Components

React Router의 컴포넌트는 크게 3가지로 나눌 수 있습니다.

 

Routers

  • <BrowserRouter>
  • <HashRouter>

 

Route Matchers

  • <Route>
  • <Switch>

 

Navigation

  • <Link>
  • <NavLink>
  • <Redirect>

Routers

프로젝트에 react-router을 적용하기 위해서는 엘리먼트 계층중의 root에 렌더링이 되도록 설정해야합니다. 이 때문에 대부분 아래와 같이 <App> 엘리먼트를 감싸도록 작성합니다.

 

src/index

import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";

import "./index.css";
import App from "./App";

ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById("root")
);

 

<BrowserRouter>와 <HashedRouter>의 차이는 무엇일까요?

 

BrowserRouter

<BrowserRouter>은 우리가 흔히 사용하는 URL를 사용합니다.  URL를 사용하여 서버와 소통할 수 있으며, HTML5의 History API를 사용하여 페이지를 새로고침하지 않고도 주소를 변경할 수 있습니다.

 

HashedRouter

그 반면 <HashedRouter>을 사용하면 URL에 http://localhost:3000/#/users 이렇게 '#(해쉬)'를 발견할 수가 있습니다. 해쉬는 서버에 보내지지 않기에 서버와의 소통이 불가합니다. 그 때문에 주로 정적인 페이지를 작성할 때 사용합니다. 

Route Matchers

Route Matchers 컴포넌트에는 Switch와 Route가 있습니다. Switch 컴포넌트는 여러 Route를 감싸서 그 중 제일 처음 매칭되는 하나의 라우트만을 렌더링합니다. 만약에 맞는 Route가 없다면 Switch는 null을 렌더링합니다.

 

<Route path>의 특징

<Route>의 path는 전체 값이 정확히 일치하는지를 보지 않고 URL의 시작부터 매칭을 합니다. 그렇기 때문에 언제나 <Route path="/">와는 항상 매칭할 수밖에 없습니다. 

 

띠라서 아래의 경우와 같이 <Route path="/">를 가장 위에 작성했다면 /about과 /users으로 이동이 불가능합니다.

<Switch>
  <Route path="/">
    <Home />
  </Route>
  <Route path="/about">
    <About />
  </Route>
  <Route path="/users">
    <Users />
  </Route>
</Switch>;

확인해보시면 아래와 같이 작동이 안되는 것을 알 수가 있습니다.

 

이를 해결하기 위해 2가지 방법이 있습니다.

 

첫번째 방법은 exact를 사용하는 것입니다.

<Switch>
  <Route path="/" exact>
    <Home />
  </Route>
  <Route path="/about">
    <About />
  </Route>
  <Route path="/users">
    <Users />
  </Route>
</Switch>;

exact 속성을 주면 주어진 path값과 완벽히 일치해야 이동할 수 있도록 제한할 수 있습니다.

 

두번째 방법은 가장 아래에 두는 것입니다.

<Switch>
  <Route path="/about">
    <About />
  </Route>
  <Route path="/users">
    <Users />
  </Route>
  <Route path="/">
    <Home />
  </Route>
</Switch>;

가장 아래에 두면 path="/"를 가장 마지막에 찾기 때문에 이를 방지할 수가 있습니다.

Navigation

<Link>

리액트 라우터는 <Link>컴포넌트를 통해 다른 주소로 이동시켜 줄 수 있는 링크를 생성할 수 있습니다. <Link> 사용 시 HTML 다큐먼트에 <a>태그를 랜더링하여 사용합니다. 그러나 우리가 흔히 사용하는 <a>태그와 다르게 페이지 전환을 방지하는 기능을 내장하고 있어 페이지를 새로 부르지 않고 페이지의 주소만 변경하도록 합니다.

 

<NavLink>

<NavLink>는 <Link>와 같습니다만 "active" 상태일 때 스타일링을 할 수 있는 <Link>입니다.

아래와 같이 activeClassName을 주어 active 상태일 때 해당 css가 적용할 수 있습니다.

<NavLink to="/react" activeClassName="hurray">
  React
</NavLink>

 

또는 아래와 같이 activeStyle로 직접 active 상태일 때의 css를 줄 수가 있습니다.

<NavLink
  to="/faq"
  activeStyle={{
    fontWeight: "bold",
    color: "red"
  }}
>
  FAQs
</NavLink>



<Redirect>

<Redirect>는 강제로 해당 주소로 이동을 시킵니다.

<Route exact path="/">
  {loggedIn ? <Redirect to="/dashboard" /> : <PublicHomePage />}
</Route>

위와 같이 특정 조건에 강제로 다른 주소로 이동 시킬 때 사용할 수가 있습니다.

 

예시 2 :  Nested 라우팅

import React from "react";
import {
  BrowserRouter as Router,
  Switch,
  Route,
  Link,
  useRouteMatch,
  useParams
} from "react-router-dom";

export default function App() {
  return (
    <Router>
      <div>
        <ul>
          <li>
            <Link to="/">Home</Link>
          </li>
          <li>
            <Link to="/about">About</Link>
          </li>
          <li>
            <Link to="/topics">Topics</Link>
          </li>
        </ul>

        <Switch>
          <Route path="/about">
            <About />
          </Route>
          <Route path="/topics">
            <Topics />
          </Route>
          <Route path="/">
            <Home />
          </Route>
        </Switch>
      </div>
    </Router>
  );
}

function Home() {
  return <h2>Home</h2>;
}

function About() {
  return <h2>About</h2>;
}

function Topics() {
  let match = useRouteMatch();

  return (
    <div>
      <h2>Topics</h2>

      <ul>
        <li>
          <Link to={`${match.url}/components`}>Components</Link>
        </li>
        <li>
          <Link to={`${match.url}/props-v-state`}>
            Props v. State
          </Link>
        </li>
      </ul>

      {/* The Topics page has its own <Switch> with more routes
          that build on the /topics URL path. You can think of the
          2nd <Route> here as an "index" page for all topics, or
          the page that is shown when no topic is selected */}
      <Switch>
        <Route path={`${match.path}/:topicId`}>
          <Topic />
        </Route>
        <Route path={match.path}>
          <h3>Please select a topic.</h3>
        </Route>
      </Switch>
    </div>
  );
}

function Topic() {
  let { topicId } = useParams();
  return <h3>Requested topic ID: {topicId}</h3>;
}

 

위의 예시는 Topics 함수와 Topic 함수에 주목하시면 됩니다. /Topics 라우트는 Topics 컴포넌트를 paths :id 값에 따라 조건적으로 렌더링합니다.

 

 

위에 새로 사용한 useRouteMatch와 useParams에 대하여 알아보도록 하겠습니다.

useRouteMatch()

useRouteMatch 훅을 사용하면 match 오브젝트에 손쉽게 접근할 수가 있습니다.

 

match는 무엇일까요? 

match는 <Route path>가 어떻게 URL과 매칭이 되었는지에 관한 정보를 담은 오브젝트입니다. 

 

match는 아래의 값을 지니고 있습니다.

  • path : [string] 매칭에 사용된 path 패턴
  • url : [string] 매칭에 사용된 url 부분
  • isExact : [boolean] 전체 경로가 완전히 매칭할 때 true 반환 
  • params : [JSON object] url path로 전달된 파라미터 객체 

덕분에 match 오브젝트를 통해 url 및 파라미터 값을 손쉽게 접근할 수 있습니다.

 

사실 match와 같은 부가적인 정보를 지닌 오브젝트는 2개 더 있습니다. 위의 예제와 상관없지만 react-router을 구성하는 중요 오브젝트들이니 알아보도록 하겠습니다.

 

history

 

history 오브젝트는 현재까지 이동한 경로를 저장한 오브젝트입니다. 해당 오브젝트를 통해 특정 버튼을 눌렀을 시 뒤로 가거나, 로그인 후 화면을 전환할 때 등에 history를 활용할 수가 있습니다.

 

hisory는 아래의 값을 지닙니다.

 

  • length : [number] 전체 history 스택의 길이
  • action : [string] 최근에 수행된 action (PUSH, REPLACE or POP)
  • location : [JSON object] 최근 경로 정보
  • push(path, [state]) : [function] 새로운 경로를 history 스택으로 푸시하여 페이지를 이동
  • replace(path, [state]) : [function] 최근 경로를 history 스택에서 교체하여 페이지를 이동
  • go(n) : [function] : history 스택의 포인터를 n번째로 이동
  • goBack() : [function] 이전 페이지로 이동
  • goForward() : [function] 앞 페이지로 이동
  • block(prompt) : [function] history 스택의 PUSH/POP 동작을 제어

 

사용예시

  • block 메서드를 통하여 해당 페이지를 떠날 시 '정말로 떠나겠습니까?'와 같은 문구가 나타나도록 할 수 있습니다.
  • push 메서드를 통하여 다른 경로로 이동할 수 있습니다.
  • goBack goForward 메서드를 통해 전 후 페이지를 다시 찾아 갈 수 있습니다.

 

Hook

let history = useHistory();

history 또한 match와 마찬가지로 훅을 통해 접근할 수가 있습니다.

 

location

 

location 오브젝트는 현재 페이지에 대한 정보를 지닙니다.

 

location은 아래의 값을 지닙니다.

 

  • pathname : [string] 현재 페이지의 경로명
  • search : [string] 현재 페이지의 query string
  • hash : [string] 현재 페이지의 hash

 

사용예시

  • location은 아래와 같이 <Link to>에 사용도 가능하며 history에 location 값을 넣을 때도 사용합니다.
const location = {
  pathname: '/somewhere',
  state: { fromDashboard: true }
}

<Link to={location}/>
<Redirect to={location}/>
history.push(location)
history.replace(location)

Hook

let location = useLocation();

 

location 또한 useLocation() 훅을 통해 접근할 수가 있습니다.

 

useParams()

useParams() 훅은 url 파라미터를 key/value 페어 형태인 오브젝트를 반환합니다.

 

참고자료

 

React | Router : match, location, history

Router Props  브라우저와 리액트앱의 라우터를 연결하게 되면 그 결과 라우터가 history api에 접근할 수 있게 되며 각각의 Route와 연결된 컴포넌트에 props로 match, location, history라는 객체를 전달하게..

gongbu-ing.tistory.com

 

React Router: Declarative Routing for React

Learn once, Route Anywhere

reactrouter.com