Vue.js | 표시 데이터 변경 및 이벤트 처리

Vue.js에 표시된 데이터를 이벤트를 통해 갱신한다.

  • Vue 2.5.4

직접 DOM을 변경하지 않아도 된다.

데이터를 변경되면 자동적으로 DOM(Document Object Model)도 변경해야 했던 처리를 Vue.js가 해준다. 이는 Vue.js와 같은 데이터 바인딩 라이브러리들의 큰 특징이며, 데이터 잘못 표시하거나 표현에 버그가 덜 발생시킬 수 있다. 덧붙여 말하자면, 많은 개발자들이 JavaScript 프레임워크를 사용하는 가장 큰 이유는 이런 기능이 유용하기 때문이다.

가상 DOM에 대한 여러가지

Vue.js는 최초에 응용 프로그램에 사용할 템플릿을 분해하여 실제 DOM과 대응되는 트리 구조의 가상 DOM으로 만든다. 트리를 구성하는 각각의 요소는 VNode이라고 한다. 데이터를 변경되어도 즉시 DOM을 변경하지 않고, 가상 DOM을 통해 동기화 처리 결과를 비동기식으로 실제 DOM에 반영하기 때문에 불필요한 DOM 조작이 없어서 빠르다.

비동기로 DOM을 변경하기 위해서는 데이터를 조작한 후에 동기적으로 DOM에 접근해서도 먼저 먼저 변경되지 않은 상태로 그대로 남아 있는다.

이벤트 처리를 등록하기

편의상 이벤트에 대한 간단한 설명하자면, “요소에 마우스를 올라 왔을 때”, “버튼을 클릭했을 때"등의 이벤트를 이용(핸들링)하기 위해서는 v-on 지시문을 사용한다. jQuery의 on과 비슷하다.

<button v-on:click="처리 내용 및 메소드 이름">click!</button>

v-on은 @으로 생략이 가능하다. v-bind와 동일하게 처음에는 v-on으로 작성하고 익숙해지면 생략하는 것을 추천한다.

<button @click="처리 내용 및 메소드 이름">click!</button>

수식어을 사용할 수 있게 되면 prevent 같이 자기 자신의 판정도 가능해 진다.

<div @click.self="처리 내용 및 메소드 이름" class="overlay">...</div>

간단한 텍스트 변경하기

간단한 텍스트를 변경해 보자. 아래 예제를 작성해서 버튼을 클릭하면 methods에 정의되어 있는 test 메소드를 호출한다. 처리 내용은 데이터에 정의되어 있는 count 속성의 수를 늘린다.

예제 코드 실행

<div id="app" class="area">
    <p>당신은 {{ count }}회 클릭했습니다.</p>
    <button v-on:click="test">click!</button>
</div>

코드를 보면 알수 있듯이 DOM을 변경하는 처리 작업을 없다.

new Vue({
  el: '#app',
  data: {
    count: 0
  },
  methods: {
    test: function() {
      this.count++
    }
  }
})

this.count++으로 count 값을 증가시키면 표시 부분도 같이 증가된다.

v-on도 지시문이기에 값에 식을 사용할 수도 있다. 반복 사용하지 않을 짧은 코드라면 메소드를 없이 직접 식을 작성할 수 있다. 템플릿에 코드를 넣으면 보기에도 좋지 않기에 기본적으로 메소드를 지정하는 것을 추천한다.

<button @click="count++">click!</button>

예제 코드 실행

v-model을 사용하기

v-model은 폼(form) 입력과 데이터를 연결하는 지시문으로, 연결된 폼 입력 값과 선택 값과 데이터가 항상 동기화할 수 있다.

예제 코드 실행

<div id="app">
  <p>당신은 '{{ message }}'라고 입력했습니다.</p>
  <p><input type="text" v-model="inputMessage" /><button @click="getMessage">click!</button></p>
</div>
new Vue({
  el: '#app',
  data: {
    message: '',
    inputMessage: ''
  },
  methods: {
    getMessage: function() {
      this.message = this.inputMessage
    }
  }
})

