Skip to content

๋™์  ๋ผ์šฐํŠธ ๋งค์นญ๊ณผ ํŒŒ๋ผ๋ฏธํ„ฐ โ€‹

์ข…์ข… ์ฃผ์–ด์ง„ ํŒจํ„ด์˜ ๋ผ์šฐํŠธ๋ฅผ ๋™์ผํ•œ ์ปดํฌ๋„ŒํŠธ์— ๋งคํ•‘ํ•ด์•ผ ํ•  ๋•Œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋ชจ๋“  ์‚ฌ์šฉ์ž์— ๋Œ€ํ•ด ๋ Œ๋”๋ง๋˜์–ด์•ผ ํ•˜์ง€๋งŒ ์„œ๋กœ ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž ID๋ฅผ ๊ฐ€์ง„ User ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Vue Router์—์„œ๋Š” ๊ฒฝ๋กœ์— ๋™์  ์„ธ๊ทธ๋จผํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด๋ฅผ ๋‹ฌ์„ฑํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด๋ฅผ _ํŒŒ๋ผ๋ฏธํ„ฐ_๋ผ๊ณ  ๋ถ€๋ฆ…๋‹ˆ๋‹ค:

js
import User from './User.vue'

// ์ด๋“ค์€ `createRouter`์— ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค
const routes = [
  // ๋™์  ์„ธ๊ทธ๋จผํŠธ๋Š” ์ฝœ๋ก ์œผ๋กœ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค
  { path: '/users/:id', component: User },
]

์ด์ œ /users/johnny์™€ /users/jolyne์™€ ๊ฐ™์€ URL์ด ๋ชจ๋‘ ๋™์ผํ•œ ๋ผ์šฐํŠธ์— ๋งคํ•‘๋ฉ๋‹ˆ๋‹ค.

_ํŒŒ๋ผ๋ฏธํ„ฐ_๋Š” ์ฝœ๋ก  :์œผ๋กœ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. ๋ผ์šฐํŠธ๊ฐ€ ๋งค์นญ๋˜๋ฉด, ํ•ด๋‹น _ํŒŒ๋ผ๋ฏธํ„ฐ_์˜ ๊ฐ’์€ ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ์—์„œ route.params๋กœ ๋…ธ์ถœ๋ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ, User์˜ ํ…œํ”Œ๋ฆฟ์„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์—…๋ฐ์ดํŠธํ•˜์—ฌ ํ˜„์žฌ ์‚ฌ์šฉ์ž ID๋ฅผ ๋ Œ๋”๋งํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

vue
<template>
  <div>
    <!-- ํ˜„์žฌ ๋ผ์šฐํŠธ๋Š” ํ…œํ”Œ๋ฆฟ์—์„œ $route๋กœ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค -->
    User {{ $route.params.id }}
  </div>
</template>

ํ•˜๋‚˜์˜ ๋ผ์šฐํŠธ์— ์—ฌ๋Ÿฌ _ํŒŒ๋ผ๋ฏธํ„ฐ_๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ด๋“ค์€ route.params์˜ ํ•ด๋‹น ํ•„๋“œ์— ๋งคํ•‘๋ฉ๋‹ˆ๋‹ค. ์˜ˆ์‹œ:

ํŒจํ„ด๋งค์นญ๋œ ๊ฒฝ๋กœroute.params
/users/:username/users/eduardo{ username: 'eduardo' }
/users/:username/posts/:postId/users/eduardo/posts/123{ username: 'eduardo', postId: '123' }

