티스토리 뷰
Vue.js 로 개발을 하기 위해서 거의 필수적으로 사용되는 Vue Router 라이브러리에 대해서 공부해보자. 기타 블로그보다 공식 홈페이지가 더 잘 되어 있어서 공식 홈페이지를 기반으로 설명할 예정이고, 프로젝트를 하며 고민했던 부분이나 참고할만한 부분을 추가로 작성하려고 한다.
내가 이해한 Vue Router는 어떤 URL이 입력되었을 때 어떤 컴포넌트를 보여줄지를 매칭시켜주는 역할이다.
설치방법
Vue Router를 사용하고자 하는 프로젝트 경로에가서 다음 명령어로 해당 라이브러리를 설치한다.
npm install vue-router
Vue Router를 위한 경로를 별도로 생성하지 않고 main.ts 파일에서 직접해줘도 되지만, 가독성 및 유지보수를 위해서 src 경로 밑에 router/router.ts 파일을 생성한다.
그리고 router.ts 파일에 아래 내용을 입력한다.
import Vue from 'vue';
import VueRouter from 'vue-router';
Vue.use(VueRouter);
const router = new VueRouter({
mode: 'history',
linkActiveClass: 'active',
routes: [{
path: '/',
component: DashboardLayout,
redirect: '/home',
children: [{
path: 'home',
name: 'Home',
component: Home
},
{
path: 'scan',
name: 'IoT Firmware Scan',
component: Scan
}]
}],
});
export default router;
이렇게 export한 router를 main.ts파일에서 import해 애플리케이션에 router를 주입하자. (이렇게 주입하면 컴포넌트 어디서든 this 객체를 통해 접근할 수 있기 때문에 편리하다.)
동적 라우트 매칭
path에 :[Name]을 포함하면 해당 경로를 동적으로 사용할 수 있다. 예를 들어서 /user/1, /user/2, /user/3 등이 올 수 있는 것이다. 이렇게 온 가변인자는 $route.params를 통해 접근할 수 있다.
($route는 new Vue할 때 router를 주입했기 때문에 사용가능한 것이다.)
// User Component 생성
const User = {
template: '<div>User {{ $route.params.id }}</div>'
};
// router 생성
const router = new VueRouter({
routes: [{
path: '/user/:id',
component: User
},
]
});
Params 변경 사항에 반응하기
Vue에서는 사용자가 /user/foo 에서 /user/bar 로 이동할 때 동일한 컴포넌트 인스턴스를 재사용하게 된다. 두 route 모두 동일한 컴포턴트를 랜더링하기 때문에 인스턴스를 새롭게 만드는 것보다 매우 효율적이다. 그러나 이는 컴포넌트의 라이프사이클 훅이 호출되지 않는다는 문제가 발생할 수 있다.
개발할 당시에 created() 라이프사이클 훅을 통해서 내부에서 사용되는 데이터를 서버로부터 읽어오도록 처리한적이 있는데, /static/report/1 에서 /static/report/2 로 이동하더라도 내부 데이터가 갱신되지 않는 문제가 있었다.
이 문제는 위에서 설명한 컴포넌트 인스턴스 재사용으로 인해 발생한 문제이다. 이어서 설명할 방법들을 통해 이러한 문제를 해결할 수 있었다.
이러한 문제를 해결하기 위한 두 가지 방법이 있다.
첫 번째는 $route 객체를 watch를 통해 모니터링하는 방법이 있다.
const User = {
template: '<div> User {{ $route.params.id }} </div>',
watch: {
'$route' (to, from) {
// 경로가 변경되었을 때 호출.
}
}
}
비슷한 코드지만 실제 프로젝트할 당시에 사용한 코드를 보면 아래와 같다.
두 번째는 beforeRouteUpdate 함수를 사용하는 방법이 있다. route가 업데이트되기 전에 자동으로 트리거되며 마지막에 3번째 인자로 넘어온 next() 함수를 반드시 호출해줘야 정상적으로 동작한다.
const User = {
template: '<div> User {{ $route.params.id }} </div>',
beforeRouteUpdate(to, from, next) {
// 경로가 변경되었을 때 호출
next();
}
}
마찬가지로 실제 프로젝트에서 사용한 코드를 보면 아래와 같다.
추후에 고급 사용법에서 더욱 자세한 내용을 다룰 예정이다.
중첩된 라우트
실제로 애플리케이션을 구성하면, 하나의 컴포넌트에 모든 것을 구현하는 것이 아니라 여러 단계로 중첩된 컴포넌트로 화면을 구성하게 된다.
실제로 프로젝트에서도 아래와 같이 중첩된 컴포넌트를 통해 화면을 구성한 것을 알 수 있다.
그러면 어떻게 컴포넌트 위에 새로운 컴포넌트를 올려놓을 수 있는지 살펴보자.
<!-- User -->
<div id="app">
<h2> User {{ $route.params.id }} </h2>
<router-view></router-view>
</div>
const router = new VueRouter({
routes: [{
path: '/user/:id',
component: User,
children: [{
path: '',
component: UserHome
},
{
path: 'profile',
component: UserProfile
},
{
path: 'posts',
component: UserPosts
}]
}]
})
위 예시 코드를 보면 User 컴포넌트에 <router-view> 태그가 위치한 것을 볼 수 있는데 이 태그의 위치가 하위 컴포넌트를 위한 자리라고 할 수 있다. 이런 하위 컴포넌트에 대한 정보는 router를 선언할 때 children 속성을 통해서 추가할 수 있다.
예를 들어, /user/:id/profile 이라는 URL에 접근하게 되면 /user/:id 밑에 있는 하위 컴포넌트 중에 UserProfile 컴포넌트를 선택해 부모 컴포넌트인 User에서 <router-view> 태그 자리에 위치시키게 된다.
이 상태에서 /user/:id/posts 로 이동하게 되면 UserProfile 컴포넌트 자리를 UserPosts 컴포넌트로 대체하게 된다. 참고로, 변경되는 컴포넌트는 이것 뿐이니 User 컴포넌트는 다시 불러오지 않는다.
실제 프로젝트에서 사용한 예시코드를 살펴보고 넘어가자.
위 예시코드에는 없었던 redirect 속성 값은 /dynamic 이라고만 쳤을 때 /dynamic/home 으로 리다이렉트하기 위한 속성이다. 위 예시코드에서 path: '' 와 동일한 기능을 수행한다.
여기서 볼 수 있듯이 하나의 깊이가 아니라 여러 단계의 하위 컴포넌트를 가질 수 있다. (말이 좀 이상한데..)
프로그래밍 방식 네비게이션
프로그래밍 방식 네비게이션이란 한마디로 기존에 javascript에서 사용하던 location.href 와 같은 역할이라고 생각면 편하다. 즉, javascript 코드를 통해 URL을 변경하는 방법에 대해서 설명한다.
router.push(location. onComplete?, onAbort?)
단순히 다른 URL로 이동하기 위한 메소드이다. 실제 코드에서는 this.$router.push 와 같이 사용하게 된다. 이 메소드를 사용할 경우 이동할 경로를 히스토리 Stack에 넣기 때문에 뒤로가기 버튼을 눌렀을 경우 이전 URL로 이동하게 된다.
(보통 화면이동할 땐 이 메소드를 사용하면 된다.)
Vue에서는 <router-link> 태그를 사용해 특정 URL로 이동하는 template 코드를 작성할 수 있는데, 이도 내부적으로 router.push 메소드를 호출한다.
router.push 메소드를 사용하는 여러가지 방법을 살펴보자.
// 리터럴 string
router.push('home')
// object
router.push({ path: 'home' })
// 이름을 가지는 라우트
router.push({ name: 'user', params: { userId: 123 }})
// 쿼리와 함께 사용, 결과는 /register?plan=private 입니다.
router.push({ path: 'register', query: { plan: 'private' }})
두번째와 세번째 인자로 onComplete, onAbort 콜백함수를 전달할 수 있는데, 이 콤백은 성공적으로 완료되거나 중단되었을 때 호출된다.
router.replace(location)
router.push 메소드와 동일한 역할을 하지만, 이동할 경로를 히스토리 Stack에 넣지 않고 이동하게 된다.
Vue에서는 <router-link> 태그에 replace 속성을 추가해 이 메소드를 호출할 선언적으로 호출할 수 있다.
router.go(n)
기존에 javascript에서 사용하던 window.history.go(n)와 유사하게 히스토리 Stack에서 위치를 이동하기 위해 사용한다.
// 한 단계 앞으로 갑니다. history.forward()와 같습니다. history.forward()와 같습니다.
router.go(1)
// 한 단계 뒤로 갑니다. history.back()와 같습니다.
router.go(-1)
// 3 단계 앞으로 갑니다.
router.go(3)
// 지정한 만큼의 기록이 없으면 자동으로 실패 합니다.
router.go(-100)
router.go(100)
실제 프로젝트에서 사용했던 코드를 살펴보면서 넘어가자.
이름을 가지는 라우트
route에 이름을 부여해서 URL이 아니라 이름을 통해 접근할 수 있다. 경로가 복잡하거나 좀 더 명시적으로 나타내고 싶을때 이름을 통해 쉽게 접근할 수 있다.
이렇게 선언한 name은 아래 코드를 통해 접근할 수 있다.
<router-link :to="{ name: 'StaticHome', params: { userId: 123 }}"></router-link>
router.push({ name: 'StaticHome', params: { userId: 123 }});
위 예시에서 확인할 듯이 경로에 가변인자가 있다면 params를 통해서 전달할 수 있다.
이름을 가지는 뷰
하나의 부모 컴포넌트에 여러 개의 자식 컴포넌트를 배치하고 싶을 수 있다. 이럴 경우 <router-view> 태그가 하나의 컴포넌트에 여러 개 존재할 수 있는데, 이 때 각각에 어떠한 컴포넌트를 배치할지 매칭하기 위해 <router-view> 태그에 이름을 부여할 수 있다.
<router-view name="a"></router-view>
<router-view></router-view>
<router-view name="b"></router-view>
위 예시코드처럼 <router-view> 태그에 이름을 부여할 수 있고, 부여하지 않은 태그에 대해서는 default로 접근할 수 있다. 그러면 router 객체를 생성해보자.
(동일한 Route에 대해서 여러 컴포넌트가 필요하기 때문에 s를 반드시 붙여줘야 한다!)
cosnt router = new VueRouter({
routes: [{
path: '/',
components: {
default: Foo,
a: Bar,
b: baz
}
}]
})
리다이렉트와 별칭
리다이렉션은 위에서도 잠깐 소개했듯이 특정 경로로 접근하는 요청에 대해서 다른 경로로 리다이렉트하기 위해 사용한다. 예시 코드를 살펴보자.
const router = new VueRouter({
routes: [{
path: '/static',
component: StaticSideBar,
redirect: '/static/home',
children: [{
path: 'home',
component: StaticHome
},
{
path: 'mypage',
component: StaticMyPage
}]
}]
})
위 예시는 /static으로 접근하는 요청을 /static/home으로 리다이렉션하도록 구현된 것이다. 즉, 단순히 /static으로 접근했을 경우에는 별도로 구성하지 않고 자동으로 /static/home으로 이동시키도록 하는 것이다.
이러한 리다이렉트는 경로뿐만 아니라 이름으로도 지정할 수 있다.
const router = new VueRouter({
routes: [{
path: '/static',
component: StaticSideBar,
redirect: 'Home',
children: [{
path: 'home',
name: 'Home',
component: StaticHome
},
{
path: 'mypage',
component: StaticMyPage
}]
}]
})
이외에도 함수형태로 redirect를 구현할 수는 있지만 아직 크게 필요성을 못 느끼겠다.
별칭(Alias)는 특정 경로를 방문할 수 있는 또다른 별칭 경로를 만드는 것이다.
const router = new VueRouter({
routes: [{
path: '/a',
component: A,
alias: '/b'
}]
})
이렇게 구성한 후 사용자가 /a 경로에 접근하면 당연히 A 컴포넌트를 띄운다. 하지만 사용자가 /b 경로에 접근했을 때도 앞에와 같이 /a 경로에 접근한 것처럼 A 컴포넌트를 띄우게 된다.
라우트 컴포넌트에 속성 전달
우선 예시코드를 먼저 보자.
const User = {
template: '<div> User {{ $route.params.id }} </div>'
};
const router = new VueRouter({
routes: [{
path: '/user/:id',
component: User,
}]
})
기존에 라우트 컴포넌트에 속성 값을 전달하기 위해서 이렇게 사용했다. 하지만 이는 $route 객체와의 의존성이 있기 때문에 컴포넌트를 재사용하기 어렵다.
const User = {
props: ['id'],
template: '<div> User {{ id }} </div>'
};
const router = new VueRouter({
routes: [{
path: '/user/:id',
component: User,
props: true
}]
})
하지만 이와같이 props: true 속성을 주게되면 해당 컴포넌트에 기존에 $route.params로 전달되던 속성 값이 props 로 속성 값이 전달되기 때문에 의존성이 사라지고 해당 라우트 외에도 컴포넌트를 재사용하기에 유리하다.
(재사용할일이 많을지는 잘 모르겠다..)
이외에도 props를 객체, 함수 형태로 지정하는 것도 있는데 나중에 더 자세히 살펴보도록 한다.
'Front-End > Vue.js' 카테고리의 다른 글
[Vue.js] Vuex 라이브러리 (0) | 2021.05.07 |
---|---|
[Vue.js] 환경 구성 및 프로젝트 구조 (0) | 2021.04.08 |