데이터와 동기화되어 있기에 message를 직접 연결되면, 위에 예제와 같이 클릭하지 않고도 입력한 순간에 message가 변경된다.

$refs을 사용해 보자

$ refs를 사용하면 직접 DOM을 참고할 수가 있다. 나중에 배우게 될 구성 요소 태그도 사용할 수 있다. 주위해야 할 점은 이는 가상 DOM을 사용하지 않기 때문에 렌더링 최적화를 하지 않는다. 뭔가 조작할 때마다 DOM에 액세스하여 다시 그리기 때문에, 그림의 갱신에 사용에는 적합하지 않다. 기본적으로 많이 사용하지는 않지만 요소를 참조하고 싶을 때도 있을 수 있기에 기억해 두도록 하자.

템플릿의 태그에 ref에 이름을 붙여두면 this.$refs.NAME으로 참조가 가능하다.

예제 코드 실행

<div id="app">
  <p><input type="text" ref="message"> <button @click="getMessage">click!</button></p>
</div>
new Vue({
  el: '#app',
  methods: {
    getMessage: function() {
      // ref="message"의 입력 값을 사용한다.
      alert(this.$refs.message.value)
    }
  }
})

요소의 위치나 높이 등 직접 DOM에 액세스를 해야 경우에 사용한다.

목록 데이터 갱신

배열 갱신

준비된 영어 단어 목록 중에서, 인덱스 0 번의 “apple"를 대문자로 하도록 변경해 보자.

<div id="app">
  <ul>
   <li v-for="val in list">{{ val }}</li>
  </ul>
  <p><button @click="test">click!</button></p>
</div>
new Vue({
  el: '#app4',
  data: {
    list: ['apple', 'banana', 'strawberry']
  },
  methods: {
    test: function() {
      this.list[0] = this.list[0].toUpperCase()
    }
  }
})

처음 아무 생각없이 이렇게 작성을 해보았는데 이는 동작하지 않는듯 하다. 참고

코드 실행

참고 URL을 읽어 보면, JavaScript의 제한으로 인해 배열은 직접 변경할 수 없기 때문에 내용이 바뀐 것을 Vue가 감지할 수 있도록 $set 메소드를 사용하라는 내용이다.

vm.$set(object, key, value)

위 데이터를 대체는 이런 식으로 하면 된다. 메소드의 내용을 고쳐 보자.

this.$set(this.list, 0, this.list[0].toUpperCase())

코드 실행

이번에는 제대로 변경이 되었다. 아무튼, 이 사양은 항상 그렇듯이 IE 때문이다. 아마 다음 버전(Vue.js3?)는 IE의 서포트를 하지 않게되고 Vue.set은 필요하지 않게 되지 않을까 싶다.

객체 변경

객체는 제대로 문제 없이 변경할 수 있다.

<div id="app">
  <ul>
    <li v-for="(val, key) in list">(Key:{{ key }}) {{ val }}</li>
  </ul>
  <p><button @click="test">click!</button></p>
</div>
new Vue({
  el: '#app',
  data: {
    list: {
      a: 'apple',
      b: 'banana',
      c: 'strawberry'
    }
  },
  methods: {
    test: function() {
      this.list.a = this.list.a.toUpperCase()
    }
  }
})

코드 실행

덧붙이지면, 여기에 원래 없는 속성으로 d를 데이터로 추가할 때도 this.$set이 필요하다.

객체 목록 변경

또 배열을 일단은 $set을 사용하지 않고 시도해 보도록 하자.

<div id="app">
  <ul>
    <li v-for="(val, idx) in list" v-bind:key="val.name">
      (No:{{ idx }}) {{ val.name }} {{ val.count }}개
    </li>
  </ul>
  <button v-on:click="test(100, $event)">click!</button>
</div>

v-on 핸들러는 매개 변수를 갖는 인라인으로도 수행할 수 있다. 이 경우 $event으로 원래 이벤트 객체를 넘길 수 있다.