route.params ์™ธ์—๋„, route ๊ฐ์ฒด๋Š” route.query(URL์— ์ฟผ๋ฆฌ๊ฐ€ ์žˆ์„ ๊ฒฝ์šฐ), route.hash ๋“ฑ๊ณผ ๊ฐ™์€ ์œ ์šฉํ•œ ์ •๋ณด๋„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ „์ฒด ์„ธ๋ถ€ ์ •๋ณด๋Š” API Reference์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ์˜ˆ์ œ์˜ ๋™์ž‘ํ•˜๋Š” ๋ฐ๋ชจ๋Š” ์—ฌ๊ธฐ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํŒŒ๋ผ๋ฏธํ„ฐ ๋ณ€๊ฒฝ์— ๋ฐ˜์‘ํ•˜๊ธฐ โ€‹

ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ์žˆ๋Š” ๋ผ์šฐํŠธ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ์ฃผ์˜ํ•  ์ ์€ ์‚ฌ์šฉ์ž๊ฐ€ /users/johnny์—์„œ /users/jolyne์œผ๋กœ ์ด๋™ํ•  ๋•Œ, ๋™์ผํ•œ ์ปดํฌ๋„ŒํŠธ ์ธ์Šคํ„ด์Šค๊ฐ€ ์žฌ์‚ฌ์šฉ๋œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋‘ ๋ผ์šฐํŠธ ๋ชจ๋‘ ๋™์ผํ•œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ Œ๋”๋งํ•˜๋ฏ€๋กœ, ์ด์ „ ์ธ์Šคํ„ด์Šค๋ฅผ ํŒŒ๊ดดํ•˜๊ณ  ์ƒˆ๋กœ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ๋” ํšจ์œจ์ ์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ, ์ด๋Š” ์ปดํฌ๋„ŒํŠธ์˜ ๋ผ์ดํ”„์‚ฌ์ดํด ํ›…์ด ํ˜ธ์ถœ๋˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์˜๋ฏธ์ด๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค.

๋™์ผํ•œ ์ปดํฌ๋„ŒํŠธ์—์„œ ํŒŒ๋ผ๋ฏธํ„ฐ ๋ณ€๊ฒฝ์— ๋ฐ˜์‘ํ•˜๋ ค๋ฉด, ์ด ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ๋Š” route.params์™€ ๊ฐ™์ด route ๊ฐ์ฒด์˜ ์–ด๋–ค ๊ฒƒ์ด๋“  ๊ฐ์‹œ(watch)ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค:

vue
<script setup>
import { watch } from 'vue'
import { useRoute } from 'vue-router'

const route = useRoute()

watch(
  () => route.params.id,
  (newId, oldId) => {
    // ๋ผ์šฐํŠธ ๋ณ€๊ฒฝ์— ๋ฐ˜์‘...
  }
)
</script>
vue
<script>
export default {
  created() {
    this.$watch(
      () => this.$route.params.id,
      (newId, oldId) => {
        // ๋ผ์šฐํŠธ ๋ณ€๊ฒฝ์— ๋ฐ˜์‘...
      }
    )
  },
}
</script>

๋˜๋Š”, beforeRouteUpdate ๋„ค๋น„๊ฒŒ์ด์…˜ ๊ฐ€๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ์œผ๋ฉฐ, ์ด๋ฅผ ํ†ตํ•ด ๋„ค๋น„๊ฒŒ์ด์…˜์„ ์ทจ์†Œํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค:

vue
<script setup>
import { onBeforeRouteUpdate } from 'vue-router'
// ...

onBeforeRouteUpdate(async (to, from) => {
  // ๋ผ์šฐํŠธ ๋ณ€๊ฒฝ์— ๋ฐ˜์‘...
  userData.value = await fetchUser(to.params.id)
})
</script>
vue
<script>
export default {
  async beforeRouteUpdate(to, from) {
    // ๋ผ์šฐํŠธ ๋ณ€๊ฒฝ์— ๋ฐ˜์‘...
    this.userData = await fetchUser(to.params.id)
  },
  // ...
}
</script>

๋ชจ๋“  ๊ฒฝ๋กœ/404 Not found ๋ผ์šฐํŠธ ์žก๊ธฐ โ€‹

