안동민 개발노트 아이콘

안동민 개발노트

6장 : React 라우팅 기초

React Router 사용법

React Router 사용법에서 렌더링 흐름, 상태 경계, 사용자 반응을 정리한 것입니다.

이제 SPA에서 가장 중요한 기능 중 하나인 클라이언트 사이드 라우팅을 구현하기 위한 필수 라이브러리, React Router의 사용법을 배워보겠습니다.

React Router는 리액트 컴포넌트를 URL 경로와 연결해, 브라우저 URL이 변경될 때 해당 경로에 맞는 컴포넌트를 렌더링하도록 돕는 라이브러리입니다. 페이지 전체를 새로고침하지 않고도 마치 여러 페이지를 이동하는 듯한 사용자 경험을 제공할 수 있습니다.


React Router 설치

React Router를 사용하려면 먼저 프로젝트에 설치해야 합니다. 터미널에서 다음 명령어를 실행하세요.

npm install react-router-dom
# 또는
yarn add react-router-dom

react-router-dom은 웹 애플리케이션용 React Router를 포함하고 있습니다. (React Native용은 react-router-native)


React Router 주요 컴포넌트

React Router를 사용하기 위해 가장 기본적으로 알아야 할 컴포넌트들은 다음과 같습니다.

BrowserRouter (또는 HashRouter): 라우팅을 적용할 컴포넌트들을 감싸는 최상위 라우터 컴포넌트입니다.

  • BrowserRouter: HTML5 History API를 사용하여 깔끔한 URL(example.com/about)을 만듭니다. 서버 설정이 필요할 수 있습니다. (가장 일반적으로 사용)
  • HashRouter: URL에 # 기호(example.com/#/about)를 사용하여 라우팅을 처리합니다. 서버 설정이 필요 없습니다. (정적 파일 서버 환경에 유용)

Routes: 여러 Route들을 그룹화하고, 현재 URL과 일치하는 첫 번째 Route를 렌더링합니다. React Router v6부터 Switch 대신 사용됩니다.

Route: 특정 URL 경로에 어떤 컴포넌트를 렌더링할지 정의합니다.

  • path prop: 매칭될 URL 경로
  • element prop: 렌더링할 리액트 컴포넌트

Link (또는 NavLink): 페이지 이동을 위한 링크를 생성합니다. HTML의 <a> 태그와 유사하지만, 페이지 전체를 새로고침 하지 않고 클라이언트 사이드 라우팅을 수행합니다.

  • to prop: 이동할 URL 경로
  • NavLink: 현재 활성화된 경로에 따라 active 클래스나 스타일을 적용할 수 있어 내비게이션 바 등에 유용합니다.

useNavigate Hook: 컴포넌트 내에서 JavaScript 코드로 페이지를 이동해야 할 때 사용하는 훅입니다. (예: 폼 제출 후 리다이렉트)

useParams Hook: URL 경로에 포함된 동적인 파라미터 값을 가져올 때 사용하는 훅입니다.


기본 라우팅 구현 예제

간단한 내비게이션 바와 세 개의 페이지(Home, About, Contact)를 가진 SPA를 만들어 봅시다.

페이지 컴포넌트 생성

src/pages 폴더를 생성하고 Home.js, About.js, Contact.js 파일을 만듭니다.

src/pages/Home.js
import React from 'react';

function Home() {
  return (
    <div style={{ padding: '20px', textAlign: 'center' }}>
      <h2 style={{ color: '#2ecc71' }}>환영합니다!</h2>
      <p style={{ fontSize: '1.1em', color: '#555' }}>
        이곳은 홈페이지입니다. 다양한 기능을 탐색해 보세요.
      </p>

    </div>
  );
}

export default Home;
src/pages/About.js
import React from 'react';

function About() {
  return (
    <div style={{ padding: '20px', textAlign: 'center' }}>
      <h2 style={{ color: '#3498db' }}>회사 소개</h2>
      <p style={{ fontSize: '1.1em', color: '#555' }}>
        저희 회사에 대해 더 자세히 알아보세요.
      </p>

    </div>
  );
}

export default About;
src/pages/Contact.js
import React from 'react';
import { useNavigate } from 'react-router-dom'; // useNavigate 훅 임포트

function Contact() {
  const navigate = useNavigate(); // useNavigate 훅 사용

  const goToHome = () => {
    navigate('/'); // '/' 경로로 이동
  };

  return (
    <div style={{ padding: '20px', textAlign: 'center' }}>
      <h2 style={{ color: '#e74c3c' }}>문의하기</h2>
      <p style={{ fontSize: '1.1em', color: '#555' }}>
        문의 사항은 프로젝트에서 안내한 공식 채널을 통해 접수할 수 있습니다.
      </p>

      <button
        onClick={goToHome}
        style={{
          marginTop: '30px',
          padding: '10px 20px',
          backgroundColor: '#e74c3c',
          color: 'white',
          border: 'none',
          borderRadius: '5px',
          cursor: 'pointer',
          fontSize: '1em'
        }}
      >
        홈으로 돌아가기
      </button>
    </div>
  );
}

