JavaScript/React

React - React Router DOM

제이널 2022. 1. 26. 11:45

 

이 글은 react-router-dom 6 버전을 기준으로 작성되었습니다.


1. React Router?

  • React를 위한 클라이언트 측 라우팅 라이브러리입니다.
  • URL이 바뀌면 페이지를 새로고침하지 않고, 필요한 부분만 로드합니다.
  • React는 기본적으로 UI 구축에만 초점을 맞추기 때문에, 라우팅을 위한 내장 프로그램이 없습니다.
  • create-react-app 구성에 기본적으로 포함되어 있지 않기 때문에, 별도로 설치해야 합니다.
  • https://reactrouter.com/docs/en/v6/getting-started/concepts#main-concepts

 

React Router vs. React Router DOM

 

2. react-router-dom 최신 버전(v6) 설치

$ npx create-react-app react-router-dom-test
$ cd react-router-dom-test
$ npm i react-router-dom

 

3. 사용해보기

App.js 수정

import { BrowserRouter, Routes, Route } from 'react-router-dom';

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/profile" element={<Profile />} />
        <Route path="/about" element={<About />} />
      </Routes>
    </BrowserRouter>
  );
}

function Home() {
  return <div>Home 페이지</div>;
}

function Profile() {
  return <div>Profile 페이지</div>;
}

function About() {
  return <div>About 페이지</div>;
}

export default App;
  • BrowserRouter : HTML5 환경에서 라우터를 구현할 수 있게 해주는 API 컴포넌트(HTML5를 지원하지 않는 구형 브라우저에서 동작하지 않음)
  • Routes : v6에서 새로 추가된 <Switch> 대체 컴포넌트
  • Route : 현재 URL 경로가 path와 일치할 때 element를 렌더링

 

개발 서버 시작 후 접속 테스트

$ PORT=8080 npm start

4. Dynamic 라우팅

  • URL을 동적으로 매핑시키는 라우팅 방법입니다.
/* App.js */

import { BrowserRouter, Route, Routes, Outlet, useParams } from "react-router-dom";

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/profile" element={<Profile />}>
          <Route path=":id" element={<Profile />} />
          <Route path=":id/:name" element={<Profile />} />
        </Route>
        <Route path="/*" element={<Error />} />
      </Routes>
    </BrowserRouter>
  );
}

function Home() {
  return <div>Home 페이지입니다.</div>;
}

function Profile() {
  const parm = useParams();
  return (
    <>
      <div>Profile 페이지입니다.</div>
      <div>id : { parm.id }</div>
      <div>name : { parm.name }</div>
      {/* <Outlet />  */}
    </>
  )
}

function Error() {
  return <div>Error 페이지입니다.</div>;
}

export default App;
  • /profile : 부모 Route의 element 컴포넌트를 렌더링
  • /profie/123 : 자식 Route 중, :id와 매핑시켜서, 부모 Route의 element 컴포넌트를 렌더링하고 파라미터 전달
  • /profile/123/123 : 자식 Route 중, :id, :name과 매핑시켜서, 부모 Route의 element 컴포넌트를 렌더링하고 파라미터 전달
  • /profile/123/123?age=1 : react-router-dom의 useLocation()과 query-string의 queryString.parse()를 사용해 Query String을 캐치 가능
  • /profile/123/123/123 : 매핑되는 URL이 없으므로, path="/*"를 가지는 Route의 컴포넌트를 렌더링
  • <Outlet /> : 부모 Route 컴포넌트를 렌더링하고, 매핑된 자식 Route 컴포넌트도 함께 렌더링

 

 

<Outlet /> 적용

 

5. Dynamic 라우팅 - Link

  • React는 SPA로 동작하기 때문에 <a> 태그를 사용하면 페이지를 새로 로드해서 다시 렌더링 하는 특징이 있습니다.
  • react-router-dom의 Link 컴포넌트를 사용하면, HTML5 History API를 사용해 URL만 바꾸고, 페이지를 새로 불러오지 않습니다.
/* App.js */

import { ..., Link } from "react-router-dom";

