회사의 새 프로젝트에 답글 읽기 기능을 추가하면서 다음과 같은 요구 사항을 만족하는 코드를 작성해야 하는 경우가 생겼다.
따라서 각각의 답글 컴포넌트에 click
이벤트 핸들러가 바인딩되어야 하고, 클릭할 수 있다는 것을 사용자에게 알려주기 위해 cursor: pointer
스타일을 추가해야 한다.
따라서 이벤트 핸들러와 스타일 객체를 조건부로 바인딩하기 위해 다음과 같은 코드를 작성하였다.
<div
v-for="comment in Comments"
v-on="isNewComment(comment.is_dokyung_message, comment.read_at)
? readableCommentClickEventHandler(comment.id)
: {}"
:style="isNewComment(comment.is_dokyung_message, comment.read_at)
? readableCommentStyle
: {}"
:key="comment.id"
>
...
<script lang="ts">
export default defineComponent({
setup(props) {
const readableCommentStyle = reactive({
cursor: 'pointer'
})
const readableCommentClickEventHandler = (commentId: number) => ({
click: () => readComment(commentId)
})
return {
...
}
}
})
</script>
이 때 조건부 바인딩의 조건이 되는 isNewComment
는 methods로 작성하였는데, 이는 필요한 조건들을 인자로 받아서 이에 맞는 boolean 값을 리턴해주는 로직이 필요하기 때문이었다.
const isNewComment = (isDokyungMessage: string, readAt: Date | null) => {
return isDokyungMessage === 'Y' && !readAt
}
나는 웬만한 조건문 안의 조건들은 computed로 작성을 해 왔었다. 그럼에도 불구하고 여기서 메서드를 사용했던 이유는 인자로 특정한 조건들을 받아야 했기 때문이다. 이렇게 놓고 보니, computed로 작성했던 기존의 코드들 역시 그냥 메서드로 대체하여 사용하면 되는 게 아닌가 하는 의문이 들었다. 즉, 메서드로도 작성할 수 있는 조건들을 굳이 computed로 사용하는 이유는 무엇인가?
vue의 공식 문서에는 다음과 같은 이야기가 있다.
Instead of a computed property, we can define the same function as a method. For the end result, the two approaches are indeed exactly the same. However, the difference is that computed properties are cached based on their reactive dependencies.
메서드와 computed 둘 다 같은 기능을 할 수 있지만, computed가 다른 점은 캐싱이 가능하다는 점. 밑의 코드를 보자.
methods: {
isBookFromDokyung() {
return this.book.owner === 'Dokyung' ? true : false
}
}
위의 methods 코드와 밑의 computed 코드는 근본적으로는 같은 기능을 한다. 만약 두 속성 모두 컴포넌트에서 한 번씩만 호출되는 경우는 차이가 없을 수 있다.
computed: {
isBookFromDokyung() {
return this.book.owner === 'Dokyung' ? true : false
}
}
하지만 이 둘이 여러 곳에서 접근된다고 가정해 보자.
<template>
<div v-if="isBookFromDokyung" />
...
<div v-for="book in books" />
<span v-if="isBookFromDokyung" />
</div>
...
</template>
methods의 경우에는 접근이 일어날 때마다 메서드가 호출되어 계산이 이루어진다. 한 두번은 그러려니 할 수 있겠지만, 그 횟수가 수십 번, 수백 번이 일어나는 경우에는 성능 저하가 일어날 수 있다.
그러나 computed의 경우는 다르다. 만약 computed가 기대고 있는 reactive dependency들이 변하지 않는 이상, computed는 해당 계산을 하지 않고 이전에 계산되었던 값을 그대로 리턴한다.
다시 위의 코드로 돌아와서, 사실 저 코드는 computed를 사용하든 methods를 사용하든 큰 차이는 없을 것이다. 매 번 comment 데이터가 v-for를 통해 바뀌면서 새로 들어오기 때문에 계속해서 갈아치워지기 때문이다. 물론 computed의 경우 일부러 setter를 설정해 writable한 computed 속성을 만들 수도 있다. 하지만 캐싱에 대한 이점이 없다면 굳이 computed를 사용하지 않아도 될 것 같다.
예전에 팀원들이랑 computed를 사용하는 것이 성능상 떨어지지 않을까 고민했던 적이 있다. dependency가 변할 때마다 다시 계산을 수행하기 때문에, 불필요한 계산이 계속해서 일어나는 것이 아닐까 우려했던 것이다. 하지만 지금 돌이켜 보니 그 주장은 다시 생각해 볼 여지가 있는 듯 하다. 오히려 computed는 캐싱을 이용해 불필요한 계산을 줄임으로써 성능을 향상시키는 역할을 수행하고 있지 않을까 한다.
<참고 링크>
https://vuejs.org/guide/essentials/computed.html#computed-caching-vs-methods
<22.10.17 노션 글 아카이빙>