라우터 객체

router 객체

라우터 객체를 이용한 주소변경

앞서서 <view-link to="">를 활용한 주소변경에 대해서는 알아보았습니다. 하지만 때론 자바스크립트로 주소변경을 해야 하는 경우가 발생할수 있습니다.
이런 경우에는 this.$router.push('주소')을 이용하여 주소를 이동할수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<button @click="onClik">클릭</button>

<script>
export default {
methods: {
onClick() {
this.$router.push('주소');
this.$router.push({path : '주소'});
this.$router.push({path : '주소', params: {id: 123}});
this.$router.push({path : '주소', query: {id: 123}}); // query를 사용하는 경우 주소창에 /주소?id=123으로 출력
}
}
}
</script>

히스토리 목록에 추가하지 않고 주소 변경

push를 이용하면 히스토리 목록에 추가가 됩니다. 하지만 replace를 이용하면 목록에 추가 되지 않습니다.($router.go 메서드를 이용해서 이전 주소로 이동이 불가능합니다.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<router-link to="주소" replace></router-link>   <!-- 선언적 방식은 속성으로 replace를 추가 -->

<script>
export default {
methods: {
onClick() {
this.$router.replace('주소');
this.$router.replace({path : '주소'});
this.$router.replace({path : '주소', params: {id: 123}});
this.$router.replace({path : '주소', query: {id: 123}}); // query를 사용하는 경우 주소창에 /주소?id=123으로 출력
}
}
}
</script>

$router는 VueRouter인스턴스 입니다.

route 객체

$route.params

동적라우팅으로 데이터를 받거나 push등을 통해서 params에 데이터를 저장한 경우에 this.$route.params.데이터를 통해서 데이터를 얻어 올수 있습니다.

1
2
3
4
5
6
7
<script>
export default {
created() {
this.$route.params.데이터
}
}
</script>

$route.query

push등을 통해서 query에 데이터를 저장한 경우에 this.$route.query.데이터를 통해서 데이터를 얻어 올수 있습니다.

1
2
3
4
5
6
7
<script>
export default {
created() {
this.$route.query.데이터
}
}
</script>

Comment and share

동적 라우팅

블로그 주소를 /post/포스트번호로 설계하여 포스트번호가 동적으로 변화하여야 하는 경우에는 동적으로 라우팅을 설정해야 합니다. 동적으로 주소를 사용할땐 주소 앞에 :를 사용합니다.

동적 라우팅 구성

1
2
3
4
5
6
var routes = [
{
path: '/post/:postId',
component: {template: '<p>포스팅입니다.</p>'}
}
];

/post/001, /post/002 처럼 동적으로 접근 가능
게시판에서 게시글 상세보기 접근할때 해당하는 동적 주소 사용 가능

동적 라우팅으로 구성한 데이터 접근

동적으로 작성된 주소의 데이터는 this.$route.params.데이터명 으로 접근이 가능합니다.

1
2
3
4
5
6
7
<script>
export default {
created() {
this.$route.params.postId
}
}
</script>

props로 데이터 접근

동적 라우팅으로 구성한 데이터를 props로 가져 올수 있다. this.$route.params.데이터명로 하면 라우트와의 의존성이 높아지므로 props를 활용하는게 좋다.

1
2
3
4
5
6
7
var routes = [
{
path: '/post/:postId',
component: {template: '<p>포스팅입니다.</p>'},
props: true
}
];

URL에 동적 데이터 구성 이외에도 queryString 방식도 이용 가능 하다.

1. url 작성
1
url : /user?id=userId
2. 라우터 작성
1
2
3
4
var routes = [{
path: '/user'
component: 컴포넌트
}]
3. 값 받아오기
1
this.$route.query.id
4. props로 받아오기

키가 id 값을 props에 userName이라는 키로 변환하여 보내주고 있다.

1
2
3
4
5
var routes = [{
path: '/user'
component: 컴포넌트,
props: (route) => ({userName: route.query.id})
}]

Comment and share

뷰 라우터

싱글페이지앱에서는 화면의 새로고침이 없이 페이지가 전환이 되어야 합니다. 그럴때 사용하는 것이 뷰 라우터로써 라우트에 컴포넌트를 맵핑 시켜 놓고 특정주소에서 해당하는 컴포넌트를 렌더링하는 방식으로 싱글페이지앱에서 페이지의 전환을 대신합니다.

1. vue-router 구성

1-1. vue-router파일 작성

참고사항

vue-router의 실제 코드를 main.js에 작성하게 되면 vue-router 편향적인 파일이 되기 때문에 삼가한다. vue-router만을 위한 파일을 생성하여 작성하도록 하자.

vue-router 설치

1
npm install vue-router

import

1
2
3
4
import Vue from 'vue';
import VueRouter from 'vue-router';

Vue.use(VueRouter);

컴포넌트 생성

렌더링할 컴포넌트 생성

1
2
const Foo = {template: '<p>foo</p>'}
const Bar = {template: '<p>bar</p>'}

VueRouter 생성

path는 주소, component는 해당 주소에서 렌더링할 컴포넌트를 맵핑

1
2
3
4
5
6
7
8
let routes = [
{path: '/foo', component: Foo},
{path: '/bar', component: Bar}
]

export const router = new VueRouter({
routes
});

1-2. main.js 작성

Vue 인스턴스 생성

1
2
3
4
5
import {router} from '라우터작성 경로';

new Vue({
router
})

뷰 라우터 태그 작성

라우터 설정시에 맵핑했었던 컴포넌트가 router-view에 렌더링 됩니다.

1
2
3
<div id="app">
<router-view></router-view>
</div>

라우터 이동

path부분에 주소를 입력 해주면 해당 라우팅 경로로 이동할수 있습니다(실제론 해당 주소로 컴포넌트를 바꿔서 렌더링 작업을 처리해준다는 것).

1
2
3
<div id="app">
<router-link to="path"></router-link>
</div>

redirect

path 경로로 들어왔을 때 리다이렉트 할수 있다.

1
2
3
4
5
6
var routes = [
{
path: '/',
redirect: '/리다이렉트할 경로'
}
]

url에 # 제거하기

mode에 history값을 주면 #을 제거 가능하다.

1
2
3
4
5
6
new VueRouter({
mode : history,
routes : [

]
})

2. 심화된 라우팅 구성

중첩된 라우트

중첩된 라우트를 사용하기 위해서는 children 속성을 이용하면 가능합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var routes = [
{
path: '/mypage',
component: {template: '<p>안녕하세요</p><router-view></router-view>'},

// 중첩된 라우트를 사용하기 위해서는 children 속성을 이용하면 가능합니다.
children: {
path: 'myBoard',
component: {template: '<p>내 게시물</p>'}
}
}
]

new VueRouter({
routes
})

중첩된 라우트는 화면 구성 컴포넌트의 수가 적으면 유용하지만 한번에 더 많은 컴포넌트를 표시하는데는 한계가 있습니다.

다중 라우트

하나의 주소에 여러개의 컴포넌트와 매핑을 합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<view-router></view-router>
<view-router name="header"></view-router>

var routes = [
{
path: '/mypage',
components: {
default: {template: '<p>기본 컴포넌트</p>'}, // view-router에 name속성의 값을 설정하지 않으면 default
header: {template: '<header>헤더 컴포넌트</header>'} // view-router에 name속성의 값을 키로 하여 컴포넌트를 맵핑
}
}
];

new VueRouter({
routes
});

상하위 관계가 아닌 동등한 레벨의 여러개의 컴포넌트를 한번에 표시가 가능합니다.

Comment and share

component 확장해서 사용하기

extends는 기존 컴포넌트를 확장해서 유사한 컴포넌트를 만들수 있는 방법입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/* Hello.vue */
<template>
<div>
{{ message }}
</div>
</template>

<script>
export default {
data() {
return {
message: "Hello extends 사용하기"
}
},
created() {
console.log('Hello created 호출');
}
}
</script>

/* HelloWorld.vue */
<script>
import Hello from './Hello.vue';

export default {
name: 'HelloWorld',
extends: Hello,
data() {
return {
message: "HelloWorld extends 사용하기"
}
},
created() {
console.log('HelloWorld created 호출');
}
}
</script>

extends를 사용할 경우 data, computed, methods, filters는 오버라이딩이 된다. 하지만 라이프사이클은 모두 순서대로 호출이 된다.

Comment and share

vue를 사용하게 되면 DOM요소에 대한 관리를 vue가 해주기 때문에 직접 접근할 일이 드물지만, 특별한 케이스에 한해서 DOM 요소에 접근을 해야할 경우가 생길수 있습니다.

1. DOM 요소에 접근

1
2
3
4
5
6
7
8
9
10
11
12
13
<template>
<div>
<span ref="message"></span>
</div>
</template>

<script>
export default {
created() {
this.$refs.message.innerHTML = "ref 테스트"
}
}
</script>

this.$refs.ref이름 으로 DOM요소에 접근 가능

2. 자식 컴포넌트의 메서드 호출

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<template>
<div>
<childComponent ref="child"/>
</div>
</template>

<script>

/* parentComponent */
import childComponent from './childComponent'

export default {
components: {
childComponent
},
created() {
this.$refs.child.displayConsole()
}
}

/* childComponent */
export default {
methods: {
displayConsole() {
console.log('ref 테스트')
}
}
}
</script>

Comment and share

watch는 데이터에 대한 변화를 감지할 때 주로 사용하게 됩니다.

데이터가 Array나 Object인 경우

대상이 array나 Object인 경우에 문제가 발생합니다. object의 프로퍼티나 array의 자식 값의 변화에는 감지를 하지 못합니다. 그래서 set를 이용하여 중첩된 관계에 대한 반응성을 추가 할수 있습니다.

Comment and share

computed를 사용하면서 느낀 점

computed는 기본 데이터를 가공 처리 작업 후에 데이터 바인딩을 해주어야 하는 경우에 주로 사용하는 거 같습니다. 기본 데이터가 변경 되면 computed가 실행이 되어서 처리 로직을 실행 한 후에 결과를 변경후 렌더링합니다. 기존 데이터가 변경이 되는 걸 감지한다는 점에 있어서 watch와 비슷한 기능을 하는 것 처럼 보이지만 실제로는 watch를 사용하는 것보다는 computed를 사용하는게 더 적절한 경우가 많다고 생각합니다.

computed VS watch

watch는 해당 데이터의 변화를 감지하면 메소드가 실행이 됩니다. computed도 내루 로직에서 사용 하는 데이터가 변경이 되면 감지하여 메소드가 실행이 됩니다. 이를 보면 두개의 속성이 비슷하게 사용되는 것 처럼 느낄수 있습니다. 둘 모두를 사용했을 때 같은 결과를 얻어 낼수 있다면 computed를 사용하는 것이 좋다고 생각합니다. watch를 사용 할 때는 주로 사용자의 입력등 비동기인 경우에 사용하는 것이 좋다고 생각합니다.

computed VS methods

computed와 methods의 가장 큰 차이는 캐싱입니다. 두 속성 모두 사용자가 원하는 결과를 낼수 있지만 computed 같은 경우에는 종속 된 대상(실행부에서 사용한 인스턴스 데이터 등)의 값이 변화할 때만 다시 실행을 하게 됩니다. 그렇게 때문에 computed를 사용해도 되는 경우에는 methods 보다는 computed를 사용합시다.

computed는 필수적으로 return 받은 값으로 데이터 바인딩 합니다.

computed 속성의 메서드에서 await를 썼더니 원하지 않는 결과 도출(당연하게도 await를 메서드에 설정하면 해당 메서드는 Promise를 return하기 때문)

GET | SET

computed의 프로퍼티는 직접 수정으로 인한 값이 수정 되지 않는다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
export default {
data() {
return {
num1: 10,
num2: 20
}
},
computed: {
sum() {
return num1 + num2
}
},
methods: {
updateSum() {
this.sum = 100; // 이렇게 직접 값을 주어도 변경 되지 않는다 => 이런식의 코딩은 노노
}
}
}

sum 앞에 아무런 키워드를 입력하지 않으면 get이 생략된 것으로써 값을 가져오는 기능을 한다
sum 앞에 set 키워드를 넣어서 해당 sum을 동작하게 하는 데이터를 변경하는 식으로 코드 작성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
export default {
data() {
return {
num1: 10,
num2: 20
}
},
computed: {
get sum() {
return num1 + num2
},
set sum() {
this.num = 10 // sum을 직접 수정하는게 computed 메서드를 동작하게 하는 데이터를 변경하는 식으로 작성
}
}
}

Comment and share

상위 - 하위 컴포넌트간의 데이터전달

각 컴포넌트는 고유한 유효범위를 가지고 있기 때문에 다른 컴포넌트의 값을 직접 참조할수가 없습니다. 그래서 컴포넌트간의 데이터를 전달 하는 방법을 알아야 합니다.

1. 상위 컴포넌트에서 하위 컴포넌트로 값 전달

이 방법은 상위 컴포넌트에서 하위 컴포넌트로 v-bind 디렉티브를 이용해서 값을 전달 할수 있습니다.

상위 컴포넌트

상위 컴포넌트에서는 하위 컴포넌트에 v-bind 디렉티브를 이용해서 속성에 값을 전달합니다.

1
2
3
4
5
6
7
8
9
10
11
<child-componenet v-bind:msg="message"></child-componenet>

<script>
export default {
data() {
return {
message: '안녕하세요'
}
}
}
</script>

하위 컴포넌트

하위 컴포넌트에서는 상위 컴포넌트에서 속성의 값을 props 속성을 이용하여 전달 받을 수 있습니다.

1
2
3
4
5
6
7
<script>
export default {
props: {
msg: String // 속성명 : 전달 받을 타입으로 작성
}
}
</script>

2. 하위 컴포넌트에서 상위 컴포넌트로 값 전달

하위 컴포넌트에서 상위 컴포넌트로 값을 전달하기 위해서는 이벤트를 활용합니다. 이벤트를 발생시키는 $emit, $on을 이용하여 값을 전달 합니다.

하위 컴포넌트

하위 컴포넌트는 상위컴포넌트로 값을 전달 할 상황이 생기면 $emit을 이용하여 이벤트를 발생시키면서 데이터를 전달 합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<button @click="onClick">클릭</button>

<script>
export default {
data() {
return {
msg : '안녕하세요.'
}
},

methods: {
onClick() {
this.$emit('@click', this.msg);
}
}
}
</script>

상위 컴포넌트

하위 템플릿에 추가한 이벤트에서는 상위 컴포넌트의 메서드를 호출 할수 있기 때문에 하위 컴포넌트에서 $emit과 함께 전달한 데이터를 상위 컴포넌트로 전달 할수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
<child-component @@click="onSubmit"></child-component>

<script>
export default {
methods: {
onSubmit(msg) {
console.log(msg);
}
}
}
</script>

단점 : 트리구조가 복잡하게 얽혀있는 구조인 경우에 하나의 데이터를 변경하려고 해도 연관된 상하위컴포넌트 전체를 변경해야 하기 때문에 유지 보수의 어려움

3. 이벤트 버스

상위-하위 컴포넌트 간의 데이터 통신은 상-하위 관계가 복잡하게 이루어져 있을 경우에 연관된 props와 이벤트 발생가 많이 생성이 되어서 하나를 수정해야 한다면 관련된 모든 props와 이벤트를 수정하여야 하기 때문에 유지보수의 어려움이 있습니다. 그래서 이벤트 버스를 활용하면 직접 관계가 없는 컴포넌트끼리 데이터를 전송할수 있습니다.

이벤트 버스 또한 $emit와 $on을 활용 합니다.

1) 이벤트 버스 생성