export default Contact;

내비게이션 바 컴포넌트 생성

src/components/Navbar.js 파일을 만들고 NavLink를 사용하여 링크를 만듭니다.

src/components/Navbar.js
import React from 'react';
import { NavLink } from 'react-router-dom'; // NavLink 임포트

function Navbar() {
  const navLinkStyle = ({ isActive }) => {
    return {
      fontWeight: isActive ? 'bold' : 'normal',
      color: isActive ? '#007bff' : '#333',
      textDecoration: 'none',
      padding: '10px 15px',
      margin: '0 10px',
      borderRadius: '5px',
      transition: 'all 0.3s ease',
      backgroundColor: isActive ? '#e7f5ff' : 'transparent',
    };
  };

  return (
    <nav style={{
      backgroundColor: '#f8f9fa',
      padding: '15px 0',
      boxShadow: '0 2px 5px rgba(0,0,0,0.1)',
      display: 'flex',
      justifyContent: 'center',
      gap: '20px'
    }}>
      <NavLink to="/" style={navLinkStyle}>Home</NavLink>
      <NavLink to="/about" style={navLinkStyle}>About</NavLink>
      <NavLink to="/contact" style={navLinkStyle}>Contact</NavLink>
      <NavLink to="/product/123" style={navLinkStyle}>Product (예시)</NavLink> {/* 동적 라우팅을 위한 예시 */}
    </nav>
  );
}

export default Navbar;
  • navLinkStyle 함수는 isActive prop을 받아 현재 경로가 활성화되었는지 여부에 따라 동적으로 스타일을 적용합니다.

App.js에 라우터 설정

src/App.js 파일에서 BrowserRouter, Routes, Route 컴포넌트를 사용하여 라우팅을 설정합니다.

src/App.js
import React from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom'; // 필요한 컴포넌트 임포트
import Navbar from './components/Navbar'; // Navbar 컴포넌트
import Home from './pages/Home';
import About from './pages/About';
import Contact from './pages/Contact';
import ProductDetail from './pages/ProductDetail'; // 다음 예제를 위한 컴포넌트

function App() {
  return (
    // 최상위에 BrowserRouter로 감싸줍니다.
    <BrowserRouter>
      <div className="App">
        <Navbar /> {/* 내비게이션 바는 모든 페이지에서 보입니다. */}

        {/* Routes 컴포넌트 안에 모든 Route들을 정의합니다. */}
        <Routes>
          {/* path="/" 경로에 Home 컴포넌트 렌더링 */}
          <Route path="/" element={<Home />} />
          {/* path="/about" 경로에 About 컴포넌트 렌더링 */}
          <Route path="/about" element={<About />} />
          {/* path="/contact" 경로에 Contact 컴포넌트 렌더링 */}
          <Route path="/contact" element={<Contact />} />
          {/* 동적 파라미터를 갖는 경로: :id 부분은 동적으로 변할 수 있습니다. */}
          <Route path="/product/:id" element={<ProductDetail />} />
          {/* 일치하는 경로가 없을 때 렌더링될 컴포넌트 (404 Not Found) */}
          <Route path="*" element={
            <div style={{ textAlign: 'center', padding: '50px', color: '#888' }}>
              <h2>404 Not Found</h2>
              <p>페이지를 찾을 수 없습니다.</p>
            </div>
          } />
        </Routes>
      </div>
    </BrowserRouter>
  );
}

export default App;

기본 라우팅 동작 확인

npm run dev (또는 yarn dev) 명령어를 실행하여 애플리케이션을 시작하고 브라우저에서 http://localhost:5173에 접속합니다.

  • 내비게이션 링크를 클릭하여 페이지가 새로고침되지 않고 내용만 바뀌는 것을 확인합니다.
  • 브라우저의 뒤로 가기/앞으로 가기 버튼이 정상적으로 작동하는지 확인합니다.
  • URL을 직접 http://localhost:5173/about 등으로 변경해도 해당 페이지가 렌더링되는지 확인합니다.
  • Contact 페이지의 홈으로 돌아가기 버튼을 클릭했을 때 useNavigate 훅을 통해 페이지가 이동하는 것을 확인합니다.