new Vue({
  el: '#app',
  data: {
    list: [{
      name: '사과',
      count: 0
    }, {
      name: '바나나',
      count: 0
    }, {
      name: '딸기',
      count: 0
    }]
  },
  methods: {
    test: function(count) {
      this.list[0].count = count
    }
  }
})

예제 코드 실행

정상 동작을 하였다. 배열이 인덱스에 직접 만지지만 않으면 되는듯 하다. 아무래도 안에서 객체의 모니터링이 동작하고 있는 것 같다.

목록 데이터 v-model으로 변경하기

v-for로 얻은 목록의 데이터를 직접 v-model에 지정하면 안되기 때문에, 그런 경우는 대신에 @input를 사용하는 것지만, 원래 배열 데이터이라면 연결이 가능 하다.

<div id="app">
  <ul>
    <li v-for="(val, idx) in list" v-bind:key="idx">
      (No:{{ idx }}) {{ val }}
    </li>
  </ul>
  <ul>
    <li v-for="(val, idx) in list" v-bind:key="idx">
      (No:{{ idx }}) {{ val }} <input size="10" v-model="list[idx]">
    </li>
  </ul>
</div>
new Vue({
  el: '#app',
  data: {
    list: ['apple', 'banana', 'strawberry']        
  }
})

예제 코드 실행

jQuery와 같이 외부에서 변경에 대응하기

처음에는 아무래도 jQuery와 같은 플러그인을 함께 사용하고 싶은 일도 있을지도 모른다. 그럴 때는 JavaScript dispatchEvent를 사용하면 된다.

<section id="app">
  <p>{{ message }}</p>
  <input type="text" v-model="message" id="message">
  <h2>jQuery Button</h2>
  <button data-update="jQuery Text1">Text1</button>
  <button data-update="jQuery Text2">Text2</button>
  <h2>Vue Button</h2>
  <button @click="update('Vue Text1')">Text1</button>
  <button @click="update('Vue Text2')">Text2</button>
</section>
// IE를 제외하면 Event 객체도 OK!
var eventInput = document.createEvent('UIEvents')
eventInput.initEvent('input', false, false);

$(document).on('click', '[data-update]', function() {
  $('#message').val($(this).data('update'))
  // 외부에서 갱신되면, 이벤트를 호출한다.
  $('#message')[0].dispatchEvent(eventInput)
});

new Vue({
  el: '#app',
  data: {
    message: 'Message'
  },
  methods: {
    update(message) {
      this.message = message
    }
  }
});

예제 코드 실행

참조만이라면 그런 문제는 없겠지만, 테이블 정렬과 같은 요소를 재구성하는 것은 가급적 피하는 것이 좋다. 실제 DOM을 조작하여도 이는 데이터를 갱신하는 것은 아니기 때문에, 경우에 따라 가상 DOM에 의해 수정이 되기 때문이다. Vue.js에서는 주역은 DOM이 아니라 데이터가 중요한 포인트이다. 목록의 순서를 바꾸거나 필터링하려면 다음 장에서 소개될 산출 속성을 사용하면 매우 간단해 진다.

데이터를 갱신할 때 빠트린 포인트

객체에 차례대로 넣는 경우에도, 배열 인덱스를 사용하여 갱신할 경우는 $set을 사용하지 않으면 반응하지 않는다! 아무래도 필요한 경우 외에는 될 수 있으면 마지막에 객체 목록을 갱신하는 것이 편할지도 모른다.

결론

데이터 및 요소를 연동시키는 것은 jQuery으로 작성하는 것보다 매우 편하지만 그래도 코드가 커지게 되면 데이터의 무결성을 유지하는 것이 힘들어 진다고 한다. 지금은 Vue를 간단히 사용하고는 있지만 대규모로 개발하는 큰 것을 만든 본 적이 없기 때문에 별로 와닫지 않을 지도 모른다. 암튼 이를 보완하기 위해 등장한 것이 Vuex인거 같다. 공부하는 것은 해도해도 끝이 없나보다!

이번에는 v-on에서 이벤트 등록 방법과 데이터 업데이트의 기본을 배웠다. 다음에는 조건 분기에 의한 조작을 해 보도록 하겠다.