function App() {
  return (
    <BrowserRouter>
      <ul>
        <li><Link to="/">Home</Link></li>
        <li><Link to="/profile">Profile</Link></li>
        <li><Link to="/profile/123/123">Profile/123/123</Link></li>
        <li><Link to="/erroroccured">Error Page</Link></li>
      </ul>
      <Routes>
        ...
      </Routes>
    </BrowserRouter>
  );
}

...

export default App;
  • Link 컴포넌트는 <a> 태그의 href 대신 to를 사용해 이동할 URL을 정의할 수 있습니다.

 

6. Dynamic 라우팅 - NavLink

  • Link 컴포넌트와 비슷하지만, 현재 활성화된 NavLink에 스타일 또는 클래스 속성을 토글해 줄 수 있습니다.
  • NavLink의 두 속성이 활성화 되려면, 현재 브라우저의 URL과 to 속성의 URL이 일치해야 합니다.
/* App.js */

import { ..., NavLink } from "react-router-dom";

function App() {
  return (
    <BrowserRouter>
      <ul>
        <li><NavLink to="/" style={ ({ isActive }) => isActive ? {color:'red'}:undefined }>Home</NavLink></li>
        <li><NavLink to="/profile" className={ ({ isActive }) => isActive ? 'active':''}>Profile</NavLink></li>
        <li>
          <NavLink to="/profile/123/123">
            {({ isActive }) => (
              <span style={ isActive ? {color:"green"}:undefined}>Profile/123/123</span>
            )}
          </NavLink>
        </li>
        ...
      </ul>
      <Routes>
        ...
      </Routes>
    </BrowserRouter>
  );
}

...

export default App;
  • v5에서 사용하던 activeStyle과 activeClassName은 없어졌고, v6에선 함수 형태로 정의할 수 있습니다.
  • isActive 값이 들어있는 객체를 인자로 구조분해할당 받아서, 현재 NavLink가 활성화 되었는지 확인할 수 있습니다.
  • 컴포넌트의 내용을 함수로 작성할 땐, 함수와 형제 요소가 없어야 합니다.

 

7. JS로 라우팅 이동하기

  • JS에서 useNavigate 훅을 사용해 URL을 이동시킬 수 있습니다.
/* App.js */

import ... from 'react-router-dom';
import Login from './pages/Login';

function App() {
  return (
    <BrowserRouter>
      ...
      <Routes>
        <Route path="/login" element={<Login />} />
        ...
      </Routes>
    </BrowserRouter>
  );
}

...

export default App;
/* Login.jsx */

import { useNavigate } from 'react-router-dom';

export default function Login() {
  const navigate = useNavigate();
    
  function login() {
    setTimeout(()=>{
      navigate('/');
    }, 1000);
  }

  return (
    <div>
      <h2>Login 페이지입니다.</h2>
      <button onClick={login}>로그인하기</button>
    </div>
  )
}
  • useNavigate : v5에서 사용하던 props.history 대체 훅입니다. Programmatically 하게 URL을 이동시킬 수 있게 해줍니다.
  • navigate('/') : v5에서 사용하던 props.history.push의 대체입니다. 인자로 Route에 등록한 path로 이동시킵니다.

 

8. Navigate

  • URL을 Route 경로로 redirect 해주는 컴포넌트입니다.
/* App.js */

import { ..., Navigate } from "react-router-dom";

const isLogin = false;

function App() {
  return (
    <BrowserRouter>
      ...
      <Routes>
        ...
        <Route path="/login" element={<Login />} />
        <Route path="/profile" element={isLogin ? <Profile />:<Navigate to="/login"/>} >
          ...
        </Route>
        ...
      </Routes>
    </BrowserRouter>
  );
}

...

function Login() {
  return (
    <div>
      <h2>Login 페이지입니다.</h2>
    </div>
  )
}

export default App;
  • 로그인이 되지 않은 상태에서 프로필 화면에 접근하려 하면 login 페이지로 redirect 시켜주고 있습니다.
  • 삼항 연산자 부분에서 <Navigate> 대신, <Login />을 사용해도 되지만,Login 컴포넌트 수정되었을 때, 모두를 바꿔줘야 하는 측면에선 redirect가 유리하다고 생각합니다.