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은 어렵지 않으니 적극 활용해보자.