72 뷰 라우터 기본개념잡기
source: categories/study/vue-experiance/vue-experiance_9-72.md
72 뷰 라우터 기본개념잡기
www.yourwebsite.com/user/index.html
/root
폴더에서/user
폴더를 찾고/user
폴더에서index.html
파일을 찾아 렌더링한다.
vue.js
에서는 이러한 방식으로 주소를 컨트롤하지 않는다.www.yourwebsite.com/user
아까와 다르게 뒤에index.html
주소가 안 붙어있다.- 여기서
/user
이 주소를vue-router
가 캐치를 합니다. vue-router
가 가지고 있는 라우터 중에서/user
를 가지고있는 라우터를 찾고 이 라우터에 매칭시켜놓은component
를 찾습니다.- 이
component
는 당연히 하나의 컴포넌트로만 이루어져있지 않고 여러 컴포넌트를import
해오면서 이루어져있을겁니다.
- 이
- 여튼 매칭시켜놓은 컴포넌트가
/user
를 통해서 라우터에 전달이돼고 라우터가 전달받은 컴포넌트들을 화면에 뿌려줍니다. - 이때 전달받은 컴포넌트들을 뿌려줄 장소가 필요합니다. (
router-view
)
- 좀 더 깊게 들어가면 할 이야기가 더 많겠지만 기초 내용을 다루는 이 시점에서는 이런식으로 동작하는구나 까지정도만 인식하시고
vue-router
에 대한 공부를 시작하시면 됩니다.
// src/router.js
import Vue from 'vue';
import Router from 'vue-router';
import Home from './views/Home.vue';
Vue.use(Router);
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'home',
component: Home,
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ './views/About.vue'),
}
]
})
// src/router.js
import Vue from 'vue';
import Router from 'vue-router';
import Home from './views/Home.vue';
Vue.use(Router);
const About = () => import(/* webpackChunkName: "about" */ './views/About.vue');
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'home',
component: Home,
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: About,
}
]
})
<template>
<div>
<button type="button" @click="$router.push({ name: 'home' })">home</button>
<button type="button" @click="$router.push({ name: 'about' })">about</button>
</div>
</template>
<script>
export default {
name: 'ExampleComponent',
}
</script>
<template>
<div>
<button type="button" @click="$router.push({ path: '/' })">home</button>
<button type="button" @click="$router.push({ path: '/about' })">about</button>
</div>
</template>
<script>
export default {
name: 'ExampleComponent',
}
</script>
<template>
<div>
<button type="button" @click="$router.push('/')">home</button>
<button type="button" @click="$router.push('/about')">about</button>
</div>
</template>
<script>
export default {
name: 'ExampleComponent',
}
</script>
<template>
<div>
<button type="button" @click="$router.push({ name: 'home', query: { }, params: { } })">home</button>
<button type="button" @click="$router.push({ name: 'about' })">about</button>
</div>
</template>
<script>
export default {
name: 'ExampleComponent',
}
</script>
<template>
<div>
<router-link
:to="{ name: 'home' }"
>home</router-link>
</div>
</template>
<script>
export default {
name: 'ExampleComponent',
}
</script>
vuetify router 적용
<template>
<div>
<!-- home의 path는 / -->
<!-- about의 path는 /about -->
<!-- about 페이지가 활성화되도 home path랑도 겹침, 즉 home 버튼에도 active class가 들어간다. -->
<!-- 이를 막고싶다면, active class를 정확한 경로로 매칭시키고싶다면 exact 속성 활용 -->
<v-list-tile
router
exact
:to="{ name: 'home' }"
>
home
</v-list-tile>
<v-list-tile
router
exact
:to="{ name: 'about' }"
>
about
</v-list-tile>
</div>
</template>
<script>
export default {
name: 'ExampleComponent',
}
</script>
vue-router 옵션 설정
import Vue from 'vue';
import VueRouter from 'vue-router';
import Home from './views/Home.vue';
Vue.use(Router);
const About = () => import(/* webpackChunkName: "about" */ './views/About.vue');
const routes = [
{
path: '/',
name: 'home',
component: Home,
},
{
path: '/about',
name: 'about',
component: About,
},
];
const router = new VueRouter({
routes,
mode: 'history',
linkExactActiveClass: '_on',
linkActiveClass: '_on',
});
히스토리 모드, 해시 모드
mode: 'history'
를 지우면 주소창에/#/
이 붙는 것을 볼 수 있다.- 라우터의 기본 모드는 해시 모드이다.
- 해시 태그 뒤에있는 것을 주소로 인지한다.
localhost:8000/#/about
/about
을 인식해서about
페이지로 이동한다.
값 전달하기 - parameters
localhost:8000/value
에서value
를 하나의 값으로 받을 수 있는 방법에 대해 알아보겠다.
// src/router.js
import Vue from 'vue';
import Router from 'vue-router';
import Home from './views/Home.vue';
Vue.use(Router);
const About = () => import(/* webpackChunkName: "about" */ './views/About.vue');
const Users = () => import(/* webpackChunkName: "users" */ './views/Users.vue');
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'home',
component: Home,
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: About,
},
{
path: '/users/:userId',
name: 'users',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: Users,
}
]
})
<template>
<div>
<v-list-tile
router
exact
:to="{ name: 'home' }"
>
home
</v-list-tile>
<v-list-tile
router
exact
:to="{ name: 'about' }"
>
about
</v-list-tile>
<v-list-tile
router
exact
:to="{ name: 'users' }"
>
users
</v-list-tile>
</div>
</template>
<script>
export default {
name: 'ExampleComponent',
}
</script>
<template>
<div>
<h1>Users</h1>
{{ userId }}
</div>
</template>
<script>
export default {
name: 'Users',
computed: {
userId() {
return this.$route.params;
}
}
}
</script>
localhost:8000/users
접속시 접속 안됨localhost:8000/users/123
Users
컴포넌트로 잘 접속됨- 화면에
{"userId": "123"}
노출
$router, $route
$router
엔router.js
에 명시해놓은 모든 것이 들어가있다.routes
프로퍼티엔 위에 설정해놓은 라우터들이 담겨있다.new Router
에 선언해준 속성, 옵션 값들이 담겨있다.
$route
params
,query
, 현재 주소path
parameter 넘기는 방법
<template>
<div>
<v-list-tile
router
exact
:to="{ name: 'home' }"
>
home
</v-list-tile>
<v-list-tile
router
exact
:to="{ name: 'about' }"
>
about
</v-list-tile>
<v-list-tile
router
exact
:to="{ name: 'users', params: { userId: 4321, name: 'example' } }"
>
users
</v-list-tile>
</div>
</template>
<script>
export default {
name: 'ExampleComponent',
}
</script>
params: { userId: 4321, name: 'example' }
path: '/users/:userId',
name
은 라우터path
에 명시되어있지 않지만, 값이 제대로 넘어간다.- 하지만 새로고침시
name
에 담긴example
값은 유지가 안될 것이다.path
에 따로 선언 안되어있으므로.. 내 기억이 맞다면 그럴거임
정리
- 여튼
path
에 변수 설정하던지 params
에 담아서 보내던지- 둘 중에 어떤거 하나만 써도 값을 전달할 수 있다.
this.$route.params
여기에 다 담겨져있다. -
그런데 내 생각엔 둘 다 같이 쓰는게 좋을 거 같다. (새로고침 이슈 때문에라도.. 이 기억이 맞겠지..?)
- 흠 그런데
params
값을url
에 노출시키면 안되는 경우..? 그럼 새로고침시 어떻게 값을 유지하지?query
쓰면url
에 노출되잖아?params
값은 데이터 보안이랑은 큰 연관 없는 거 같은데..url
에 반드시 노출안되어야하는 민감한걸 FE 쪽에서 보통 안다루니..- 내 생각엔 이런 블로그 글들이 잘못된거 같은데.. 흠 여튼..
params
를 새로고침시 유지하려면path
에도/:variance
설정
- 내 생각엔 이런 블로그 글들이 잘못된거 같은데.. 흠 여튼..
params
로 숫자를 넘겼을시, 새로고침하면 문자열 데이터로 바뀌나봄?- 맞아 그래서 이것 때문에
==
이런 데이터 유형은 비교 안하는 연산자를 썼나보네.. - 그래도
==
이런 연산자는 지양.. - 여튼 관련 이슈 블로그글..
- 맞아 그래서 이것 때문에
값 전달하기 - query
localhost:8000/about?mode=form&name=hyungju
<template>
<div>
<v-list-tile
router
exact
:to="{ name: 'home' }"
>
home
</v-list-tile>
<v-list-tile
router
exact
:to="{ name: 'about' }"
>
about
</v-list-tile>
<v-list-tile
router
exact
:to="{
name: 'users',
params: { userId: 4321, name: 'example' },
query: { group: 'member', category: 'trial' },
}"
>
users
</v-list-tile>
</div>
</template>
<script>
export default {
name: 'ExampleComponent',
}
</script>
<template>
<div>
<h1>Users</h1>
{{ $route.query }}
</div>
</template>
<script>
export default {
name: 'Users',
}
</script>
localhost:8000/users/4321?group=member&category=trial
-
화면에
{ "group": "member", "category": "trial" }
노출 url
에localhost:8000/users/4321?name=hyungju&address=seoul
직접 입력url
로 직접 전달한query
값, 받을 수 있음
Note
params
를 쓸지query
로 쓸지 그 기준은?query
는 분류 느낌이고,params
는 노출되도 상관없는 값 처리할 때 사용하면 될 거 같긴한데..- 이는 좀 더 생각해봐야될 듯
하위 경로 children
localhost:8000/home/service/service2
이렇게 경로를 깊게 들어가는걸 하위 경로라고 한다.localhost:8000/users/123/edit
localhost:8000/users
localhost:8000/users/:id
localhost:8000/users/:id/edit
- 이렇게 각 라우터를 다 따로 팔 수 있음
- 하지만 이번엔
children
이란 속성을 활용해서 한 라우터 안에 설정해보도록 하겠음
// src/router.js
import Vue from 'vue';
import Router from 'vue-router';
import Home from './views/Home.vue';
Vue.use(Router);
const About = () => import(/* webpackChunkName: "about" */ './views/About.vue');
const Users = () => import(/* webpackChunkName: "users" */ './views/Users.vue');
const UsersDetail = () => import(/* webpackChunkName: "users-detail" */ './views/UsersDetail.vue');
const UsersEdit = () => import(/* webpackChunkName: "users-edit" */ './views/UsersEdit.vue');
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'home',
component: Home,
},
{
path: '/about',
name: 'about',
component: About,
},
{
path: '/users',
name: 'users',
component: Users,
// 하위 경로들은 users라는 컴포넌트 내에서만 동작하는 개념
// 전체 주소를 총괄하는 'users'라는 라우터가 하나 있고, 'users' 안에서만 동작하는 라우터가 하나 더 있다고 생각하면됨
children: [
{
path: ':id',
name: 'users-detail',
component: UsersDetail,
},
{
path: ':id/edit',
name: 'users-edit',
component: UsersEdit,
}
]
}
]
})
<template>
<div>
<v-list-tile
router
exact
:to="{ name: 'home' }"
>
home
</v-list-tile>
<v-list-tile
router
exact
:to="{ name: 'about' }"
>
about
</v-list-tile>
<v-list-tile
router
exact
:to="{ name: 'users' }"
>
users
</v-list-tile>
</div>
</template>
<script>
export default {
name: 'ExampleComponent',
}
</script>
<template>
<div>
<h1>Users</h1>
<p>유저를 검색해 주세요.</p>
<input type="text" placeholder="유저 번호를 입력하세요." v-model="userId">
<button type="button" @click="$router.push({ path: `users/${userId}` })">검색</button>
<!-- localhost:8000/users/하위경로 : 이 하위 경로는 아래 router-view 에서만 동작함 -->
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'Users',
data() {
return {
userId: null,
}
}
}
</script>
<template>
<div>
<h1>Users</h1>
<p>유저를 검색해 주세요.</p>
<input type="text" placeholder="유저 번호를 입력하세요." v-model="userId">
<button type="button" @click="$router.push({ name: 'users-detail', params: { id: userId } })">검색</button>
<!-- localhost:8000/users/하위경로 : 이 하위 경로는 아래 router-view 에서만 동작함 -->
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'Users',
data() {
return {
userId: null,
}
}
}
</script>
<template>
<div>
<h1>Users Detail</h1>
유저 번호: {{ userId }}
<button type="button" @click="edit">수정</button>
</div>
</template>
<script>
export default {
name: 'UsersDetail',
computed: {
userId() {
return this.$route.params.id;
}
},
methods: {
edit() {
// 아래 두개는 똑같은 동작을 한다.
// 라우터는 같은 Level(수준)에서 이동.
// this.$router.push({ name: 'users-edit' });
this.$router.push({ path: `${this.userId}/edit` });
},
}
}
</script>
<template>
<div>
<h1>Users Edit</h1>
유저 번호: {{ userId }}
</div>
</template>
<script>
export default {
name: 'UsersEdit',
computed: {
userId() {
return this.$route.params.id;
}
},
}
</script>
라우터 가드
- 라우터를 지킨다.
- 로그인을 안한 유저 > 마이 페이지 접근 불가
localhost:8000/mypage
> 이런식으로 직접 주소창에 입력하여 접근할 때, 로그인을 안한 유저라면 접근 안되게 막는다던지,localshot:8000/login
> 아니면 이미 로그인한 유저가 로그인 페이지로 들어오려고할 때, 접근을 막는다던지,
- 조건에 따라 라우터를 열어줄건지 말건지
// src/router.js
import Vue from 'vue';
import Router from 'vue-router';
import Home from './views/Home.vue';
Vue.use(Router);
const About = () => import(/* webpackChunkName: "about" */ './views/About.vue');
const Users = () => import(/* webpackChunkName: "users" */ './views/Users.vue');
const UsersDetail = () => import(/* webpackChunkName: "users-detail" */ './views/UsersDetail.vue');
const UsersEdit = () => import(/* webpackChunkName: "users-edit" */ './views/UsersEdit.vue');
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'home',
component: Home,
},
{
path: '/about',
name: 'about',
component: About,
},
{
path: '/users',
name: 'users',
// vue-router에 있는 라우터 가드 역할을하는 속성들을 사용하여 라우터 가드 생성
beforeEnter: (to, from, next) => {
// to: 불러올 대상이되는 컴포넌트의 $route 객체
// from: 지금 현재 컴포넌트의 $route 객체
// next: 이 내부 로직들이 실행되고나서 어떤 라우터를 불러올지 실제로 정하는 함수
// next(); // 이렇게 작성하면 to 즉, 불러올 대상이되는 컴포넌트를 불러온다.
// 로그인 상태라면
if (isUserLogin === true) {
next();
} else {
next('/');
}
},
component: Users,
// 하위 경로들은 users라는 컴포넌트 내에서만 동작하는 개념
// 전체 주소를 총괄하는 'users'라는 라우터가 하나 있고, 'users' 안에서만 동작하는 라우터가 하나 더 있다고 생각하면됨
children: [
{
path: ':id',
name: 'users-detail',
component: UsersDetail,
},
{
path: ':id/edit',
name: 'users-edit',
component: UsersEdit,
}
]
}
]
})
- 만약에
beforeEnter
를 통해서 해당 라우터 접근을 금지하고 싶다면,next('/');
이런식으로 다른 라우터 주소를 입력해주시면 된다. - 실제로 위
beforeEnter
에next('/')
를 입력해놓으면users
로 이동했는데home
으로 가시는걸 확인할 수 있다. next('about')
이라고 입력하면users
로 이동했는데about
페이지로 이동하는걸 확인할 수 있다.- 음.. 그런데
next('/about)
이라고 입력 안하고next('about')
이라고 입력했는데, 그럼path
,name
중 일치하는 곳으로 매칭시켜서 라우터 불러오는건가..? - 여튼 이거되면 활용성 클듯?
- 음.. 그런데
beforeEnter
가 먼저 실행.beforeEnter
이후next()
가 호출되면 해당 라우터가 불러와지면서 해당 컴포넌트의created
실행.- 순서 중요! 라이프사이클 훅!!!
router.js 파일 내에서 beforeEnter 실행하지 말고, 컴포넌트에서 router 가드 실행해보기
<template>
<div>
예시 컴포넌트
</div>
</template>
<script>
export default {
name: 'ExampleComponent',
// 아까와 다르게 Route라는 단어가 포함되었다.
beforeRouteEnter(to, from, next) {
console.log('beforeEnter');
next(); // next를 호출해야 라우트가 동작을 한다는거!!
},
// 여기선 한가지 훅을 더 사용할 수 있다.
// 라우터가 이 컴포넌트를 떠나기 직전에 동작한다.
beforeRouteLeave(to, from, next) {
console.log('beforeLeave');
},
created() {
console.log('created');
},
destroyed() {
console.log('destroyed');
},
}
</script>
- 위와 같이 작성해도
beforeRouteEnter
먼저 실행, 그 다음에created
실행 -
router.js
에서beforeEnter
작성한 것과 같이 가드 실행됨 - 해당 컴포넌트에 들어가는 순간
- beforeEnter
- created
- 해당 컴포넌트에서 나오는 순간
- beforeLeave
- destroyed
Note
라우터 가드
- 사용자의 상태 또는 우리가 원하는 조건에 따라서 이 라우터를 사용자에게 열어줄건지 말건지, 이런 것들을 조절하기 위한 Vue-Router의 기능임
Vue-Router redirection
- 우리가 라우터를 만들지 않은 곳으로 사용자가 주소를 입력해서 들어왔을 때, 그 사용자를 안전한 곳으로 리다이렉트 시키는 방법이다.
// src/router.js
import Vue from 'vue';
import Router from 'vue-router';
import Home from './views/Home.vue';
Vue.use(Router);
const About = () => import(/* webpackChunkName: "about" */ './views/About.vue');
const Users = () => import(/* webpackChunkName: "users" */ './views/Users.vue');
const UsersDetail = () => import(/* webpackChunkName: "users-detail" */ './views/UsersDetail.vue');
const UsersEdit = () => import(/* webpackChunkName: "users-edit" */ './views/UsersEdit.vue');
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'home',
component: Home,
},
{
path: '/about',
name: 'about',
component: About,
},
{
path: '/users',
name: 'users',
// vue-router에 있는 라우터 가드 역할을하는 속성들을 사용하여 라우터 가드 생성
beforeEnter: (to, from, next) => {
// to: 불러올 대상이되는 컴포넌트의 $route 객체
// from: 지금 현재 컴포넌트의 $route 객체
// next: 이 내부 로직들이 실행되고나서 어떤 라우터를 불러올지 실제로 정하는 함수
// next(); // 이렇게 작성하면 to 즉, 불러올 대상이되는 컴포넌트를 불러온다.
// 로그인 상태라면
if (isUserLogin === true) {
next();
} else {
next('/');
}
},
component: Users,
// 하위 경로들은 users라는 컴포넌트 내에서만 동작하는 개념
// 전체 주소를 총괄하는 'users'라는 라우터가 하나 있고, 'users' 안에서만 동작하는 라우터가 하나 더 있다고 생각하면됨
children: [
{
path: ':id',
name: 'users-detail',
component: UsersDetail,
},
{
path: ':id/edit',
name: 'users-edit',
component: UsersEdit,
}
]
},
{
path: '/redirect-me',
redirect: { name: 'users' },
},
{
path: '/*',
redirect: { name: 'home' },
}
]
})