icon
6장 : React 라우팅 기초

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를 사용하기 위해 가장 기본적으로 알아야 할 컴포넌트들은 다음과 같습니다.

  1. BrowserRouter (또는 HashRouter): 라우팅을 적용할 컴포넌트들을 감싸는 최상위 라우터 컴포넌트입니다.
    • BrowserRouter: HTML5 History API를 사용하여 깔끔한 URL(example.com/about)을 만듭니다. 서버 설정이 필요할 수 있습니다. (가장 일반적으로 사용)
    • HashRouter: URL에 # 기호(example.com/#/about)를 사용하여 라우팅을 처리합니다. 서버 설정이 필요 없습니다. (정적 파일 서버 환경에 유용)
  2. Routes: 여러 Route들을 그룹화하고, 현재 URL과 일치하는 첫 번째 Route를 렌더링합니다. React Router v6부터 Switch 대신 사용됩니다.
  3. Route: 특정 URL 경로에 어떤 컴포넌트를 렌더링할지 정의합니다.
    • path prop: 매칭될 URL 경로
    • element prop: 렌더링할 리액트 컴포넌트
  4. Link (또는 NavLink): 페이지 이동을 위한 링크를 생성합니다. HTML의 <a> 태그와 유사하지만, 페이지 전체를 새로고침 하지 않고 클라이언트 사이드 라우팅을 수행합니다.
    • to prop: 이동할 URL 경로
    • NavLink: 현재 활성화된 경로에 따라 active 클래스나 스타일을 적용할 수 있어 내비게이션 바 등에 유용합니다.
  5. useNavigate Hook: 컴포넌트 내에서 JavaScript 코드로 페이지를 이동해야 할 때 사용하는 훅입니다. (예: 폼 제출 후 리다이렉트)
  6. useParams Hook: URL 경로에 포함된 동적인 파라미터 값을 가져올 때 사용하는 훅입니다.

기본 라우팅 구현 예제

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

페이지 컴포넌트 생성

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

src/pages/Home.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>
      <img src="https://via.placeholder.com/300/2ecc71/FFFFFF?text=Home" alt="Home" style={{ marginTop: '20px', borderRadius: '8px' }} />
    </div>
  );
}

export default Home;
src/pages/About.js
// 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>
      <img src="https://via.placeholder.com/300/3498db/FFFFFF?text=About" alt="About" style={{ marginTop: '20px', borderRadius: '8px' }} />
    </div>
  );
}

export default About;
src/pages/Contact.js
// 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>
      <img src="https://via.placeholder.com/300/e74c3c/FFFFFF?text=Contact" alt="Contact" style={{ marginTop: '20px', borderRadius: '8px' }} />
      <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
// 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
// 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 start (또는 yarn start) 명령어를 실행하여 애플리케이션을 시작하고 브라우저에서 http://localhost:3000에 접속합니다.

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

동적 라우팅과 useParams

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

ProductDetail.js 생성

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

src/pages/ProductDetail.js
// 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>
      <img src={`https://via.placeholder.com/200/007bff/FFFFFF?text=Product+${id}`} alt={product.name} style={{ marginTop: '20px', borderRadius: '8px' }} />
      <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:3000/product/123 또는 http://localhost:3000/product/456을 입력하여 제품 상세 페이지가 렌더링되고, id 값이 올바르게 표시되는지 확인합니다.
  • Navbar의 "Product (예시)" 링크를 클릭해도 동일한 결과를 볼 수 있습니다.

중첩 라우팅 (Nested Routing)

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

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


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

이제 여러분은 리액트 SPA에서 페이지 이동 기능을 구현하고 URL에 따라 동적으로 콘텐츠를 렌더링할 수 있는 기본적인 역량을 갖추게 되었습니다.