관련지식
vue.js, Vue.set(), Vue.delete()

Vue.js에서 기초이면서 가장 중요한 데이터 변경 감지에 대해 알아보겠습니다. 데이터는 아래와 같이 7개가 있습니다. 심플하죠.

  1. data1: '변경전',
  2. data2 : ['A', 'B', 'C', 'D'],
  3. data3 : ['A', 'B', 'C', 'D'],
  4. data4 : ['A', 'B', 'C', 'D'],
  5. data5 : ['A', 'B', 'C', 'D'],
  6. data6 : {
  7. name : "mark",
  8. lang : 'eng'
  9. },
  10. data7 : {
  11. name : "mark",
  12. lang : 'eng'
  13. }

기본 방식

문자열의 변경은 매우 쉽습니다.

데이터 변경)

  1. this.data1 = '변경후';

배열 데이터를 변경하는것 또한 매우 쉽습니다.

배열 변경)

  1. this.data2 = ['1', '2', '3', '4'];

배열 변경 감지

배열의 일부분을 변경한다면 조금 번거로워 집니다. 만약 배열의 두번째 위치(시작은 0번째)의 값을 Z 로 변경하려고 한다면 아래와 같이 하면 감지를 하지 못합니다.

  1. this.data3[2] = 'Z';

Vue.set() 또는 this.$set() 함수를 이용하여 변경해야 합니다. 두개의 함수는 동일한 함수입니다.

배열 일부 변경)

  1. Vue.set(this.data3, 2, 'Z');

배열의 일부를 삭제 하는것은 간단합니다. 보통의 자바스크립트에서도 splice() 함수를 이용하여 배열의 일부분을 삭제 했을테니까요. 아래는 배열의 두번째 위치에서 한개의 값을 삭제하는 소스입니다.

배열 일부 삭제)

  1. this.data4.splice(2, 1);

배열 변경의 특이 사례

조금전에 “this.data3[2] = ‘Z’;” 와 같은 소스는 값의 변경을 감지하지 못한다고 했습니다. 하지만 저 방식을 써야만 하는 경우가 있습니다. 예를들어 특정 룰에 의해 값의 위치를 서로 바꾸는 로직을 만들려고 합니다. 즉 Swap 이 여러차례 발생하는 로직이라고 가정합니다. 단순히 데이터를 추가하거나 삭제하는것이 아니기 때문에 splice() 함수를 이용해서 처리하기엔 매우 복잡해집니다. 그냥 아래와 같이 처리하는게 훨씬 편하죠.

  1. var tmp = this.data5[2];
  2. this.data5[2] = this.data5[0];
  3. this.data5[0] = tmp;

그런데 이 방식은 변경 감지가 안되죠. 함수 하나만 더 호출하면 됩니다.

배열 Swap)

  1. var tmp = this.data5[2];
  2. this.data5[2] = this.data5[0];
  3. this.data5[0] = tmp;
  4. this.data5.push(); //배열의 변경된 값을 감지함

이 방식은 배열의 값 변화가 빈번하게 발생할때 유용합니다. Vue.js에서 데이터의 변경을 감지했다는 것은, 해당 데이터가 바인딩된 화면도 갱신한다는 것입니다. 그런데 만약 매우 짧은 시간안에 데이터가 수시로 변경된다면, 예를들어 1000개의 데이터가 들어있는 배열의 데이터를 정렬을 해야 할때 Vue.set() 함수를 사용하면 굉장히 많은 변경 감지가 발생하게 됩니다. 따라서 이 경우엔 위와 같은 방식으로 감지가 안되는 형태로 배열을 변경하고 마지막에 한번만 감지하도록 하는것이 좋습니다.

객체 감지

데이터를 문자열이나 배열이 아닌 객체로 만들어서 관리하는 방법도 많이 사용합니다. 로직에 의해 객체의 속성을 추가하거나 삭제할수도 있을텐데, 자바스크립트의 기본 함수로는 역시 변경 감지가 되지 않습니다.

속성을 새롭게 추가할때는 배열에서 사용했던 Vue.set() 함수를 이용할 수 있습니다.

속성 추가)

  1. this.$set(this.data6, 'age', 20);

