React 라우터(Router)

CDH·2025년 5월 20일

Single Page Application(SPA)

MPA vs SPA

  • MPA(Multi Page Application) : URL 별로 여러 개의 html 파일로 구성되며 새롭게 로드됨.
  • SPA(Single Page Application) : 하나의 HTML 페이지에서 필요한 데이터만 가져오는 형태.


SPA 개념

  • 웹 애플리케이션에서 단일 HTML 페이지로 동작하는 애플리케이션 구조.
    - 페이지를 이동할 때 서버에서 새로운 HTML 파일을 로드하여 이동하지 않고, 동적으로 필요한 데이터만 가져와 컨텐츠를 업데이트함

특징

  • 클라이언트 중심
    - 페이지 로딩과 UI 업데이트는 주로 클라이언트(브라우저)에서 이루어짐

  • 필요 데이터만 비동기로 가져와 UI를 업데이트하므로 전체 페이지를 재로드 할 필요 없음

  • Javascript 프레임워크 사용
    - SPA는 React, Angular, Vue.js와 같은 프레임워크/라이브러리를 사용하여 구축됨

  • SPA에서는 URL에 따라 콘텐츠를 변경하기 위해 클라이언트 측 라우팅이 필요함

장점

  • 최초 로딩 이후 추가적인 페이지 로드 없이 데이터만 요청하므로 속도가 빠름
  • 더 나은 사용자 경험(UX)
  • 페이지 전환이 부드럽고 끊김이 없어 데스크톱 애플리케이션과 유사한 경험을 제공
  • 서버 부하 감소
  • 서버에서 전체 HTML 파일을 전달하지 않으므로 부하가 줄어듦

단점

  • SEO 문제
  • SPA는 클라이언트에서 렌더링되므로, 검색 엔진이 콘텐츠를 제대로 인덱싱하지 못할 수 있음
  • 해결 방법: 서버 사이드 렌더링(SSR), 정적 사이트 생성(SSG) 사용
  • 초기 로딩 시간 증가
  • 초기 JavaScript 번들이 크면 첫 로딩 시간이 느려질 수 있습니다

알고가기 (SSR, SSG, ISR, RSC)

  • SSR : 서버 사이드 렌더링
    - 사용자의 요청이 들어올 때마다 서버에서 HTML을 동적으로 생성하여 보내주는 방식

  • SSG : 정적 사이트 생성
    - 웹사이트를 빌드(배포)할 때 모든 페이지의 HTML을 미리 만들어 두는 방식

  • ISR : 점진적 정적 재생성
    - SSG의 단점을 보완한 방식으로, 정적 페이지를 미리 만들어두되, 설정한 주기마다 서버가 자동으로 새로운 데이터를 반영해 페이지를 다시 생성

  • RSC : 리액트 서버 컴포넌트
    - 리액트 컴포넌트 중 일부를 서버에서만 실행하고, 클라이언트에는 결과만 전달하는 방식


Routing (라우팅)

  • URL 경로에 따라 사용자가 보게 될 컨텐츠를 결정하는 방식 =
    - SPA에서 라우팅은 클라이언트 측에서 수행되며, 브라우저의 URL은 변경되지만
    페이지는 다시 로드되지 않음

리액트 라우팅 라이브러리 설치

라이브러리 설치
npm install react-router-dom

라이브러리 업데이트
npm install react-scripts@5


주요 구성 요소

예시 코드

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

function Home() {
  return (
    Home Page
  );
}

function About() {
  return (
    About Page
  );
}

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

동적 라우팅

  • URL의 일부를 변수처럼 사용하여 여러 데이터를 처리하는 라우팅 방식
  • 예) 아래 URL 예시와 같이 상품 ID에 따라 다른 페이지를 보여줄 수 있다


예시 코드

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

function Product() {
  const { id } = useParams(); // id 파라미터 읽기
  return (
    Product ID: {id}
  );
}

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/product/:id" element={<Product />} />
      </Routes>
    </BrowserRouter>
  );
}

/product/:id → :id는 동적 파라미터
파라미터 가져오기 : useParams 훅 함수 사용


