React Router 사용해서 라우팅 하기

클라이언트 측 라우팅을 위한 React Router

React Router는 React로 만들어진 웹 애플리케이션에서 클라이언트 측 라우팅을 구현하기 위한 라이브러리이다. 라우팅이란 ‘어떤 네트워크 안에서 통신 데이터를 보낼 때 최적의 경로를 선택하는 과정’ 이라고 한다. 이를 토대로 웹 사이트의 어느 주소(URL)에 접근했을 때 그에 맞는 적절한 화면을 보여 주는 기능으로 생각할 수 있을 것 같다.

기존의 전통적인 서버 측 라우팅은 사용자가 어떤 페이지에 접근하면 URL에 해당하는 페이지의 전체 HTML을 새로 생성하여 전송한다. 사용자가 다른 페이지로 이동할 때마다 새로운 요청이 서버로 전송되고, 브라우저는 서버로부터 응답받아 새 HTML로 전체 페이지를 다시 로드한다.

클라이언트 측 라우팅은 페이지 전환을 브라우저에서 처리하는 방식이다. 싱글 페이지 애플리케이션(SPA)에서는 새로운 페이지의 요청을 서버에 보내 전체 페이지를 새로 불러오는 대신 필요한 데이터만 서버로부터 비동기적으로 가져와서 현재 페이지를 업데이트한다. 이는 애플리케이션에서 사용자에게 더 빠른 사용자 경험을 제공한다.

클라이언트 측 라우팅과 서버 측 라우팅 비교

React Router와 React Router Dom의 차이

React Router는 웹(React Router Dom)뿐 아니라 모바일 앱(React Router Native)을 위한 라우팅 기능을 제공하는 반면, React Router Dom은 웹 애플리케이션용으로 특별히 설계된 React Router의 확장 라이브러리이다.

React Router Vs React Router DOM

React Router Dom은 브라우저의 history API를 활용하여 URL을 동적으로 관리하고 웹 페이지 간의 라우팅을 가능하게 하는 컴포넌트(BrowserRouter, Link, Route 등)와 API를 제공한다. ‘웹 애플리케이션’을 개발하고 더 쉽게 라우팅을 하기 위해서는 React Router Dom을 권장한다.

DOM의 Window 객체는 history 객체를 통해 브라우저의 세션 기록에 접근할 수 있는 방법을 제공합니다. history는 사용자를 자신의 방문 기록 앞과 뒤로 보내고 기록 스택의 콘텐츠도 조작할 수 있는, 유용한 메서드와 속성을 가집니다. - MDN

<a
  href="/contact"
  onClick={(event) => {
    event.preventDefault();
    window.history.pushState({}, undefined, "/contact");
  }}
/>

(+) 동작 원리 알아보기

[React] react-router 동작 원리 간단히 알아보기

리액트 라우터 만들기

React Router DOM을 사용해서 라우팅하기

React Router의 공식 문서를 활용 v6.22.3

1. React Router DOM 설치

yarn add react-router-dom

2. 라우터 추가

createBrowserRouter를 사용하여 라우터를 생성하고, 애플리케이션의 라우트 구조를 정의한다. createBrowserRouter는 라우터 구성을 객체 형태로 선언적으로 만들 수 있게 해주며, 이를 통해 라우트의 구조와 관련 컴포넌트를 보다 명확하게 구성할 수 있다.

path는 사용자가 특정 위치(URL)로 이동했을 때 보여줄 페이지 또는 컴포넌트를 결정하는 규칙이다. element는 특정 path로 이동했을 때 실제로 화면에 렌더링될 컴포넌트를 지정한다.

// Router.tsx
import { createBrowserRouter } from 'react-router-dom';

export const router = createBrowserRouter([
  {
    path: "/",
    element: <div>Hello world!</div>,
  },
]);

3. 라우터를 전체 애플리케이션에 적용

생성한 라우터를 애플리케이션에 적용하기 위해 RouterProvider 컴포넌트를 사용한다. 이 컴포넌트에 router prop을 전달하여, 애플리케이션 전체에 라우팅을 적용할 수 있다.

main.tsx은 애플리케이션의 진입점으로 사용되는 파일이다. React 애플리케이션의 최상위 컴포넌트로서, 애플리케이션을 구성하는 루트 컴포넌트를 렌더링하는 역할을 한다.

// main.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { RouterProvider } from 'react-router-dom';
import { router } from './Router';

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <RouterProvider router={router} />
  </React.StrictMode>
);

4. 페이지 및 컴포넌트 구현

기능에 따라 필요한 페이지를 정의해보았다.

  • / - 전체 아티클을 볼 수 있는 메인 페이지
    • /topics/:major - 대분류 주제에 대한 페이지
    • /topics/:major/:sub - 소분류 주제에 대한 상세 페이지
    • /bookmark - 사용자가 북마크한 콘텐츠나 페이지를 모아 볼 수 있는 페이지
    • /profile/:id - 사용자의 프로필 페이지
    • /search/:query - 사용자가 입력한 검색 결과를 보여주는 페이지
    • *: 정의되지 않은 경로에 접근했을 때 보여주는 페이지

