Basic Example
템플릿 내에서의 표현식은 매우 편리하지만 간단한 작업을 위한 것으로, 템플릿에 너무 많은 로직을 넣으면 코드가 복잡해지고 유지보수가 어려워질 수 있다. 예를 들어, 아래와 같이 중첩된 배열을 가지는 객체가 있을 때
export default {
data() {
return {
author: {
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
]
}
}
}
}
그리고 author가 이미 책을 가지고 있는지의 여부에 따라 다른 메시지를 표시하고 싶다면
<p>Has published books:</p>
<span>{{ author.books.length > 0 ? 'Yes' : 'No' }}</span>
이 시점에서 템플릿은 조금 복잡해진다. author.books에 따라 계산을 수행해야 하며 만약 해당 계산을 템플릿에 두 번 이상 포함해야 한다면 상황은 더 복잡해진다.
그렇기 때문에 반응형 데이터를 포함하는 복잡한 논리의 경우 Computed(계산된) 속성을 사용하는 것이 좋다. 아래는 동일한 케이스를 리팩토링 한 결과이다.
export default {
data() {
return {
author: {
name: 'John Doe',
books: [
'Vue 2 - Advanced Guide',
'Vue 3 - Basic Guide',
'Vue 4 - The Mystery'
]
}
}
},
computed: {
// a computed getter
publishedBooksMessage() {
// `this` points to the component instance
return this.author.books.length > 0 ? 'Yes' : 'No'
}
}
}
<p>Has published books:</p>
<span>{{ publishedBooksMessage }}</span>
여기서는 계산된 속성인 publishedBooksMessage를 선언했다.
어플리케이션 data에서 books 배열의 값은 변경하면 그에 따라 publicationBooksMessage가 어떻게 변경되는지 확인할 수 있다.
일반적인 속성과 마찬가지로 computed 속성도 데이터 바인딩이 가능하다. Vue는 this.publishedBooksMessage가 this.author.books에 의존한다는 것을 알고 있기에 this.author.books가 변경되면 this.publishedBooksMessage에 의존하는 모든 바인딩을 업데이트한다. 그리고 가장 좋은 점은 종속성 관계를 선언적으로 만들었다는 것인데, computed getter 함수에 사이드 이펙트가 없으므로 테스트하거나 이해하기에도 더 쉽다.
Computed Caching vs. Methods
표현식에서 메서드를 호출하여 동일한 결과를 얻을 수도 있다.
<p>{{ calculateBooksMessage() }}</p>
// in component
methods: {
calculateBooksMessage() {
return this.author.books.length > 0 ? 'Yes' : 'No'
}
}
Computed 속성 대신 메서드와 동일한 함수를 정의할 수 있다. 결과적으로 두 가지 접근 방식은 동일하지만 차이점은 computed 속성은 반응형(reactive) 종속성을 기반으로 캐시 된다는 것이다. Computed 속성은 반응형 종속성 중 일부가 변경된 경우에만 다시 계산된다. 즉, author.books가 변경되지 않는다면 publishedBooksMessage computed 속성에 대해 여러 번 접근하더라도 함수를 다시 실행할 필요 없이 이전에 계산된 결과를 즉시 반환한다.
또한 이것은 Date.now()가 반응형 종속성이 아니기 때문에 아래 예저의 computed 속성이 업데이트되지 않음을 의미한다.
computed: {
now() {
return Date.now()
}
}
이와 다르게 메서드 호출은 렌더링이 다시 발생할 때마다 항상 함수를 실행한다.
그렇다면 캐싱이 필요한 이유는 무엇일까? 거대한 배열을 반복하고 많은 계산을 수행해야 하는 값 비싼 list computed 속성이 있고 list에 종속적인 다른 computed 속성들이 있다고 가정해보자. 캐싱이 없다면, list의 getter는 필요한 것 이상으로 훨씬 많이 실행될 것이다. 캐싱을 원하지 않는 경우라면 메서드를 사용하는 것이 낫다.
Writable Computed
Computed 속성은 기본적으로 getter 전용이기에 computed 속성에 새 값을 할당하려고 하면 런타임 경고가 표시된다. 작성할 수 있는 computed 속성이 필요한 드문 경우에 getter와 setter를 모두 제공하여 속성을 만들 수 있다.
export default {
data() {
return {
firstName: 'John',
lastName: 'Doe'
}
},
computed: {
fullName: {
// getter
get() {
return this.firstName + ' ' + this.lastName
},
// setter
set(newValue) {
// Note: we are using destructuring assignment syntax here.
[this.firstName, this.lastName] = newValue.split(' ')
}
}
}
}
이제 this.fullName = 'John Doe'를 실행하면 setter가 호출되고 이에 따라 this.firstName 및 this.lastName이 업데이트된다.
Best Practices
Getters should be side-effect free
Computed getter 함수는 순수한 계산만 수행하고 사이드 이펙트가 없어야 함을 기억하는 것이 가장 중요하다. 예를 들어 비동기 요청을 하거나 computed getter 내부에서 DOM을 변경해서는 안된다. Computed 속성을 "다른 값을 기반으로 값을 파생하는 방법을 선언적으로 설명하는 것"이라고 생각해야 한다. Computed 속성의 유일한 책임은 해당 값을 계산하고 반환하는 것이어야 한다. 추후 watchers를 사용하여 상태 변경에 대한 반응으로 사이드 이펙트를 수행하는 방법에 대해 언급될 것이다.
Avoid mutating computed value
Computed 속성에서 반환된 값은 파생된 상태로, 소스 상태가 변경될 때마다 새로 생성되는 임시 스냅샷으로 생각할 수 있다. 스냅샷을 변경하는 것은 의미가 없으므로 computed 반환 값을 읽기 전용으로 처리되어야 하며 변경되지 않아야 한다. 변경되지 않는 대신에 새 계산을 트리거하기 위해 의존하는 소스 상태를 업데이트해야 한다.
관련 Repo
참고자료
https://vuejs.org/guide/essentials/computed.html
'Development > Vue.js' 카테고리의 다른 글
[Vue.js] Document 따라하기 - Conditional Rendering (0) | 2022.06.07 |
---|---|
[Vue.js] Document 따라하기 - Class and Style Bindings (0) | 2022.06.03 |
[Vue.js] Document 따라하기 - Reactivity Fundamentals (0) | 2022.05.27 |
[Vue.js] Document 따라하기 - Template Syntax (0) | 2022.05.24 |
[Vue.js] Document 따라하기 - Creating a Vue Application (2) | 2022.03.11 |