Vue Router는 Vue.js의 공식 라우팅 라이브러리입니다. URL과 컴포넌트를 매핑하여 SPA(Single Page Application)의 페이지 전환을 처리합니다.

면접에서 "SPA의 라우팅은 어떻게 동작하나요?"라고 물어보면, History API와 해시 모드 의 차이를 설명할 수 있어야 합니다.


설치와 기본 설정

TYPESCRIPT
// router/index.ts
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '@/views/HomeView.vue'

const router = createRouter({
  // HTML5 History 모드 — 깔끔한 URL (/about)
  history: createWebHistory(import.meta.env.BASE_URL),

  routes: [
    {
      path: '/',
      name: 'home',
      component: HomeView
    },
    {
      path: '/about',
      name: 'about',
      // 지연 로딩 — 코드 분할
      component: () => import('@/views/AboutView.vue')
    },
    {
      path: '/users',
      name: 'users',
      component: () => import('@/views/UsersView.vue')
    }
  ]
})

export default router
TYPESCRIPT
// main.ts
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'

createApp(App).use(router).mount('#app')

RouterView와 RouterLink

VUE
<!-- App.vue -->
<script setup lang="ts">
import { RouterLink, RouterView } from 'vue-router'
</script>

<template>
  <nav>
    <!-- RouterLink — 클릭 시 히스토리 API로 네비게이션 (페이지 리로드 없음) -->
    <RouterLink to="/">홈</RouterLink>
    <RouterLink to="/about">소개</RouterLink>
    <RouterLink :to="{ name: 'users' }">사용자</RouterLink>

    <!-- active 클래스 자동 적용 -->
    <!-- router-link-active: 부분 매칭, router-link-exact-active: 정확한 매칭 -->
  </nav>

  <!-- 매칭된 컴포넌트가 렌더링되는 위치 -->
  <RouterView />
</template>

<style>
/* RouterLink 활성화 스타일 */
.router-link-active {
  font-weight: bold;
}
.router-link-exact-active {
  color: #42b883;
}
</style>

동적 라우트 매칭

TYPESCRIPT
const routes = [
  // 동적 세그먼트 — :id
  {
    path: '/users/:id',
    name: 'user-detail',
    component: () => import('@/views/UserDetail.vue')
  },

  // 여러 동적 세그먼트
  {
    path: '/users/:userId/posts/:postId',
    name: 'user-post',
    component: () => import('@/views/UserPost.vue')
  },

  // 선택적 파라미터 — ?
  {
    path: '/users/:id?',
    component: () => import('@/views/UserView.vue')
  },

  // 정규식 매칭
  {
    path: '/articles/:id(\\d+)',  // 숫자만 매칭
    component: () => import('@/views/ArticleView.vue')
  },

  // Catch-all — 404 페이지
  {
    path: '/:pathMatch(.*)*',
    name: 'not-found',
    component: () => import('@/views/NotFound.vue')
  }
]
VUE
<!-- UserDetail.vue -->
<script setup lang="ts">
import { useRoute } from 'vue-router'
import { watch } from 'vue'

const route = useRoute()

// route.params로 동적 세그먼트 접근
console.log(route.params.id)

// 같은 컴포넌트에서 파라미터만 변경되면 컴포넌트가 재사용됨
// watch로 파라미터 변경을 감시해야 함
watch(
  () => route.params.id,
  async (newId) => {
    // 새 데이터 로드
    // await fetchUser(newId)
  }
)
</script>

프로그래매틱 네비게이션

VUE
<script setup lang="ts">
import { useRouter } from 'vue-router'

const router = useRouter()

// push — 히스토리에 추가
const goToUser = (id: number) => {
  router.push(`/users/${id}`)
  // 또는 이름 기반
  router.push({ name: 'user-detail', params: { id } })
  // 쿼리 파라미터
  router.push({ path: '/search', query: { q: 'vue', page: 1 } })
}

// replace — 현재 항목을 교체 (뒤로 가기 불가)
const replaceRoute = () => {
  router.replace({ name: 'home' })
}

// go — 히스토리 이동
const goBack = () => router.go(-1)
const goForward = () => router.go(1)
</script>

중첩 라우트

TYPESCRIPT
const routes = [
  {
    path: '/dashboard',
    component: () => import('@/views/Dashboard.vue'),
    children: [
      {
        path: '',  // /dashboard — 기본 자식 라우트
        component: () => import('@/views/DashboardHome.vue')
      },
      {
        path: 'analytics',  // /dashboard/analytics
        component: () => import('@/views/DashboardAnalytics.vue')
      },
      {
        path: 'settings',  // /dashboard/settings
        component: () => import('@/views/DashboardSettings.vue')
      }
    ]
  }
]
VUE
<!-- Dashboard.vue -->
<template>
  <div class="dashboard">
    <aside>
      <RouterLink to="/dashboard">홈</RouterLink>
      <RouterLink to="/dashboard/analytics">분석</RouterLink>
      <RouterLink to="/dashboard/settings">설정</RouterLink>
    </aside>
    <main>
      <!-- 자식 라우트가 여기에 렌더링됨 -->
      <RouterView />
    </main>
  </div>
</template>

History 모드 vs Hash 모드

항목History 모드Hash 모드
URL/about/#/about
APIHTML5 History APIwindow.location.hash
서버 설정필요 (모든 경로 → index.html)불필요
SEO유리불리
TYPESCRIPT
import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router'

// History 모드 (권장)
const router = createRouter({
  history: createWebHistory(),
  routes
})

// Hash 모드
const router = createRouter({
  history: createWebHashHistory(),
  routes
})

라우트 메타 필드

TYPESCRIPT
const routes = [
  {
    path: '/admin',
    component: AdminLayout,
    meta: {
      requiresAuth: true,
      role: 'admin'
    },
    children: [/* ... */]
  }
]
VUE
<script setup lang="ts">
import { useRoute } from 'vue-router'

const route = useRoute()
console.log(route.meta.requiresAuth) // true
</script>

면접 팁

  • **History 모드에서 서버 설정이 필요한 이유 **: 브라우저가 /about을 서버에 요청하면 서버에 해당 파일이 없으므로 404를 반환합니다. 모든 경로를 index.html로 리다이렉트하는 설정이 필요합니다
  • 동적 라우트에서 ** 같은 컴포넌트가 재사용 **되는 것을 이해하고, watch로 파라미터 변경을 감시하는 패턴을 알고 있어야 합니다
  • 지연 로딩(() => import(...))은 ** 코드 분할 **과 ** 초기 로딩 최적화 **의 핵심입니다

요약

Vue Router는 createRouter로 설정하고, RouterView에서 매칭된 컴포넌트를 렌더링합니다. 동적 세그먼트(:id)로 유연한 URL 매칭이 가능하고, 중첩 라우트로 레이아웃을 구성합니다. History 모드 사용 시 서버 설정이 필수이며, 지연 로딩으로 번들을 분할하는 것이 권장됩니다.

댓글 로딩 중...