Vue의 프로토 타입으로 $EventBus(이름은 상관 없음) 뷰 인스턴스를 생성합니다(이때 아무런 옵션을 설정하지 않습니다). 이렇게 생성하면 컴포넌트 내부에서 this.$EventBus로 접근이 가능합니다.

1
2
3
import Vue from 'vue'

Vue.prototype.$EventBus = new Vue();

2) 데이터를 전달 하려는 컴포넌트

1
2
3
4
5
6
7
8
9
10
11
<button @click="onClick">클릭</button>

<script>
export default {
methods: {
onClick() {
this.$EventBus.$emit('@click', '안녕하세요');
}
}
}
</script>

3) 데이터를 전달 받는 컴포넌트

1
2
3
4
5
6
7
8
9
<script>
export default {
created() {
this.$EventBus.$on('@click', (msg) => {
console.log(msg);
});
}
}
</script>

4) 이벤트 해제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script>
export default {
methods: {
created() {
this.$EventBus.$on('@click', () => {
console.log('생성')
});
},
destroyed() {
this.$EventBus.$off('@click')
}
}
}
</script>

장점

서로 연관이 없는 컴포넌트끼리 직접적으로 쉽게 데이터를 전송하고 받을수 있다.

단점

이벤트 버스를 너무 남용하게 되면 데이터의 흐름을 파악하기 힘들다(이벤트를 emit 했는데 누가 받고 있는지 실제 소스 코드를 확인하기 전까지는 확인이 어렵다).

