
이 문서는 React Router의 기능 중 하나인 중첩 라우팅(Nested Routing)에 대해 설명합니다. 이 문서를 통해 공통된 UI 레이아웃을 여러 페이지에 걸쳐 효율적으로 재사용하는 방법을 학습할 수 있습니다.
<Outlet> 컴포넌트의 역할과 사용법createBrowserRouter에서 children 속성을 사용한 라우트 중첩 방법웹 애플리케이션을 만들다 보면 여러 페이지가 동일한 헤더(Header), 푸터(Footer), 또는 사이드바(Sidebar)를 공유하는 경우가 많습니다. 중첩 라우팅은 바로 이러한 공통 레이아웃 구조를 쉽게 구현하기 위한 기능입니다.
경로(Route) 안에 또 다른 경로를 중첩시키는 방식으로, 부모 경로는 공통 레이아웃 역할을 하는 컴포넌트를 렌더링하고, 자식 경로는 해당 레이아웃의 특정 영역에 실제 페이지 콘텐츠를 렌더링합니다.
💡 쉽게 이해하기
중첩 라우팅은 "액자"와 "그림"에 비유할 수 있습니다.
- 부모 라우트 (Layout): 모든 페이지에 동일하게 적용되는 "액자"입니다.
- 자식 라우트 (Page): 액자 안에 들어갈 개별 "그림"입니다.
<Outlet>: 그림이 들어갈 액자의 빈 공간입니다.
<Outlet> 컴포넌트<Outlet> 컴포넌트는 부모 라우트의 컴포넌트(레이아웃) 내에서 자식 라우트의 컴포넌트가 렌더링될 위치를 지정해주는 표식(Placeholder)입니다.
React Router는 현재 URL에 맞는 자식 라우트를 찾아, 해당 컴포넌트를 부모의 <Outlet> 위치에 렌더링합니다.
// layouts/MainLayout.jsx
import { Outlet, Link } from "react-router-dom";
export default function MainLayout() {
return (
<div>
<header>
<h1>My App</h1>
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
</nav>
</header>
<hr />
<main>
{/* 자식 컴포넌트가 이 자리에 렌더링됩니다 */}
<Outlet />
</main>
<hr />
<footer>
<p>© 2025 My App</p>
</footer>
</div>
);
}
children 속성라우터를 설정하는 createBrowserRouter 함수에서, 특정 경로 객체 내에 children 속성을 사용하여 자식 경로들을 배열 형태로 정의할 수 있습니다.
children 경로 규칙path는 부모 경로에 상대적으로 작성됩니다. /로 시작하지 않아야 합니다.path: "/products", 자식 path: "1" → 전체 URL: /products/1path 대신 index: true를 사용하면, 부모의 URL과 정확히 일치할 때 기본으로 렌더링될 자식 컴포넌트를 지정할 수 있습니다.// router/index.js
import MainLayout from "../layouts/MainLayout";
import HomePage from "../pages/HomePage";
import AboutPage from "../pages/AboutPage";
const router = createBrowserRouter([
{
// 부모 라우트
path: "/",
element: <MainLayout />, // 공통 레이아웃 컴포넌트
children: [
{
// 자식 라우트 1 (인덱스)
index: true, // 완성 URL: "/"
element: <HomePage />,
},
{
// 자식 라우트 2
path: "about", // 완성 URL: "/about"
element: <AboutPage />,
},
],
},
]);
공통 헤더를 가진 메인 레이아웃과, 인증 관련 페이지들을 위한 별도의 인증 레이아웃, 두 가지 중첩 라우팅 구조를 만들어 보겠습니다.
📁 src/
├── 📁 pages/ # 페이지 컴포넌트
│ ├── ⚛️ Home.jsx
│ ├── ⚛️ About.jsx
│ ├── ⚛️ Login.jsx
│ └── ⚛️ Signup.jsx
├── 📁 layouts/ # 레이아웃 컴포넌트
│ ├── ⚛️ RootLayout.jsx
│ └── ⚛️ AuthLayout.jsx
├── 📁 router/
│ └── 🚦 index.js # 라우터 설정
└── ⚛️ main.jsx
router/index.js)import { createBrowserRouter } from "react-router-dom";
// 레이아웃 컴포넌트
import RootLayout from "../layouts/RootLayout";
import AuthLayout from "../layouts/AuthLayout";
// 페이지 컴포넌트
import Home from "../pages/Home";
import About from "../pages/About";
import Login from "../pages/Login";
import Signup from "../pages/Signup";
const router = createBrowserRouter([
{
path: "/",
element: <RootLayout />,
children: [
{ index: true, element: <Home /> },
{ path: "about", element: <About /> },
],
},
{
path: "/auth",
element: <AuthLayout />,
children: [
{ path: "login", element: <Login /> },
{ path: "signup", element: <Signup /> },
],
},
]);
export default router;
layouts/RootLayout.jsx
'''jsx
import { Outlet, Link } from "react-router-dom";
export default function RootLayout() {
return (
<div>
<header style={{ padding: 20, borderBottom: '1px solid gray' }}>
<h2>메인 헤더</h2>
<nav>
<Link to="/" style={{ marginRight: 10 }}>홈</Link>
<Link to="/about" style={{ marginRight: 10 }}>소개</Link>
<Link to="/auth/login">로그인</Link>
</nav>
</header>
<main style={{ padding: 20 }}>
<Outlet />
</main>
</div>
);
}
'''
layouts/AuthLayout.jsx
'''jsx
import { Outlet, Link } from "react-router-dom";
export default function AuthLayout() {
return (
<div>
<header style={{ padding: 20, borderBottom: '1px solid gray' }}>
<h2>인증 페이지 헤더</h2>
<Link to="/">홈으로</Link>
</header>
<main style={{ padding: 20, backgroundColor: '#f0f0f0' }}>
<Outlet />
</main>
</div>
);
}
'''
pages/Home.jsx
export default function Home() {
return <h1>🏠 홈 페이지</h1>;
}
pages/Login.jsx
export default function Login() {
return <h2>🔒 로그인 페이지</h2>;
}
(다른 페이지 컴포넌트들도 유사하게 작성합니다.)
RouterProvider 적용 (main.jsx)라우터 설정을 앱에 적용하는 부분은 변경되지 않습니다.
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { RouterProvider } from "react-router-dom";
import router from "./router";
createRoot(document.getElementById("root")).render(
<StrictMode>
<RouterProvider router={router} />
</StrictMode>
);
이제 /, /about 경로에서는 RootLayout이 적용되고, /auth/login, /auth/signup 경로에서는 AuthLayout이 적용되는 것을 확인할 수 있습니다.
element에 레이아웃 컴포넌트를, children에 하위 페이지 라우트 배열을 설정합니다.<Outlet />을 반드시 포함해야 합니다.path는 부모에 대한 상대 경로로 작성하며, index: true는 부모 경로와 동일한 URL의 기본 페이지를 지정합니다.