> For the complete documentation index, see [llms.txt](https://dahye-jeong.gitbook.io/til/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://dahye-jeong.gitbook.io/til/vuejs/2019-10-22-template.md).

# Template

뷰의 **템플릿은 HTML, CSS 등의 마크업 속성과 뷰 인스턴스에서 정의한 데이터 및 로직을 연결하여 사용자가 브라우저에서 볼 수 있는 형태의 HTML로 변환해주는 속성**이다.

* ES5에서 뷰 인스턴스의 template 속성 활용
* 싱글 파일 컴포넌트 체계의 `<template>` 코드 활용

템플릿 속성은 다음과 같이 두가지 방법으로 사용할 수 있다.

## 1. ES5에서 뷰 인스턴스의 template 속성 활용

```javascript
new Vue({
  template: '<p>Hello {{message}}</p>'
});
```

이전 예제에서 주로 사용한 위와 같은 코드이다.

템플릿은 사용자가 볼 수는 없지만 라이브러리 내부적으로 template 속성에서 정의한 마크업 + 뷰 데이터를 가상 돔 기반의 `render()` 함수로 변환한다. 변환된 `render()` 함수는 최종적으로 사용자가 볼 수 있게 화면을 그리는 역할을 한다.

* template 속성을 사용하지 않은 경우

```markup
<div id="app">
  <h3>{{message}}</h3>
</div>
<script>
  new Vue({
    el: '#app',
    data: {
      message: 'Hello'
    }
  });
</script>
```

template 속성을 사용하지 않은 경우에는 우선 `<h3>{{message}}</h3>` 코드를 화면에 표시하고, 인스턴스가 생성되면 message의 값을 data속성 안의 값으로 치환한다.

* template 속성을 사용한 경우

```markup
<div id="app">
</div>
<script>
  new Vue({
    el: '#app',
    data: {
      message: 'Hello'
    },
    template: '<h3>{{message}}</h3>'
  });
</script>
```

template속성을 사용하면 아무 내용이 없다가 인스턴스가 생성되면 `<h3>{{message}}</h3>` 코드가 화면에 추가되어 표시된다.

## 2. 싱글 파일 컴포넌트 체계의 `<template>` 코드 활용

```markup
<template>
  <p>{{message}}</p>
</template>
```

### 데이터 바인딩

데이터 바인딩은 HTML 화면 요소를 뷰 인스턴스의 데이터와 연결하는 것을 의미한다.

#### {{}}

뷰 인스턴스의 데이터를 HTML 태그에 연결하는 가장 기본적인 텍스트 삽입 방식이다.

```markup
<div id="app">
  <h3>{{message}}</h3>
</div>
<script>
  new Vue({
    el: '#app',
    data: {
      message: 'Hello'
    }
  });
</script>
```

data 속성의 message 값을 `{{message}}`에 연결하여 화면에 나타내는 코드이다. 여기서 data 속성의 message 값이 변경되면 뷰 반응성에 의해 화면이 자동으로 갱신된다.

만약에 뷰 데이터가 변경되어도 값을 바꾸고 싶지 않다면 `v-once` 속성을 사용하면된다.

```markup
<div id="app">
  <h3 v-once>{{message}}</h3>
</div>
<script>
  new Vue({
    el: '#app',
    data: {
      message: 'Hello'
    }
  });
</script>
```

### v-bind

`v-bind`는 id, class, style 등의 **HTML 속성 값에 뷰 데이터 값을 연결할 때 사용**하는 데이터 연결 방식이다.

```markup
<div id="app">
    <p v-bind:id="idA">ID binding</p>
     <p v-bind:class="classA">Class binding</p>
    <p v-bind:style="styleA">style binding</p>
</div>
<script>
  new Vue({
    el: '#app',
    data: {
      idA: 10,
      classA: 'container',
      styleA: 'color: blue'
    }
  });
</script>
```

다음과 같이 v-bind 속성으로 지정할 HTML 속성이나 props 속성앞에 접두사로 붙여주면된다.

```markup
<div id="app">
  <p id="10">ID binding</p>
  <p class="container">Class binding</p>
  <p style="color: blue;">style binding</p>
</div>
```

코드를 실행하면 데이터의 값이 각 `<p>` 에 연결되어 화면에 나타난다.

여기서 `v-bind:` 문법을 `:` 로 간소화할 수 있다. 예를 들어 `v-bind:id`는 `:id`와 같은 동작을 한다.

## 자바스크립트 표현식

뷰의 템플릿에서도 자바스크립트 표현식을 쓸 수 있다. `{{}}` 안에 자바스크립트 표현식을 넣으면 된다.

```markup
<div id="app">
  <p>{{ message }}</p>
  <p>{{ message + "!!!" }}</p>
  <p>{{ message.split('').reverse().join('') }}</p>
</div>
```

자바스크립트 표현식을 사용할 때 주의해야할 점이 있다.

1. 자바스크립트 선언문과 분기 구문은 사용할 수 없다.
2. 복잡한 연산은 인스턴스 안에서 처리하고 화면에는 간단한 연산 결과만 표시해야한다.

```markup
<div id="app">
  {{ var a = 10;}} <!-- X, 선언문은 사용 불가 -->
  {{ if(true){ return 100; } }}<!-- X, 분기 구문 사용 불가 -->
  {{ true? 100 : 0 }} <!-- O, 삼항연산자 표현 가능 -->
  {{ message.split('').reverse().join('') }} <!-- X, 복잡한 연산은 인스턴스 내에서 수행 -->
  {{ reversedMessage }} <!-- O, 스크립트에서 computed 속성으로 계산 후 최종 값만 표현 -->
</div>
```

## computed

cpmputed 속성은 데이터 연산들을 정의하는 영역이다.

```javascript
var vm = new Vue({
  el: '#app',
  data: {
    message: 'Hello'
  },
  computed: {
    reversedMessage: function(){
      // 여기서 this는 vm 인스턴스를 가리킨다.
      return this.message.split('').reverse().join('')
    }
  }
});
```

복잡한 계산은 `computed` (데이터 속성을 자동으로 계산해주는 속성)를 이용하여 나타내는 것이 좋다. HTML에 최종적으로 표현될 값만 나타내고, 데이터의 기본연산은 자바스크립트 단에서 함으로써 화면단 코드의 가독성을 높일 수 있기 때문이다. 또한 반복적인 연산에 대해서는 미리 계산하여 저장해 놓고, 필요할 때 바로 불러오는 **caching** 효과를 얻을 수 있다.

즉, computed 속성은 다음과 같은 장점을 가지고 있다.

* data 속성 값의 변화에 따라 자동으로 다시 연산한다.
* caching : 동일한 연산을 반복해서 하지 않기 위해 연산의 결과 값을 미리 저장하고 있다가 필요할 때 불러오는 동작 ( 종속 대상 )

### computed vs methods

methods 속성은 호출할 때만 해당 로직이 수행되고, computed 속성은 대상 데이터 값이 변경되면 자동적으로 수행된다. 즉, **수동적으로 데이터를 갱신하는지, 능동적으로 데이터를 갱신하는지의 차이**이다.

methods속성은 수행할 때마다 연산을 하기 때문에 별도로 캐싱을 하지 않지만, computed 속성은 데이터가 변경되지 않는 한 데이터를 캐싱하고 있다가, 필요할때 반환해 준다. 따라서, 복잡한 연산을 반복 수행해서 화면에 나타내야 한다면, computed 속성을 이용하는 것이 성능면에서 더 효율적이다.

```javascript
 computed: {
        message: function(){
          return Date.now();
        }
      }
```

computed 속성은 **종속 대상을 따라 저장(캐싱)**&#xB418;기 때문에 `Date.now()` 와 같이 아무곳에도 의존하지 않는 속성은 절대로 update되지 않는다.

### computed vs watch

watch 속성은 데이터 변화를 감지해 자동으로 특정 로직을 수행한다. computed 속성과 유사하지만 **computed 속성은 내장 API를 활용한 간단한 연산에 적합한 반면, watch 속성은 데이터 호출과 같이 시간이 상대적으로 많이 소모되는 비동기 처리에 적합**하다.

```markup
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div id="app">
    <input v-model="message">
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script>
    new Vue({
      el: '#app',
      data: {
        message: 'reverse message',
      },
      watch: {
        message: function(data){
          console.log(data + '값이 변경됩니다.');
        }
      }
    });
  </script>
</body>
</html>
```

## Directive

뷰 디렉티브란 HTML 태그 안에 `v-접두사`를 가지는 모든 속성들을 의미한다. 앞에서 다룬 `v-bind` 속성도 디렉티브에 해당된다. 디렉티브는 화면의 요소를 더 쉽게 조작하기 위해 사용하는 기능으로 뷰의 데이터 값이 변경되었을 대 화면의 요소들이 변경된 데이터 값에 따라 갱신된다. 화면의 요소를 직접 제어할 필요없이 뷰의 디렉티브를 활용해 화면 요소를 조작할 수 있다.

| 디렉티브 종류 | 역할                                                                                                                                                                |
| ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| v-if    | 지정한 뷰 데이터 값의 참, 거짓 여부에 따라 해당 HTML 태그를 화면에 표시하거나 표시하지 않는다. (해당 태그 완전히 삭제)                                                                                          |
| v-for   | 지정한 뷰 데이터의 개수만큼 해당 HTML 태그를 반복 출력한다.                                                                                                                              |
| v-show  | v-if와 유사하게 데이터의 진위 여부에 따라 해당 HTML 태그를 화면에 표시하거나 표시하지 않는다. (`display:none` css 를 주어 실제 태그는 남아 있고, 화면상으로만 보이지 않음)                                                   |
| v-bind  | HTML 태그의 기본 속성과 뷰 데이터 속성 연결                                                                                                                                       |
| v-on(@) | <p>화면 요소의 이벤트를 감지하여 처리할 때 사용<br><code>v-on:</code>을 간소화한 문법은 <code>@</code> 이다.</p>                                                                               |
| v-model | form에서 주로 사용되는 속성으로, 폼에 입력한 값을 뷰 인스턴스의 데이터와 즉시 동기화한다. 화면에 입력된 값을 저장해 서버에 보내거나 `watch` 와 같은 속성을 이용해 추가 로직을 수행할 수 있다. `<input>`, `<select>`, `<textarea>` 태그에만 사용가능 |

```markup
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div id="app">
    <a v-if="flag">vue.js</a>
    <ul>
      <li v-for="system in systems">{{system}}</li>
    </ul>
    <p v-show="flag">vue.js</p>
    <h5 v-bind:id="uid">Vue Documents</h5>
    <button v-on:click="popupAlert">alert</button>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script>
    new Vue({
      el: '#app',
      data: {
        flag: false,
        systems: ['ios', 'window', 'android'],
        uid: 10
      },
      methods: {
        popupAlert: function(){
          return alert('alert!!');
        }
      }
    });
  </script>
</body>
</html>
```

## 이벤트 처리

뷰 화면에서 발생한 이벤트를 처리하기 위해 `v-on` 디렉티브와 `methods` 속성을 활용한다.

```markup
<div id="app">
  <button v-on:click="clickBtn">클릭</button>
</div>
```

```javascript
var vm = new Vue({
  el: '#app',
  methods: {
    clickBtn: function(){
      alert('clicked');
    }
  }
});
```

다음 예제는 v-on:click 디렉티브를 추가하여 클릭 버튼 클릭시 `clickBtn` 메서드가 실행된다.

```markup
<div id="app">
  <button v-on:click="clickBtn(10)">클릭</button>
</div>
```

```javascript
var vm = new Vue({
  el: '#app',
  methods: {
    clickBtn: function(num){
      alert('clicked'+num);
    }
  }
});
```

다음과 같이 인자를 넘길 수 있다. 추가적으로 event 인자를 이용해 화면 돔 이벤트에 접근할 수 있다.

```markup
<div id="app">
  <button v-on:click="clickBtn">클릭</button>
</div>
```

```javascript
var vm = new Vue({
  el: '#app',
  methods: {
    clickBtn: function(event){
        console.log(event);
    }
  }
});
```

`function(event){}` 와 같이 event 인자를 정의하면 해당 돔 요소의 이벤트 객체에 접근할 수 있다.

```javascript
MouseEvent {isTrusted: true, screenX: 2181, screenY: 557, clientX: 25, clientY: 356, …}
```
