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 의 진짜 동작 + 언제 쓰고 언제 안 쓰는지.