동적 라우팅

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

function Product() {
  const { category, id } = useParams(); // category, id 파라미터 읽기
  return (
    Product ID: {id}
  );
}

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/:category/:id" element={<Product />} />
      </Routes>
    </BrowserRouter>
  );
}

/product/:category/:id → :category, :id는 동적 파라미터


상품 상세 페이지 실습해보기

category와 id 값을 기반으로, 해당 상품의 상세 페이지를 출력하는 페이지

App.js

// import logo from './logo.svg';
import './App.css';
import { BrowserRouter, Routes, Route, Link, useParams } from 'react-router-dom'; //  Route, Link, useParams도 추가하기 (12주차)
import ProductDetail from './components/ProductDetail';
import MyComponent from './components/MyComponent';
import Detail from './components/Detail';
import Dashboard from './components/Dashboard';

function Home() { // 12주차 추가
  return <h1>Home Page</h1>
}

function About() { // 12주차 추가
  return <h1>About Page</h1>
}

function Product() {
  const {category, id} = useParams();
  return <h1>Product({category}) ID : {id}</h1>
}


function App() {
  return (
    <BrowserRouter>
      <nav>
        <Link to='/'>Home</Link>
          <br />
        <Link to='/about'>About</Link>
          <br />
        <Link to='/product'>Product</Link>
          <br />
        <Link to='/detail'>Detail</Link>
          <br />
      </nav>

      <Routes>
        
        <Route path="/mycomponent"  element={<MyComponent />} />

        <Route path="/detail"  element={<Detail />} />
        <Route path="/dashboard"  element={<Dashboard />} />
        <Route path="/Home"  element={<Home />} />

        <Route path="/about"  element={<About />} />
        <Route path="/product/:category/:id"  element={<ProductDetail />} />
      </Routes>
    </BrowserRouter>
  );
}

export default App;

Login.jsx

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

export default function Login() {
    const navigate = useNavigate();

    const handleLogin = () => { // 로그인 성공 시
        navigate('/dashboard');
    };

    return (
        <button onClick={handleLogin}>로그인</button>
    );
}

Dashboard.jsx (로그아웃)

import { replace, useNavigate } from "react-router-dom";

export default function Dashboard() {
    const navigate = useNavigate();

    const handleLogout = () => { // 로그아웃
        navigate('/home');
    };

    return (
        <button onClick={handleLogout}>로그아웃</button>

    );
}

MyComponent.jsx

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

function MyComponent() {
    const navigate = useNavigate();

    const handleClick = () => {
        navigate('/detail', { state : {from:'search', itemId: 5}});
        // navigate('/home');
    };

    return (
        <button onClick={handleClick}>홈으로 이동</button>
    );
}

export default MyComponent;

Detail.jsx

import { useLocation } from "react-router-dom";

export default function Detail() {
    const location = useLocation();

    return (
        <div>
            <h1>Home</h1>
            {console.log(location.state)}
        </div>
    );
}

ProductDetail.jsx

import {useParams} from 'react-router-dom';
import Login from './Login';

const productData = {
    'books' : {
        '101' : {
            name: '리액트 입문',
            price: 18000,
        },
        '102' : {
            name: '자바스크립트 입문',
            price: 22000,
        }
    },

    'electronics' : {
        '201': {
            name: '마우스',
            price: 25000,
        },
        '202': {
            name: '키보드',
            price: 55000
        }
    }
};

function ProductDetail() {
    const {category, id} = useParams();
    const categoryData = productData[category]; // 추가
    const product = categoryData ? categoryData[id] : null; // 추가

    return ( // {product.name} 와 {product.id} 추가
        <div>
            <Login />
                <br />
            <h2>상품 상세 정보</h2>
            <p>카테고리: {category}</p>
            <p>상품 ID: {id}</p>

            { product ? (
                <div>
                <p>상품명 : {product.name}</p>
                <p>가격 : {product.price}</p>
                </div>
              )
              : ( <p style={{ color:'red'}}>해당 상품이 존재하지 않습니다</p>
              )
        }
        </div>
    );
}
export default ProductDetail;

0개의 댓글