속성을 삭제할땐 Vue.delete() 또는 this.$delete() 함수를 사용할 수 있습니다.

속성 삭제)

  1. this.$delete(this.data7, 'lang');

최종 샘플

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <title>변경감지</title>
  5. <meta charset="utf-8">
  6. <meta name="viewport" content="width=device-width, initial-scale=1">
  7. <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  8. <style>
  9. table { border:1px solid black; }
  10. table td { padding:2px 10px; }
  11. </style>
  12. </head>
  13. <body>
  14. <div id="app">
  15. <table>
  16. <tr>
  17. <td>데이터 변경</td>
  18. <td>{{data1}} <input type="button" @click="change1" value="변경"/></td>
  19. <td>this.data1 = '변경후';</td>
  20. </tr>
  21. <tr>
  22. <td>배열 변경</td>
  23. <td><span v-for="(v) in data2">{{v}}</span> <input type="button" @click="change2" value="변경"/></td>
  24. <td>this.data2 = ['1', '2', '3', '4'];</td>
  25. </tr>
  26. <tr>
  27. <td>배열 일부 변경</td>
  28. <td><span v-for="(v) in data3">{{v}}</span> <input type="button" @click="change3" value="변경"/></td>
  29. <td>Vue.set(this.data3, 2, 'Z');</td>
  30. </tr>
  31. <tr>
  32. <td>배열 일부 삭제</td>
  33. <td><span v-for="(v) in data4">{{v}}</span> <input type="button" @click="change4" value="변경"/></td>
  34. <td>this.data4.splice(2, 1);</td>
  35. </tr>
  36. <tr>
  37. <td>배열 Swap</td>
  38. <td><span v-for="(v) in data5">{{v}}</span> <input type="button" @click="change5" value="교환"/></td>
  39. <td>var tmp = this.data5[2];<br/>
  40. this.data5[2] = this.data5[0];<br/>
  41. this.data5[0] = tmp;<br/>
  42. this.data5.push(); //배열의 변경된 값을 감지함</td>
  43. </tr>
  44. <tr>
  45. <td>속성 추가</td>
  46. <td>{{data6.name}} {{data6.lang}} {{data6.age}}<input type="button" @click="change6" value="변경"/></td>
  47. <td>this.$set(this.data6, 'age', 20);</td>
  48. </tr>
  49. <tr>
  50. <td>속성 삭제</td>
  51. <td>{{data7.name}} {{data7.lang}}<input type="button" @click="change7" value="변경"/></td>
  52. <td>this.$delete(this.data7, 'lang');</td>
  53. </tr>
  54. </table>
  55. </div>
  56. <script>
  57. var app = new Vue({
  58. el: '#app',
  59. data: {
  60. data1: '변경전',
  61. data2 : ['A', 'B', 'C', 'D'],
  62. data3 : ['A', 'B', 'C', 'D'],
  63. data4 : ['A', 'B', 'C', 'D'],
  64. data5 : ['A', 'B', 'C', 'D'],
  65. data6 : {
  66. name : "mark",
  67. lang : 'eng'
  68. },
  69. data7 : {
  70. name : "mark",
  71. lang : 'eng'
  72. }
  73. },
  74. methods : {
  75. change1 : function() {
  76. this.data1 = '변경후';
  77. },
  78. change2 : function() {
  79. this.data2 = ['1', '2', '3', '4'];
  80. },
  81. change3 : function() {
  82. // this.data3[2] = 'Z'; //불가
  83. Vue.set(this.data3, 2, 'Z');
  84. },
  85. change4 : function() {
  86. this.data4.splice(2, 1);
  87. },
  88. change5 : function() {
  89. var tmp = this.data5[2];
  90. this.data5[2] = this.data5[0];
  91. this.data5[0] = tmp;
  92. this.data5.push(); //배열의 변경된 값을 감지함
  93. },
  94. change6 : function() {
  95. this.$set(this.data6, 'age', 20);
  96. },
  97. change7 : function() {
  98. this.$delete(this.data7, 'lang');
  99. }
  100. }
  101. })
  102. </script>
  103. </body>
  104. </html>