본문 바로가기

React Router 소개

본 챕터는 React Router v6.4 이상의 Data API 방식을 기준으로 작성되었습니다. 기존의 BrowserRouter 방식은 [부록] BrowserRouter 방식을 참고 바랍니다.

React Router 공식 문서

1. React Router란?

React Router는 단일 페이지 애플리케이션(SPA)에서 페이지 이동을 관리하는 라이브러리입니다.

예를 들면 books.weniv.co.kr에서 books.weniv.co.kr/essentials-react로 URL이 변경될 때, 사용자에게 보이는 화면이 바뀌게 됩니다. 이는 실제로 서버에서 새로운 페이지를 불러오는 것이 아니라 React Router가 변경된 URL을 감지하고, 그에 맞는 컴포넌트를 다시 보여주는 것으로 브라우저가 새로운 페이지를 요청하지 않고, JavaScript를 사용하여 현재 페이지의 내용을 변경합니다.

즉 React Router는 URL에 따라 해당하는 컴포넌트를 동적으로 렌더링하는 기능을 제공합니다. 이러한 방식으로 React Router는 사용자에게 웹 사이트가 여러 페이지로 구성된 것처럼 보이게 하면서도, 실제로는 단일 페이지 내에서 콘텐츠만 교체합니다.

2. 설치하기

먼저 Vite를 사용하여 프로젝트를 생성해줍니다.

npx my-react-vite-app [프로젝트 이름]

cd [프로젝트 이름]      # 프로젝트로 이동
npm install    # 의존성 설치

React에서 라우터를 사용하기 위해서는 react-router-dom을 설치해야 합니다. 아래 명령어 중 하나를 사용하여 설치해주세요. 아래는 최신 버전으로 설치하는 명령어입니다.

npm install react-router-dom
# 또는
npm install react-router-dom@latest

만약 에러가 발생했다면

Window에서 설치할 때 [eslint] Plugin "react" was conflicted 에러를 만나실 수 있습니다. 이는 플러그인 충돌로 나오는 문제이며 이를 해결하기 위해 Git Bash 터미널 창을 하나 더 열어 작업하시는 것을 권해드립니다.

3. createBrowserRouter와 RouterProvider

React Router 6.4 버전부터는 createBrowserRouter와 RouterProvider를 사용한 라우팅 방식을 권장합니다. 이 방식은 기존의 <BrowserRouter> 컴포넌트 방식보다 더 많은 기능과 더 나은 개발 경험을 제공합니다.

먼저 간단한 실습을 진행해보며 기본적인 라우팅을 구현해보겠습니다.

import { createBrowserRouter, RouterProvider } from 'react-router-dom';

const router = createBrowserRouter([
  {
    path: '/',
    element: <Index />,
  },
  {
    path: '/one',
    element: <One />,
  },
  {
    path: '/two',
    element: <Two />,
  },
  {
    path: '/three',
    element: <Three />,
  },
]);

function App() {
  return <RouterProvider router={router} />;
}

function Index() {
  return <h1>home</h1>;
}

function One() {
  return <h1>one</h1>;
}

function Two() {
  return <h1>two</h1>;
}

function Three() {
  return <h1>three</h1>;
}

export default App;

이제 접속하는 URL에 따라 해당하는 화면(컴포넌트)이 바뀌는 걸 확인해보겠습니다. 프로젝트를 시작하면 첫 화면에서 "home"이라는 문구가 화면에 표시됩니다. 이제 주소창의 URL 뒤에 /one을 붙여 이동해보겠습니다. 이번에는 "one"이라는 문구가 화면에 표시됩니다. 그리고 localhost:5173/two로 접속하면 "two"가 화면에 표시되고, localhost:5173/three로 접속하면 "three"가 화면에 표시됩니다.