이를 토대로 페이지 컴포넌트를 생성하고 해당 경로에 해당하는 컴포넌트를 렌더링하도록 라우터 설정을 변경한다.

📦linkeeper
 ┣ 📂src
 ┃ ┣ 📂pages
 ┃ ┃ ┣ 📜Bookmark.tsx
 ┃ ┃ ┣ 📜Home.tsx
 ┃ ┃ ┣ 📜index.ts
 ┃ ┃ ┣ 📜MajorTopic.tsx
 ┃ ┃ ┣ 📜NotFound.tsx
 ┃ ┃ ┣ 📜Profile.tsx
 ┃ ┃ ┣ 📜SearchResults.tsx
 ┃ ┃ ┗ 📜SubTopic.tsx
// Router.tsx
import { createBrowserRouter } from 'react-router-dom';
import Root from './Root';
import {
  Home,
  NotFound,
  MajorTopic,
  SubTopic,
  Bookmark,
  SearchResults,
  Profile,
} from './pages';

export const router = createBrowserRouter([
  {
    path: '/',
    element: <Root />,
    errorElement: <NotFound />, // 오류 발생(예: 경로 매칭 실패) 시 보여줄 컴포넌트를 지정
    children: [
      // index 속성은 상위 경로의 경로가 정확히 일치할 때 렌더링되는 기본 경로를 정의
      { path: '/', element: <Home />, index: true },
      {
        path: '/topics/:major',
        element: <MajorTopic />,
      },
      {
        path: '/topics/:major/:sub',
        element: <SubTopic />,
      },
      { path: '/bookmark', element: <Bookmark /> },
      { path: '/search/:query', element: <SearchResults /> },
      { path: '/profile/:id', element: <Profile /> },
    ],
  },
]);

데이터의 식별자(ID)나 어떤 변수 값이 URL에 사용될 때 애플리케이션의 URL 경로 일부가 변하는 경우가 있다. React Router에서는 이렇게 동적으로 변하는 URL을 처리하기 위해 동적 세그먼트를 사용한다.

예를 들어 프로필 페이지의 경로를 설정할 때 각 사용자의 고유 ID로 페이지를 구분한다고 하면 /profile/:id와 같이 경로를 설정할 수 있다. :id는 동적으로 변경되는 부분으로, 실제 사용자 ID로 대체된다. URL과 문자열이 일치하지는 않지만 동적으로 일치한다는 의미이다.

동적 경로를 사용하면 하나의 경로 설정으로 다양한 페이지를 처리할 수 있다.

// Root.tsx
import { Outlet } from 'react-router-dom';

export default function Root() {
  return (
    <>
      {/* TODO: Header, Footer, Main 등 레이아웃 요소 추가 */}
      <Outlet />
    </>
  );
}

Outlet 컴포넌트는 중첩 라우팅을 구현할 때 사용한다. 라우터에서 정의한 경로와 일치하는 컴포넌트로 대체되어 렌더링된다. 예를 들어 /bookmark로 이동하면 Outlet 컴포넌트는 URL과 매칭되는 컴포넌트 <Bookmark />로 대체된다.

export const router = createBrowserRouter([
  {
    path: '/',
    element: <Root />,
    errorElement: <NotFound />,
    children: [
      { path: '/', element: <Home />, index: true },
      {
        path: '/topics/:major',
        element: <MajorTopic />,
      },
      {
        path: '/topics/:major/:sub',
        element: <SubTopic />,
      },
      { path: '/bookmark', element: <Bookmark /> },
      { path: '/search/:query', element: <SearchResults /> },
      { path: '/profile/:id', element: <Profile /> },
    ],
  },
]);

중첩 라우팅 구조를 구성하기 위해 children 속성을 사용하여 하위 라우트를 정의할 수 있다. 위에서 정의한 라우터는 / 루트 경로의 자식으로 각 경로들이 정의되어 있다.

Outlet을 사용하면, 부모-자식 관계에 있는 라우트 간에 컴포넌트를 효과적으로 구성할 수 있다. 이를 활용한 예로 공통 레이아웃 요소를 여러 페이지에서 사용할 수 있게 하는 것이다.

image-20240328212715470

공통 레이아웃인 Header와 Sidebar, Footer는 그대로 두고 Main 영역에서만 페이지마다 다른 컨텐츠를 보여주고자 한다. Root.tsx 애플리케이션의 루트에서 공통 레이아웃을 정의하고 자식 라우트의 컴포넌트가 렌더링될 위치를 Outlet으로 지정한다. 이렇게 하면 공통 레이아웃을 유지하면서 페이지마다 고유한 컨텐츠를 렌더링 할 수 있다.

구현

result

참고자료

카테고리:

업데이트:

댓글남기기