์ผ๋ฐ˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” /๋กœ ๊ตฌ๋ถ„๋œ URL ์กฐ๊ฐ ์‚ฌ์ด์˜ ๋ฌธ์ž๋งŒ ๋งค์นญํ•ฉ๋‹ˆ๋‹ค. ๋ชจ๋“  ๊ฒƒ์„ ๋งค์นญํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐ”๋กœ ๋’ค์— ๊ด„ํ˜ธ ์•ˆ์— ์ •๊ทœ์‹์„ ์ถ”๊ฐ€ํ•˜์—ฌ ์ปค์Šคํ…€ ํŒŒ๋ผ๋ฏธํ„ฐ ์ •๊ทœ์‹์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

js
const routes = [
  // ๋ชจ๋“  ๊ฒƒ์„ ๋งค์นญํ•˜๊ณ  `route.params.pathMatch`์— ๋„ฃ์Šต๋‹ˆ๋‹ค
  { path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound },
  // `/user-`๋กœ ์‹œ์ž‘ํ•˜๋Š” ๋ชจ๋“  ๊ฒƒ์„ ๋งค์นญํ•˜๊ณ  `route.params.afterUser`์— ๋„ฃ์Šต๋‹ˆ๋‹ค
  { path: '/user-:afterUser(.*)', component: UserGeneric },
]

์ด ํŠน์ • ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ๋Š” ๊ด„ํ˜ธ ์•ˆ์— ์ปค์Šคํ…€ ์ •๊ทœ์‹์„ ์‚ฌ์šฉํ•˜๊ณ , pathMatch ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์„ ํƒ์ ์œผ๋กœ ๋ฐ˜๋ณต ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ‘œ์‹œํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ํ•„์š”ํ•˜๋‹ค๋ฉด path๋ฅผ ๋ฐฐ์—ด๋กœ ๋ถ„ํ• ํ•˜์—ฌ ํ•ด๋‹น ๋ผ์šฐํŠธ๋กœ ์ง์ ‘ ์ด๋™ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

js
router.push({
  name: 'NotFound',
  // ํ˜„์žฌ ๊ฒฝ๋กœ๋ฅผ ์œ ์ง€ํ•˜๊ณ , ๋Œ€์ƒ URL์ด `//`๋กœ ์‹œ์ž‘ํ•˜์ง€ ์•Š๋„๋ก ์ฒซ ๊ธ€์ž๋ฅผ ์ œ๊ฑฐ
  params: { pathMatch: route.path.substring(1).split('/') },
  // ๊ธฐ์กด ์ฟผ๋ฆฌ์™€ ํ•ด์‹œ๊ฐ€ ์žˆ๋‹ค๋ฉด ์œ ์ง€
  query: route.query,
  hash: route.hash,
})

์ž์„ธํ•œ ๋‚ด์šฉ์€ ๋ฐ˜๋ณต ํŒŒ๋ผ๋ฏธํ„ฐ ์„น์…˜์„ ์ฐธ๊ณ ํ•˜์„ธ์š”.

History ๋ชจ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ, ์„œ๋ฒ„๋ฅผ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๊ตฌ์„ฑํ•˜๊ธฐ ์œ„ํ•œ ์ง€์นจ๋„ ๋ฐ˜๋“œ์‹œ ๋”ฐ๋ผ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๊ณ ๊ธ‰ ๋งค์นญ ํŒจํ„ด โ€‹

Vue Router๋Š” express์—์„œ ์˜๊ฐ์„ ๋ฐ›์€ ์ž์ฒด ๊ฒฝ๋กœ ๋งค์นญ ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ, ์„ ํƒ์  ํŒŒ๋ผ๋ฏธํ„ฐ, 0๊ฐœ ์ด์ƒ/1๊ฐœ ์ด์ƒ์˜ ์š”๊ตฌ์‚ฌํ•ญ, ์‹ฌ์ง€์–ด ์ปค์Šคํ…€ ์ •๊ทœ์‹ ํŒจํ„ด๊ณผ ๊ฐ™์€ ๋‹ค์–‘ํ•œ ๊ณ ๊ธ‰ ๋งค์นญ ํŒจํ„ด์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ๊ณ ๊ธ‰ ๋งค์นญ ๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”.

๋ชจ๋‘๋ฅผ ์œ„ํ•œ ๋ฌธ์„œ ํ•œ๊ธ€ํ™”