본문으로 건너뛰기

안동민 개발노트

본문 시작

간단한 다중 페이지 앱 만들기

게시물 목록·상세·댓글·작성·404 경로를 연결해 파라미터·쿼리·중첩 라우팅이 결합된 블로그 SPA를 완성합니다.

React Router의 핵심 컴포넌트와 훅을 사용해 정적/동적 라우팅을 설정하고, URL 파라미터와 쿼리 문자열로 데이터를 전달하며, 복잡한 UI를 위한 중첩 라우팅까지 이해하셨습니다.

이번 실습에서는 이 지식을 통합해 간단한 블로그 애플리케이션(Simple Blog Application)을 직접 구축해봅니다.

이 실습을 통해 각 라우팅 개념이 실제 시나리오에서 어떻게 적용되는지 체감하고, 여러 라우팅 기법을 혼합해 완성도 높은 사용자 경험을 제공하는 방법을 경험할 수 있습니다.

React Router 블로그 앱 구성

React Router의 핵심 컴포넌트와 훅을 사용해 정적/동적 라우팅을 설정하고, URL 파라미터와 쿼리 문자열로 데이터를 전달하며, 중첩 라우팅을 화면 구조에 연결합니다. 블로그 목록, 상세, 작성 화면을 나누어 Link, NavLink, useParams가 어떤 책임을 갖는지 확인합니다.

  1. 준비 단계: 경로 우선순위 정리

    홈, 목록, 상세, 작성 경로를 먼저 정하고 동적 세그먼트가 정적 경로와 충돌하지 않게 배치합니다. 기본 라우팅

  2. 라우팅 화면용 전역 스타일 조정 (index.css)

    navigation, content 영역, 상세 페이지가 같은 레이아웃 안에서 안정적으로 배치되도록 전역 스타일을 정리합니다. 동적 라우팅 (useParams)

  3. 내비게이션 바 (Navbar.js)

    NavLink를 사용하여 블로그의 주요 페이지로 이동하는 링크를 만듭니다. 쿼리 문자열 (useSearchParams)

  4. React Router 블로그 앱 구성 기준

    정확성 홈, 블로그 목록, 게시글 작성 페이지는 정적 경로로 두고, 글 상세만 동적 파라미터로 받습니다. 비용 정적 경로와 동적 경로를 라우트 구성에서 분리해 확인합니다. 확장성 URL 파라미터는 화면 상태와 데이터 조회 키로 함께 검산합니다. 예외 쿼리 문자열은 필터, 정렬, 페이지 상태처럼 공유 가능한 값에 둡니다.

  5. 정확성 홈, 블로그 목록, 게시글 작성 페이지

    정적 경로로 두고, 글 상세만 동적 파라미터로 받습니다.

  6. 비용 정적 경로

    동적 경로를 라우트 구성에서 분리해 확인합니다.

  7. 확장성 URL 파라미터

    화면 상태와 데이터 조회 키로 함께 검산합니다.

  8. 예외 쿼리 문자열

    필터, 정렬, 페이지 상태처럼 공유 가능한 값에 둡니다.


실습 목표: 라우팅 실패 시나리오 대응

이번 실습은 라우팅 기능이 실제 사용자 동선에서 어떻게 실패하는지 먼저 점검하는 방식으로 진행합니다.

동적 파라미터 누락, 쿼리 문자열 누락, 404 전환을 핵심 점검 대상으로 둡니다.

기본 라우팅: 홈, 블로그 목록, 게시글 작성 페이지를 위한 기본 경로 설정.

동적 라우팅 (useParams): 개별 게시글의 상세 페이지 (/posts/:postId) 구현.

쿼리 문자열 (useSearchParams): 블로그 목록에서 카테고리 필터링 기능 구현.

중첩 라우팅 (Outlet): 게시글 상세 페이지 내에서 댓글 영역 등의 하위 콘텐츠를 위한 중첩 라우트 구조 설계. (간단하게 구현)

useNavigate: 게시글 작성 후 목록 페이지로 이동하는 기능 구현.

Link & NavLink: 내비게이션 및 게시글 목록에서 링크 생성.


준비 단계: 경로 우선순위 정리

준비 단계에서는 입력 분포 기준으로 우선순위를 잡습니다.

조회 요청이 가장 빈번하므로 목록/상세 라우트를 먼저 안정화하고, 작성/삭제 동작은 그다음에 연결합니다.

Vite로 생성된 프로젝트가 있다고 가정합니다.

src 폴더에 다음과 같은 구조로 파일들을 생성하고 코드를 작성하겠습니다.

App.js
index.css # 전역/기본 스타일
Navbar.js
HomePage.js
PostListPage.js
PostDetailPage.js
PostComments.js # 중첩 라우팅 예시
NewPostPage.js
NotFoundPage.js

라우팅 화면용 전역 스타일 조정 (index.css)

블로그의 기본적인 레이아웃 및 폰트 스타일을 정의합니다.

src/index.css
body {
  font-family: 'Arial', sans-serif;
  margin: 0;
  padding: 0;
  background-color: #f4f7f6;
  color: #333;
  line-height: 1.6;
}

#root {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

.main-content {
  flex-grow: 1;
  padding: 20px;
  max-width: 960px;
  margin: 20px auto;
  background-color: #ffffff;
  border-radius: 8px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
}

h1, h2, h3 {
  color: #2c3e50;
}

/* 유틸리티 스타일 */
.text-center {
  text-align: center;
}

.button {
  display: inline-block;
  padding: 10px 20px;
  background-color: #3498db;
  color: white;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  text-decoration: none;
  font-size: 1em;
  transition: background-color 0.2s ease;
}

.button:hover {
  background-color: #2980b9;
}

.button.secondary {
  background-color: #7f8c8d;
}
.button.secondary:hover {
  background-color: #616e78;
}

내비게이션 바 (Navbar.js)

NavLink를 사용하여 블로그의 주요 페이지로 이동하는 링크를 만듭니다.

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

function Navbar() {
  const navLinkStyle = ({ isActive }) => {
    return {
      padding: '10px 15px',
      textDecoration: 'none',
      color: isActive ? '#fff' : '#c0c0c0',
      backgroundColor: isActive ? '#34495e' : 'transparent',
      borderRadius: '5px',
      transition: 'all 0.3s ease',
      fontWeight: isActive ? 'bold' : 'normal',
    };
  };

  return (
    <nav style={{
      backgroundColor: '#2c3e50',
      padding: '15px 0',
      boxShadow: '0 4px 8px rgba(0,0,0,0.1)',
      display: 'flex',
      justifyContent: 'center',
      gap: '25px',
    }}>
      <NavLink to="/" style={navLinkStyle}>홈</NavLink>
      <NavLink to="/posts" style={navLinkStyle}>게시글 목록</NavLink>
      <NavLink to="/posts/new" style={navLinkStyle}>새 게시글 작성</NavLink>
    </nav>
  );
}

export default Navbar;

이어서 보기