React 교재 · 중급 15편

React Router v7 — SPA 라우팅

한 페이지에 여러 화면. URL 이 바뀌면 컴포넌트만 갈아끼는 SPA 라우팅의 정공법.

여러 UI 화면으로 분기하는 길과 내비게이션 마커 일러스트 — React Router 컨셉

1편에서 "React 는 라이브러리, 라우터는 별도" 라 했다. 그 라우터의 표준이 React Router (npm 다운로드 8M+/주). 2024 년 v7 으로 메이저 업그레이드되며 Remix 의 데이터 라우팅까지 흡수했다.

이번 15편은 React Router v7 의 가장 흔한 5 부품 (BrowserRouter·Routes·Route·Link·useNavigate) + 동적 파라미터 + 중첩 라우트 + 인증 가드까지. 회사 사이트 한 개 만들 수준의 라우팅.

1. 셋업 — 5분

npm install react-router-dom // main.tsx import { BrowserRouter } from 'react-router-dom'; import App from './App'; createRoot(document.getElementById('root')!).render( <BrowserRouter> <App /> </BrowserRouter> ); // App.tsx import { Routes, Route, Link } from 'react-router-dom'; export default function App() { return ( <div> <nav> <Link to="/">홈</Link> <Link to="/about">소개</Link> <Link to="/blog">블로그</Link> </nav> <Routes> <Route path="/" element={<Home />} /> <Route path="/about" element={<About />} /> <Route path="/blog" element={<Blog />} /> <Route path="*" element={<NotFound />} /> {/* 404 */} </Routes> </div> ); }

5 부품 — BrowserRouter (HTML5 History API 사용), Routes (한 위치에서 매칭되는 Route 하나만 렌더), Route (URL 패턴 ↔ 컴포넌트), Link (a 태그 대용, 페이지 새로고침 0), * 와일드카드 (catch-all 404).

왜 a 태그가 아닌 Link 인가<a href="/about"> 는 페이지 전체 새로고침. <Link to="/about"> 는 URL 만 바꾸고 React Router 가 <About /> 컴포넌트만 갈아끼움. SPA 의 즉시 전환 경험은 이 한 컴포넌트 차이.

2. 동적 파라미터 — :id · useParams

블로그 글 상세 페이지처럼 URL 일부가 값이 되는 경우. : 으로 placeholder.

// 라우트 정의 <Route path="/blog/:slug" element={<BlogPost />} /> <Route path="/users/:userId/posts/:postId" element={<UserPost />} /> // 컴포넌트 안에서 값 읽기 import { useParams } from 'react-router-dom'; function BlogPost() { const { slug } = useParams(); // /blog/hello-react → slug = 'hello-react' return <h1>{slug}</h1>; } function UserPost() { const { userId, postId } = useParams(); return <p>user {userId} / post {postId}</p>; }

이게 11편 useEffect 의 데이터 fetch 와 자연스럽게 결합:

function BlogPost() { const { slug } = useParams(); const { data, loading } = useFetch(`/api/posts/${slug}`); // 12편 useFetch // ... }

3. 프로그래밍 방식 navigate — useNavigate

버튼 클릭·폼 제출 후 다른 페이지로 이동시킬 때. Link 가 아니라 함수.

import { useNavigate } from 'react-router-dom'; function LoginForm() { const navigate = useNavigate(); const handleSubmit = async (e) => { e.preventDefault(); const success = await login(...); if (success) { navigate('/dashboard'); // 이동 // navigate('/dashboard', { replace: true }); // 뒤로가기 방지 // navigate(-1); // 브라우저 뒤로 } }; return <form onSubmit={handleSubmit}>...</form>; }

4. Nested Routes + 인증 가드

대시보드처럼 "공통 레이아웃 + 안에서 화면만 바뀜" 패턴. Outlet 으로 자식 라우트 표시 위치를 지정.

// App.tsx — Layout 안에 자식 라우트 <Routes> <Route path="/" element={<PublicLayout />}> <Route index element={<Home />} /> {/* / */} <Route path="about" element={<About />} /> {/* /about */} </Route> <Route path="/dashboard" element={<RequireAuth><DashboardLayout /></RequireAuth>}> <Route index element={<Overview />} /> {/* /dashboard */} <Route path="settings" element={<Settings />} /> {/* /dashboard/settings */} </Route> </Routes> // PublicLayout — 자식 라우트 위치 지정 import { Outlet } from 'react-router-dom'; function PublicLayout() { return ( <> <Header /> <main><Outlet /></main> <Footer /> </> ); } // RequireAuth — 인증 가드 import { Navigate, useLocation } from 'react-router-dom'; function RequireAuth({ children }) { const { user } = useAuth(); // 13편 Context const location = useLocation(); if (!user) return <Navigate to="/login" state={{ from: location }} replace />; return children; }

Outlet 이 핵심. Layout 컴포넌트의 그 위치에 자식 Route 의 element 가 들어간다. RequireAuth 는 Context (13편) 로 사용자 확인 후 미로그인이면 /login 으로 리다이렉트. state 로 원래 가려던 위치를 넘겨주면 로그인 후 다시 그 페이지로 보낼 수 있음.

Next.js 와의 관계 — Next.js 의 App Router 는 React Router 와 다른 시스템 (파일 기반·서버 컴포넌트 통합). React Router 는 클라이언트 사이드 SPA 위주. Vite + React 면 React Router, Next.js 면 그 라우터, 둘이 섞이지는 않음. 22편 RSC 에서 다시 등장.

15편으로 React 의 핵심 부품들이 다 모였다. 16편부터는 그 부품들이 모인 큰 앱이 빠르게 돌도록 — 성능 최적화. memo·useMemo·useCallback 의 정확한 사용 시점.

다음 글

React 교재 16편 — 성능 최적화. memo·useMemo·useCallback 의 진짜 동작 + 언제 쓰고 언제 안 쓰는지.

© 2026 주나이테크(주) @JUNAITECH