라우팅 코드를 읽을 때는 링크 클릭, URL 변경, Route 매칭, 페이지 렌더링이 한 줄로 이어진다고 보면 됩니다. 다음 다이어그램은 기본 라우팅과 동적 파라미터로 넘어가기 전의 판단 기준을 정리합니다.


동적 라우팅과 useParams

많은 웹 애플리케이션은 특정 아이템의 상세 페이지(product/1, users/profile/john-doe)와 같이 URL 경로에 동적인 값을 포함해야 합니다. React Router는 이를 경로 파라미터(Path Parameters)를 통해 지원합니다.

ProductDetail.js 생성

src/pages/ProductDetail.js 파일을 만들고 useParams 훅을 사용하여 URL 파라미터 값을 가져옵니다.

src/pages/ProductDetail.js
import React from 'react';
import { useParams } from 'react-router-dom'; // useParams 훅 임포트

function ProductDetail() {
  const { id } = useParams(); // URL 파라미터에서 'id' 값 추출

  // 실제 앱에서는 이 id를 사용하여 서버에서 제품 데이터를 가져올 것입니다.
  const productData = {
    '123': { name: 'React 신발', price: 99000, description: 'React 개발자를 위한 특별한 신발입니다.' },
    '456': { name: '리액트 후드티', price: 45000, description: '깔끔한 리액트 로고가 새겨진 후드티입니다.' }
  };

  const product = productData[id]; // 가상 데이터에서 id에 해당하는 제품 찾기

  if (!product) {
    return (
      <div style={{ padding: '20px', textAlign: 'center', color: '#888' }}>
        <h2>제품을 찾을 수 없습니다.</h2>
        <p>ID: {id}</p>
      </div>
    );
  }

  return (
    <div style={{ padding: '20px', textAlign: 'center', border: '1px solid #ddd', borderRadius: '8px', margin: '20px', backgroundColor: '#f9f9f9' }}>
      <h2 style={{ color: '#007bff' }}>{product.name} 상세 페이지</h2>

      <p style={{ fontSize: '1.2em', color: '#333', fontWeight: 'bold' }}>
        가격: {product.price.toLocaleString()}
      </p>
      <p style={{ fontSize: '1.1em', color: '#555' }}>
        {product.description}
      </p>
      <p style={{ fontSize: '0.9em', color: '#777' }}>
        현재 보고 있는 제품 ID: <span style={{ fontWeight: 'bold', color: '#007bff' }}>{id}</span>
      </p>
    </div>
  );
}

export default ProductDetail;
  • useParams() 훅은 객체를 반환하며, 이 객체의 속성 이름은 Routepath에 정의된 콜론(:) 뒤의 이름과 일치합니다. (예: path="/product/:id" -> { id: "값" })

동적 파라미터 동작 확인

  • App.jsProductDetail 컴포넌트와 Route가 이미 추가되어 있는지 확인하세요.
  • 브라우저의 URL에 직접 http://localhost:5173/product/123 또는 http://localhost:5173/product/456을 입력하여 제품 상세 페이지가 렌더링되고, id 값이 올바르게 표시되는지 확인합니다.
  • Navbar의 Product (예시) 링크를 클릭해도 동일한 결과를 볼 수 있습니다.

동적 라우팅은 URL 조각이 곧 컴포넌트 입력값이 되는 구조입니다. 아래 다이어그램은 path="/product/:id"에서 useParams()와 데이터 조회까지 이어지는 흐름을 분리해 보여줍니다.


중첩 라우팅 (Nested Routing)

실제 애플리케이션에서는 특정 경로 아래에 또 다른 하위 경로가 있는 경우가 많습니다. (예: /dashboard/settings, /dashboard/analytics). React Router는 중첩 라우팅을 쉽게 구현할 수 있도록 합니다.

중첩 라우팅은 다음 절에서 더 자세히 다루겠습니다.


React Router 사용법은 여기까지입니다. 이 장에서는 React Router를 설치하고, BrowserRouter, Routes, Route, Link, NavLink, useNavigate, useParams와 같은 핵심 컴포넌트 및 훅을 사용하여 기본적인 클라이언트 사이드 라우팅과 동적 라우팅을 구현하는 방법을 상세히 배웠습니다.

이 절에서는 리액트 SPA에서 페이지 이동을 처리하고, URL에 따라 렌더링할 컴포넌트를 결정하는 기본 흐름을 확인했습니다.

라우터 코드를 점검할 때는 컴포넌트 이름보다 URL 변화와 렌더링 결과가 일치하는지 먼저 보는 편이 안전합니다.


React Router는 URL과 컴포넌트 배치를 연결하므로 각 API가 어느 책임을 맡는지 구분해 두는 것이 좋습니다.

React Router 설치와 React Router 주요 컴포넌트 중심으로 정리한 보조 다이어그램입니다.