이 문서는 vue에 대한 빠르고 개략적인 이해를 위해서 간략한 개념만을 정리하고 있다. 따라서 실제 프로젝트에 적용하기 위해서는 참고문서에 지정된 공식문서와 다른 분들이 정리해 놓은 자료들을 참고하도록 한다. 현재 진행 중인 프로젝트에 무사히(?) 적용을 끝내고 나면 그 과정을 기준으로 새로운 정리 문서들을 추가할 예정이다.
Vue 가 무엇인가에 대한 것은 Vue 키 포인트 소개 (Gregg 소개 자료)를 참고하면 쉽게 이해할 수 있다.
현재 Top 3(Angular, React, Vue)에 해당하는 Javascript Framework으로 MVVM(Model-View-ViewModel) 패턴 기반의 UI 라이브러리
서버와 클라이언트의 UI 코드와 데이터를 분리하기 위한 기본적인 구조인 MVC (Model-View-Controller) 패턴에서 파생된 것으로 쉽게 생각하면 서버와 연계되는 데이터(Model)와 UI 코드 및 UI 동작 (View) 간의 분리와 데이터 연계를 위한 중간 매개체로의 역할 (ViewModel)을 담당하는 패턴을 의미한다.
모든 Vue 어플리케이션들은 Vue 함수를 이용해서 인스턴스를 만드는 것부터 시작된다.
아래의 예제는 아주 간단한 “Hello Vue”를 출력하는 기본 샘플이다.
1<!DOCTYPE html>
2<html>
3 <head>
4 <title>Vue First Sample</title>
5 </head>
6 <body>
7 <div id="app">
8 {{ message }}
9 </div>
10
11 <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
12 <script>
13 new Vue({
14 el: '#app',
15 data: {
16 message: 'Hello Vue.js!'
17 }
18 })
19 </script>
20 </body>
21</html>
<script>
...
new Vue({
// option properties
})
...
</script>
옵션으로 지정할 수 있는 속성들은 다음과 같다.
옵션으로 지정 가능한 전체 목록은 Vue 공식사이트의 API문서를 참고하면 된다.
Vue는 아래와 같은 라이프 사이클을 가진다.
위의 그림을 단순한 진행 스텝 단위로 정리하면 아래와 같고, 우리가 사용하게될 부분은 굵은 글씨로 표현된 이벤트 훅을 받아서 필요한 처리를 수행하면 된다.
기본 HTML 엘리먼트를 확장해서 재 사용 가능한 코드로 캡슐화한 것으로 HTML + CSS + SCRIPT 의 구성으로 특정한 기능성을 제공하기 위한 것이다.
따라서 컴포넌트를 기반으로 화면을 개발하면 코드의 재 사용성이 올라가고 일관성 유지를 하면서 빠르게 개발이 가능하다.
Vue는 사용자 지정 태그 이름에 대한 W3C 규칙을 따르지 않고 아래의 규칙을 사용한다. - 모두 소문자로 구성한다. - ’-’ 을 포함해야 한다.
Vue 인스턴스에 직접 컴포넌트를 등록한다. Vue.component(tagName, options)
1...
2<div id="example">
3 <my-component></my-component>
4</div>
5...
6<script>
7...
8// 전역 컴포넌트 등록
9Vue.component('my-compoent', {
10 // options
11 template: '<div>사용자 정의 컴포넌트!!</div>'
12})
13...
14// 인스턴스 생성
15new Vue({
16 // option properties
17 el: '#example'
18})
19
20...
21</script>
실행된 결과는 다음과 같다.
1// 렌더링 결과
2<div id="example">
3 <div>사용자 정의 컴포넌트!!</div>
4</div>
모든 컴포넌트를 전역으로 등록할 필요는 없다. 컴포넌트를 components
라는 인스턴스 옵션으로 등록해서 다른 인스턴스 또는 컴포넌트의 범위 내에서 사용하도록 할 수도 있다.
1var Child = {
2 template: '<div>사용자 정의 컴포넌트!!</div>'
3}
4
5new Vue({
6 // ...
7 components: {
8 // <my-component> 는 상위 템플릿에서만 사용할 수 있습니다.
9 'my-component': Child
10 }
11})
<ul>
, <ol>
, <table>
, <select>와 <option>
등과 같이 제한이 존재하는 경우는 오류가 발생하게 된다. 따라서 이런 문제를 해결하기 위해서는 is
라는 특수한 속성을 이용해야 한다.
1 <table>
2 <my-row>...</my-row> <!-- <tr> 태그가 아닌 사용자 정의 컴포넌트가 존재하므로 오류 발생 -->
3 </table>
4
5 <!-- 위의 문제를 해결하기 위해서는 아래와 같이 특수 속성을 사용해야 한다. -->
6 <table>
7 <tr is="my-row">...</my-row> <!-- <tr> 에 "is" 속성으로 컴포넌트 연결 -->
8 </table>
9
<script type="text/x-template">
에서 사용하는 경우.vue
컴포넌트data
의 경우는 반드시 함수여야 한다.
1 Vue.component('my-component', {
2 template: '<span>{{ message }}</span>',
3 data: { // 함수가 아니므로 경고 표시됨.
4 message: 'hello'
5 }
6 })
7
1 ...
2 <div id="example-2">
3 <simple-counter></simple-counter>
4 <simple-counter></simple-counter>
5 <simple-counter></simple-counter>
6 </div>
7 ...
8 <script>
9 var data = { counter: 0 }
10
11 Vue.component('simple-counter', {
12 template: '<button v-on:click="counter += 1">{{ counter }}</button>',
13 // 데이터는 기술적으로 함수이므로 Vue는 따지지 않지만
14 // 각 컴포넌트 인스턴스에 대해 같은 객체 참조를 반환합니다.
15 data: function () {
16 return data // 객체 반환
17 }
18 })
19
20 new Vue({
21 el: '#example-2'
22 })
23 </script>
24
data
란 객체를 생성하고 이를 반환하는 함수로 data 옵션을 구성했기 때문에 모든 컴포넌트가 동일한 객체를 바라보기 때문에 하나의 컴포넌트에서 값을 바꾸면 모든 컴포넌트가 변경되는 문제가 발생한다. 이를 해결하기 위해서는 아래와 같이 코드를 작성해야 한다.
1 ...
2 <div id="example-2">
3 <simple-counter></simple-counter>
4 <simple-counter></simple-counter>
5 <simple-counter></simple-counter>
6 </div>
7 ...
8 <script>
9 Vue.component('simple-counter', {
10 template: '<button v-on:click="counter += 1">{{ counter }}</button>',
11 // 데이터는 기술적으로 함수이므로 Vue는 따지지 않지만
12 // 각 컴포넌트 인스턴스에 대해 같은 객체 참조를 반환합니다.
13 data: function () {
14 return 0 // 객체마다 0 반환 (고유 값 배정됨)
15 }
16 })
17
18 new Vue({
19 el: '#example-2'
20 })
21 </script>
22
컴포넌트는 주로 부모-자식 관계로 구성된다. 부모 컴포넌트의 템플릿에서 자식 컴포넌트를 지정해서 사용하는 방식이기 때문에 서로 간의 정보 소통이 필요하다.
위의 그림과 같이 부모는 자식에서 속성 값을 전달하고, 자식은 부모에서 이벤트로 전달하는 방식을 취하기 때문에 각 컴포넌트의 코드가 격리된 상태로 운영되고 재 사용할 수 있다.
부모에서 자식으로 데이터 전달
prop
는 부모 컴포넌트의 정보를 전달하기 위한 사용지 지정 특성이며, 자식 컴포넌트는 props
옵션을 사용해서 받을 수 있다.
1 <script>
2 Vue.component('child', {
3 // props 정의
4 props: ['message'],
5 // 데이터와 마찬가지로 prop은 템플릿 내부에서 사용할 수 있으며
6 // vm의 this.message로 사용할 수 있습니다.
7 template: '<span>{{ message }}</span>'
8 })
9 </script>
10
11 <child message="안녕하세요!"></child> <!-- 부모 컴포넌트에서 message라는 props로 지정된 속성을 통해서 값 전달 -->
12
1 <child my-message="안녕하세요!"></child> <!-- HTML는 kebab-case를 사용해서 `my-message`로 표현 -->
2
3 <script>
4 Vue.component('child', {
5 props: ['myMessage'], // JavaScript는 camelCase로 `myMessage`로 표현
6 template: '<span>{{ myMessage }}</span>'
7 })
8 </script>
9
자식 컴포넌트에서 부모 컴포넌트로 데이터 전달
v-on:eventName
을 지정해서 부모 자식간의 이벤트 전달 및 수신$emit(eventName)
을 호출해서 부모로 전달할 이벤트 작동
1 <div id="counter-event-example">
2 <p>{{ total }}</p> <!-- 자식 컴포넌트에서 잔달된 값을 누적해서 출력 -->
3 <button-counter v-on:increment="incrementTotal"></button-counter> <!-- v-on:increment 설정으로 자식에서 전달된 이벤트 수신하여 incrementTotal 메서드 호출 (부모 컴포넌트 입장의 이벤트 수신) -->
4 <button-counter v-on:increment="incrementTotal"></button-counter> <!-- v-on:increment 설정으로 자식에서 전달된 이벤트 수신하여 incrementTotal 메서드 호출 (부모 컴포넌트 입장의 이벤트 수신) -->
5 </div>
6
7 <script>
8 Vue.component('button-counter', {
9 template: '<button v-on:click="incrementCounter">{{ counter }}</button>', // 자식 컴포넌트의 click 이벤트 처리
10 data: function () {
11 return {
12 counter: 0
13 }
14 },
15 methods: {
16 incrementCounter: function () {
17 this.counter += 1
18 this.$emit('increment') // 부모로 전달될 수 있도록 'increment' 이벤트 호출
19 }
20 },
21 })
22
23 new Vue({
24 el: '#counter-event-example',
25 data: {
26 total: 0
27 },
28 methods: {
29 incrementTotal: function () {
30 this.total += 1
31 }
32 }
33 })
34 </script>
35
동일 컴포넌트간의 직접적인 통신 방법은 제공되지 않는다.
Child > Parent > Child
방식으로 통신을 해야 한다.Event Bus
를 활용해야 한다. 이 때는 화면과 연동되는 Vue 인스턴스가 아닌 빈 인스턴스를 하나 더 만들어서 Event Bus 용도로 사용하면 된다.
1 <script>
2 var eventBus = new Vue(); // Event Bus로 활용하기 위한 별도의 Vue 인스턴스 생성
3
4 // 화면 처리용 Vue 인스턴스
5 new Vue({
6 // ...
7 })
8 ...
9 // 데이터를 보낼 컴포넌트에서 `$emit` 사용
10 eventBus.$emit('refresh', 10);
11 ...
12 // 데이터를 받는 컴포넌트에서 `$on` 사용, 단 Event Bus의 이벤트 수신은 라이프사이클 함수에서 처리
13 new Vue({
14 created: function() {
15 // Vue created event hook에서 Event Bus 수신 처리 정의
16 eventBus.$on('refresh', function(data) {
17 console.log(data); // 10
18 });
19 }
20 })
21 </script>
22
1 <script>
2 new Vue({
3 methods: {
4 callAnyMethod() { // Event Bus에서 호출할 Vue 인스턴스내의 메서드
5 // ...
6 }
7 },
8 created() {
9 var vm = this; // Vue 인스턴스를 vm에 설정
10 eventBus.$on('refresh', function(data) {
11 console.log(this); // 여기서의 this는 이벤트 버스용 인스턴스를 가리킴
12 vm.callAnyMethod() // vm은 현재 인스턴스를 가리킴
13 });
14 }
15 })
16 </script>
17
자바스크립트에서의 this
는 상황에 따라서 다른 문맥객체로 동작한다. 위의 예제에서도 굳이 this를 vm이라는 변수로 설정해 놓는 이유도 외부 메서드와 내부 메서드의 문맥객체가 다르기 때문이다.
일반적으로 this는 아래와 같은 상황들에서 다른 문맥객체로 처리된다.
- 함수 실행 : this는 전역 객체 문맥을 가진다. (브라우저라면 아마도 window 객체일 것이다)
- 엄격모드에서의 함수 실행 : this는 undefined가 된다.
- 메서드 실행 : this는 메서드를 소유하고 있는 객체로서의 문맥을 가진다.
- 생성자 실행 : this는 새롭게 인스턴싱된 객체로서의 문맥을 가진다.
- …
실행되는 상태에 따라서 this는 의미가 다른 문맥을 가르키게 되므로 항상 이 부분을 조심해야 한다.
Vue Router는 Vue 코어 라이브러리와 함께 공식 라이브러리로 제공되고 있다. 주로 Vue를 이용해서 SPA (Single Page Application)
어플리케이션을 작성할 때 사용된다.
설치는 CDN과 NPM 모두 지원한다.
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
npm install vue-router --save
Vue 라우터는 기본적으로 'Root URL'/#/'Router name'
구조를 가진다.
example.com/#/user
위의 구조에서 기본 사용되고 있는 “#“을 제거하고 싶은 경우는 아래와 같이 속성을 지정하면 된다.
new VueRouter({
mode: 'history'
})
Nested Router 라우터를 기준으로 화면을 이동할 때 Nested Router를 이용해서 지정된 하위 컴포넌트를 표시할 수 있다. 물론 컴포넌트의 구조는 Parent - Child 구조여야 한다.
1 ...
2 <!-- localhost:5000 -->
3 <div id="app">
4 <router-view></router-view>
5 </div>
6
7 <!-- localhost:5000/home -->
8 <div>
9 <p>Main Component rendered</p>
10 <app-header></app-header>
11 </div>
12 ...
13 <script>
14 ...
15 // 'localhost:5000/home'에 접근하면 Main과 Header 컴포넌트 둘다 표시된다.
16 {
17 path : '/home',
18 component: Main,
19 children: [
20 {
21 path: '/',
22 component: AppHeader
23 },
24 {
25 path: '/list',
26 component: List
27 },
28 ]
29 }
30 ...
31 </script>
32
Named Views 특정 URL로 이동했을 때 여러 개의 컴포넌트를 동시에 표현할 수 있는 방법이다.
1 ...
2 <div id="app">
3 <router-view name="appHeader"></router-view>
4 <router-view></router-view>
5 <router-view name="appFooter"></router-view>
6 </div>
7 ...
8 {
9 path : '/home',
10 // Named Router
11 components {
12 appHeader: AppHeader,
13 default: Body,
14 appFooter: AppFooter
15 }
16 }
17 ...
18 </script>
19
Nested Router vs. Named Views
Vue에서 HTTP 통신을 위해서 가장 많이 사용하는 라이브러리는 Axios로 CDN과 NPM 설치 방식을 모두 지원하며 사용하기 편리한 API와 속성들이 많다. 기본적으로 Promise 기반이기 때문에 코드를 간결하게 작성할 수 있다.
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
npm install axios
실제 사용 방법은 다음과 같다.
...
methods: {
fetchData: function() {
axios.get('URL 주소')
.then(res => {
console.log(res.data)
})
}
}
...
좀 더 자세한 설명과 샘플은 아래의 참고자료 참고
템플릿은 Vue에서 화면을 표현하기 위해서 제공되는 문법으로 Vue 인스턴스에서 관리하는 데이터와 화면과의 연계를 위한 데이터 바인딩(Data Binding)
과 화면의 조작을 위한 지시자(Directive)
로 나뉜다.
"{{ ... }}"
를 활용해서 인스턴스의 data, computed, props 속성들을 연결
할 수 있다. 또한 간단한 자바스크립트 표현식도 사용할 수 있다.
<div>{{ str }}</div>
<div>{{ number + 1 }}</div>
<div>{{ message.split('').reverse().join('') }}</div>
v-
접두사가 붙은 특별한 속성으로 화면의 DOM 조작을 쉽게할 수 있는 문법들을 제공한다.
<!-- 조건문 처리 : seen의 true/false에 따라 p 태그의 표시여부 결정 -->
<p v-if="seen">Now you see me</p>
<!-- 화면에 a 태그가 표시되는 시점에 Vue 인스턴스에서 관리하는 url 데이터 값을 href 속성 값으로 바인딩 처리 -->
<a v-bind:href="url"></a>
<!-- button의 클릭 이벤트로 doSomething이라는 메서드를 실행 -->
<button v-on:click="doSomething"></button>
|
)를 이용해서 여러 개의 필터를 적용할 수 있다.
1 <!-- message 값에 capitalize 필터를 적용하여 첫 글자를 대문자로 변경 -->
2 {{ message | capitalize }}
3 ...
4 <script>
5 new Vue({
6 filters: {
7 capitalize: function(value) {
8 if (!value) return '';
9 value = value.toString();
10 return value.charAt(0).toUpperCase() + value.slice(1);
11 }
12 }
13 })
14 </script>
15
UI에 대해서 특정 기능을 제공하는 컴포넌트로 하나의 파일 내에서 HTML, CSS, JS
코드를 모두 지원한다. 확장자는 .vue
를 사용하고 HTML 파일 내에서 Vue 개발을 진행했을 때의 한계를 극복할 수 있는 방법이다.
HTML 파일 내에서 개발할 때의 한계점들은 다음과 같다.
기본적인 골격은 다음과 같다.
<template>
<!-- HTML -->
</template>
<script>
// Javascript
</script>
<style>
/* CSS */
</style>
Single File Component로 작성된 컴포넌트를 브라우저에서 실행가능한 자바스크립트 파일로 변환해 주는 Webpack Loader로 Vue Loader를 사용하면 다음과 같은 장점이 있다.
<style>
, <template>
에 대한 각각의 Webpack Loader 지원 (eg. sass, jade, …).vue
컴포넌트의 범위(scope)로 대상으로 하는 css 스타일 지원Vue CLI3는 Vue.js 개발을 위한 시스템으로 Vue.js Core에서 공식으로 제공하는 CLI (Command Line Interface)로 Vue 어플리케이션 개발에 집중할 수 있도록 프로젝트 생성을 위한 명령어 도구로 Node 기반에서 동작하므로 당연히 Node가 사전 설치가 되어 있어야 한다.
또한 Vue 생태계에서 표준 툴을 목표로 하고 있다. 프로젝트를 구성하고 스캐폴딩에 도움을 주는 것이기 때문에 반드시 필요한 것은 아니지만 Vue.js와 관련된 오픈소스들은 대부분 Vue CLI를 통해서 설치가 가능하기 때문에 사용하는 것이 좋다.
주요 사용은 다음과 같다.
$ vue create
: 프로젝트 생성$ vue ui
: 제공되는 ui를 통해서 프로젝트 관리webpack
, webpack-dev-server
위에 구축되며 CLI Plugin을 실행하는 핵심 서비스와 webpack에 대한 설정을 포함하고 있다. 즉, webpack을 기반으로 어플리케이션의 개발, 빌드, 실행을 처리한다.Babel, Typescript, ESLint, e2d Test
등과 같은 선택적으로 설치가 필요한 Plugin을 말하며, 프로젝트 생성 시점에 포함하거나 이후에 포함시킬 수 있다.자세한 내용은 아래의 참고자료를 참고하면 된다.
React처럼 Vue도 Virtual DOM을 사용한다. Virtual DOM은 화면을 위한 기반 기술로 화면의 DOM에 추가/삭제 등의 변경이 발생하면 화면을 다시 그리는 것이 아니라 자바스크립 상에 객체로 DOM의 구조를 설정해 놓고, 실제 변경이 발생한 경우에 그 부분만 화면을 그릴 수 있도록 해서 브라우저가 화면을 다시 그리는 부하를 줄이는 기법이다.
동작하는 방법은 아래와 같이 추상적으로 예상해 볼 수 있다.
자세한 샘플은 아래의 참고자료를 참고하면 된다.