위 코드에서 사용된 주요 요소들을 살펴보겠습니다.

  1. createBrowserRouter : 라우터 객체를 생성하는 함수입니다. 배열 형태로 라우트 설정을 전달하며, 각 라우트는 path(경로)와 element(렌더링할 컴포넌트)를 포함합니다.
  2. RouterProvider : 생성된 라우터 객체를 받아 애플리케이션에 라우팅 기능을 제공하는 컴포넌트입니다. router props로 라우터 객체를 전달합니다.

3.1 라우트 객체의 구성

createBrowserRouter에 전달하는 배열의 각 객체는 다음과 같은 속성을 가질 수 있습니다.

const router = createBrowserRouter([
  {
    path: '/products', // URL 경로
    element: <Products />, // 렌더링할 컴포넌트
    loader: productsLoader, // 데이터 로딩 함수 (선택)
    errorElement: <Error />, // 에러 발생 시 표시할 컴포넌트 (선택)
    children: [
      // 중첩 라우트 (선택)
      {
        path: ':id',
        element: <ProductDetail />,
      },
    ],
  },
]);
  • path : URL 경로를 정의합니다. 정적 경로(/about)나 동적 경로(/products/:id)를 지정할 수 있습니다.
  • element : 해당 경로에서 렌더링할 React 컴포넌트를 지정합니다.
  • loader : 컴포넌트가 렌더링되기 전에 필요한 데이터를 불러오는 비동기 함수입니다. (5.5에서 자세히 다룹니다)
  • errorElement : 해당 라우트에서 오류가 발생했을 때 표시될 컴포넌트입니다. (5.5에서 자세히 다룹니다)
  • children : 중첩 라우트를 정의하는 배열입니다. (5.4에서 자세히 다룹니다)

4. Component props 전달하기

컴포넌트에 props를 전달하는 방법은 기존 방식과 동일합니다.

import { createBrowserRouter, RouterProvider } from 'react-router-dom';

const router = createBrowserRouter([
  {
    path: '/',
    element: <Index />,
  },
  {
    path: '/one',
    element: <One name="react" />,
  },
  {
    path: '/two',
    element: <Two />,
  },
  {
    path: '/three',
    element: <Three />,
  },
]);

function App() {
  return <RouterProvider router={router} />;
}

function Index() {
  return <h1>home</h1>;
}

function One({ name }) {
  return <h1>{name} world</h1>;
}

function Two() {
  return <h1>two</h1>;
}

function Three() {
  return <h1>three</h1>;
}

export default App;

위 코드에서 <One /> 컴포넌트에 name이라는 props를 전달해주고 있습니다. 이제 localhost:5173/one으로 접속하면 "react world"라는 문구가 화면에 표시됩니다.

5. 파일 분리하기

실제 프로젝트에서는 라우터 설정과 페이지 컴포넌트를 별도의 파일로 분리하여 관리합니다.

[폴더 구조 예시]

src/
├── pages/
│ ├── Index.jsx
│ ├── One.jsx
│ ├── Two.jsx
│ └── Three.jsx
├── router.jsx
├── App.jsx
└── main.jsx
// src/pages/Index.jsx
export default function Index() {
  return <h1>home</h1>;
}
// src/pages/One.jsx
export default function One({ name }) {
  return <h1>{name} world</h1>;
}
// src/router.jsx
import { createBrowserRouter } from 'react-router-dom';
import Index from './pages/Index';
import One from './pages/One';
import Two from './pages/Two';
import Three from './pages/Three';

const router = createBrowserRouter([
  {
    path: '/',
    element: <Index />,
  },
  {
    path: '/one',
    element: <One name="react" />,
  },
  {
    path: '/two',
    element: <Two />,
  },
  {
    path: '/three',
    element: <Three />,
  },
]);

export default router;
// src/App.jsx
import { RouterProvider } from 'react-router-dom';
import router from './router';

function App() {
  return <RouterProvider router={router} />;
}

export default App;

이렇게 파일을 분리하면 코드의 가독성이 높아지고, 유지보수가 쉬워집니다.

React Router 소개 - React 에센셜 | 위니버시티