대안

데이터 중앙집중식 방식을 채용하고 있는 vuex를 사용한다.

사용처

spinner(페이지 전환시에 로딩 처리)는 이벤트 버스가 좋다고 한다. 이벤트 버스는 훅에 선언하는 것이 좋다.

주의

이벤트 버스를 등록 하는 경우 필히 beforeDestory(destroyed)에 $off를 해주어야 한다. 하지 않으면 무한 증식한다.

Comment and share

1. vue-cli 설치

1
npm install -g vue-cli

2. vue-cli3 설치(최신 버전으로 설치)

1
npm i -g @vue/cli

3. vue-cli3를 통한 프로젝트 생성

1
vue create 프로젝트명

4. 프로젝트 서버 시작

1
npm run serve

5. eslint 끄기

  1. package.json와 동일한 depth에 vue.config.js 만들기
  2. 아래 코드 넣기
    1
    2
    3
    module.exports = {
    lintOnSave: false
    }

6. cli 2.x vs cli 3.x

  1. 명령어

    • 2.x : vue init 프로젝트이름 파일위치
    • 3.x : vue create 프로젝트이름
  2. 웹팩 설정파일

    • 2.x : 노출되어 있다 => 웹팩 설정 가능
    • 3.x : 노출되어 있지 않다 => 웹팩 설정을 위한 다른 방법을 학습하여야 함
  3. 프로젝트 구성

    • 2.x : 깃헙의 템플릿을 다운로드 후 사용하게 됨
    • 3.x : 플러그인 기반으로 기능 추가
  4. ES6 이해도

    • 2.x : 필요없다
    • 3.x : 필요하다

Comment and share

Vue.component를 사용하는 전역 컴포넌트로는 구조 파악이나 스타일 적용등에 있어서 어려움이 많아서 복잡한 뷰 프로젝트에서 사용하기가 힘듭니다. 그래서 .vue 파일로 프로젝트를 구성하는 방식인 싱글파일컴포넌트를 사용합니다. 1개의 .vue파일은 1개의 컴포넌트가 됩니다.

구조

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<template>

</template>

<script>
export defualt {
data() {
return {

}
},

methods: {

},

created() {

}
}
<script>

<style>

</style>

template 영역은 HTML 요소가 들어가는 부분입니다. script 영역은 컴포넌트에 대한 속성이 들어가는 부분입니다. style은 HTML 요소에 대한 스타일을 적용 할수 있습니다.

Comment and share

Moon Star

author.bio


author.job