[Vue] 리렌더링(re-rendering)
Vue는 데이터가 변경되면 화면이 바로 갱신되는 반응형 시스템을 가지고 있다.
컴포넌트 인스턴스에는 watcher
라는 것이 있으며, 이 녀석에 의해 어떤 데이터의 수정이 발생하면 다시 렌더링 된다.
아래는 Vue 2 문서에서 어떻게 변경을 감지하는지를 나타내는 그림이다.
그런데, 배열과 객체에서 수정이 발생해도 다시 렌더링이 되지 않는 경우들이 있기 때문에 주의해야 한다.
배열 변경 감지
Vue는 배열의 변경을 감지하기 위해 래핑된 메소드를 제공한다. 이 메소드를 사용해야 Vue가 배열의 변경을 감지하고 화면을 갱신한다.
push()
pop()
shift()
unshift()
splice()
sort()
reverse()
JavaScript 언어적 한계로 인해 Vue는 배열에 대해 다음과 같은 변경 사항을 감지할 수 없다.
- 인덱스로 배열에 있는 항목을 직접 설정하는 경우
vm.items[index] = newValue
- 배열 길이를 수정하는 경우
vm.items.length = newLength
예시
JavaScript에서는 배열에 요소를 추가 또는 수정할 때, push()
또는 splice()
를 주로 사용하는데 arr[2] = 26
와 같이 인덱스로 바로 접근할 수도 있다.
테스트
버튼을 누르면 리스트의 첫 번째를 ‘강아지’에서 ‘고양이’로 교체하는 예시이다. splice()
를 활용한다.
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 id="app">
<ul>
<li v-for="(a, idx) in animal" :key="idx">
{{ a }}
</li>
</ul>
<button @click="test">테스트</button>
</div>
</template>
<script>
export default {
name: "App",
data() {
return {
animal: ["강아지", "말", "도마뱀"],
};
},
methods: {
test() {
this.animal.splice(0, 1, "고양이");
},
},
};
</script>
<style></style>
만약 test()
가 인덱스로 바로 접근하는 방식이라면 결과는 아래와 같다. data는 변경됐지만, 화면은 갱신이 되지 않았다.
1
2
3
4
test() {
// this.animal.splice(0, 1, "고양이");
this.animal[0] = '고양이';
},
객체 변경 감지
Vue 2 문서를 보면 Vue는 속성의 추가, 제거를 감지할 수 없다고 한다. 인스턴스 초기화 중에 data 객체의 속성에 대해 getter/setter를 만들고, 이를 통해 속성에 접근해야 감지할 수 있다.
예시
user
객체를 정의하고, 테스트
버튼을 누르면 address
속성을 추가하려고 한다.
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
<template>
<div id="app">
<ul>
<li v-for="(value, key, idx) in user" :key="idx">
{{ key }} : {{ value }}
</li>
</ul>
<button @click="test">테스트</button>
</div>
</template>
<script>
export default {
name: "App",
data() {
return {
user: {
username: "JANG",
age: 26,
github: "minho-jang",
},
};
},
methods: {
test() {
this.user.address = "경기도 의왕시";
},
},
};
</script>
<style></style>
없었던 속성 address
를 추가했기 때문에, user.address
는 추가되었지만 Vue는 이를 감지하지 못하고 화면을 갱신하지 않는다.
이를 해결하기 위해서 2가지 방법을 사용할 수 있다.
Vue.set(object, propertyName, value)
- 새로운 객체를 재할당
Vue.set(object, propertyName, value)
객체에 반응형 속성을 추가하기 위해 Vue.set()
를 사용할 수 있다. 아래와 같이 test()
를 변경하고 실행하면 잘 동작하는 것을 볼 수 있다.
1
2
3
test() {
this.$set(this.user, 'address', '경기도 의왕시');
},
새로운 객체를 재할당
새로운 객체를 다시 할당시키는 방법으로도 해결할 수 있다. 이 때, spread 연산자(...
)를 활용하면 쉽게 구현할 수 있다.
1
2
3
4
5
6
test() {
this.user = {
...this.user,
address: '경기도 의왕시',
};
},
OFF THE RECORD
사실 개발을 하면서 배열을 다루면 splice()
나 sort()
와 같은 주로 함수를 사용하기 때문에 배열 변경을 감지하지 못하는 경우는 없었으나, 객체를 다룰 때 많이 헤맸다.
자식 컴포넌트에게 prop
로 데이터는 잘 전달된 것 같은데, 화면 갱신이 되지 않아서 watch
를 써야하는 건가? Vuex를 써야하는 건가? 싶었다.
이와 비슷한 문제를 겪는다면 Vue가 감지할 수 있도록 데이터를 수정했는지 확인해보자.
댓글 남기기