Vue Router children으로 중첩 라우트 완벽 정리
children 옵션
Vue Router의 children 옵션을 사용하면 URL 구조에 맞춰 컴포넌트를 중첩해서 렌더링할 수 있다. 레이아웃을 공유하면서 내부 콘텐츠만 바꾸고 싶을 때 특히 유용하다.
기본 구조
// router/index.js
const routes = [
{
path: '/dashboard',
component: DashboardLayout,
children: [
{
path: '', // /dashboard
component: DashboardHome
},
{
path: 'profile', // /dashboard/profile
component: Profile
},
{
path: 'settings', // /dashboard/settings
component: Settings
}
]
}
]
핵심 포인트
1. 부모 컴포넌트에 <RouterView> 필수
자식 라우트는 부모 컴포넌트 안의 <RouterView /> 위치에 렌더링된다. 빠뜨리면 자식 라우트가 아예 표시되지 않으니 반드시 확인하자.
<!-- DashboardLayout.vue -->
<template>
<div class="dashboard">
<nav><!-- 사이드바 등 공통 UI --></nav>
<!-- 자식 라우트가 여기에 렌더링됨 -->
<RouterView />
</div>
</template>
2. children의 path는 / 없이 작성
children: [
{ path: 'edit' }, // ✅ /parent/edit
{ path: '/edit' }, // ❌ 루트 경로로 덮어씀
]
3. 빈 path: ''는 기본(index) 라우트
children: [
{ path: '', component: DefaultChild }, // /parent 접근 시 보여줄 컴포넌트
]
Named Route와 함께 사용
const routes = [
{
path: '/user/:id',
component: UserLayout,
children: [
{
path: 'posts',
name: 'user-posts',
component: UserPosts
},
{
path: 'comments',
name: 'user-comments',
component: UserComments
}
]
}
]
<RouterLink :to="{ name: 'user-posts', params: { id: 123 } }">
게시글
</RouterLink>
다단계 중첩
children 안에 또 children을 넣어 3단계 이상 중첩도 가능하다.
const routes = [
{
path: '/admin',
component: AdminLayout,
children: [
{
path: 'users',
component: UsersLayout,
children: [
{ path: '', component: UserList }, // /admin/users
{ path: ':id', component: UserDetail }, // /admin/users/42
{ path: ':id/edit', component: UserEdit }, // /admin/users/42/edit
]
}
]
}
]
실전 패턴 — 레이아웃 분리
로그인 전/후 레이아웃을 나누는 데 children이 자주 쓰인다.
const routes = [
{
path: '/',
component: AuthLayout, // 헤더/푸터 없는 레이아웃
children: [
{ path: 'login', component: Login },
{ path: 'signup', component: Signup },
]
},
{
path: '/app',
component: AppLayout, // 헤더/사이드바 있는 레이아웃
meta: { requiresAuth: true },
children: [
{ path: '', component: Home },
{ path: 'rentals', component: RentalList },
{ path: 'rentals/:id', component: RentalDetail },
]
}
]
라우터 파일 분리하기
라우트가 많아지면 하나의 파일에 다 넣는 건 유지보수에 좋지 않다. 도메인별로 파일을 분리하는 것이 일반적인 방법이다.
방법 1 — 모듈별 파일 분리 (가장 일반적)
router/
├── index.js
└── modules/
├── auth.js
├── dashboard.js
└── rental.js
// router/modules/rental.js
export default [
{
path: '/rentals',
component: () => import('@/layouts/RentalLayout.vue'),
children: [
{ path: '', name: 'rental-list', component: () => import('@/views/rental/RentalList.vue') },
{ path: ':id', name: 'rental-detail', component: () => import('@/views/rental/RentalDetail.vue') },
{ path: ':id/edit', name: 'rental-edit', component: () => import('@/views/rental/RentalEdit.vue') },
]
}
]
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import authRoutes from './modules/auth'
import rentalRoutes from './modules/rental'
import dashboardRoutes from './modules/dashboard'
const router = createRouter({
history: createWebHistory(),
routes: [
...authRoutes,
...rentalRoutes,
...dashboardRoutes,
{ path: '/:pathMatch(.*)*', name: 'not-found', component: () => import('@/views/NotFound.vue') }
]
})
export default router
방법 2 — children만 별도 파일로 분리
부모 라우트는 유지하고 children 배열만 별도 파일로 관리하는 방식이다.
// router/children/dashboardChildren.js
export default [
{ path: '', name: 'dashboard-home', component: () => import('@/views/dashboard/Home.vue') },
{ path: 'profile', name: 'dashboard-profile', component: () => import('@/views/dashboard/Profile.vue') },
{ path: 'settings', name: 'dashboard-settings', component: () => import('@/views/dashboard/Settings.vue') },
]
// router/modules/dashboard.js
import dashboardChildren from '@/router/children/dashboardChildren'
export default [
{
path: '/dashboard',
component: () => import('@/layouts/DashboardLayout.vue'),
children: dashboardChildren
}
]
방법 3 — 자동 import (Vite 환경)
파일이 많아질수록 index.js에 import 구문이 쌓이는 게 부담스러울 수 있다. Vite의 import.meta.glob을 활용하면 modules/ 폴더에 파일만 추가해도 자동으로 라우트에 등록된다.
// router/index.js
const modules = import.meta.glob('./modules/*.js', { eager: true })
const routes = Object.values(modules).flatMap(module => module.default)
const router = createRouter({
history: createWebHistory(),
routes
})
정리
| 방법 | 적합한 상황 |
|---|---|
| 모듈별 파일 분리 | 도메인이 여러 개인 중·대형 프로젝트 |
| children만 분리 | 특정 라우트의 자식이 많을 때 |
| 자동 import | 파일이 매우 많고 Vite 사용 시 |
도메인이 여러 개로 나뉘어 있는 프로젝트라면 방법 1이 가장 깔끔하고, 팀 협업 시 충돌도 적다. <RouterView /> 누락만 조심하면 children은 어렵지 않으니 적극 활용해보자.