SW개발/Javascript

[Vue]Computed 와 Watch 언제 사용할까? (feat. computed vs methods)

Vue.js 공식 문서 (Computed vs Watch)

 

computed와 watch — Vue.js

Vue.js - 프로그레시브 자바스크립트 프레임워크

kr.vuejs.org

 

Computed 속성

Vue.js 의 템플릿 문법에서 Javascript 표현식을 사용하면 원하는 데이터나 연산을 DOM에 쉽게 그릴수 있다.

하지만 복잡한 연산을 템플릿 안에서 하게 될 경우 코드를 이해하고 유지보수하기 어려워진다.

<div id="leffe">
	{{ message.split('').reverse.join('') }}
</div>

위의 예시는 템플릿 문법에서 message 데이터를 역순으로 출력한 예제이다. 지금은 단순해보이지만 연산이 많아진다면 이해하는데 어렵기 때문에 computed 속성을 사용하는 것이 좋다.

 

Computed 사용 방법
<div id="leffe">
    <p>원본 메시지 : "{{ message }}"</p>
    <p>역순 메시지 : "{{ reversedMessage }}"</p>
</div>
new Vue({
    el: '#leffe',
    data: {
    	message: 'leffe tistory'
    },
    computed:{
    	// 계산된 getter
    	reversedMessage: function(){
        	return this.message.split('').reverse().join('')
        }
    }
})

위의 예시에서 computed 속성으로 reversedMessage를 선언했다. 이를 통해 reversedMessage를 getter 함수로 사용할 수 있다.

computed의 속성 또한 템플릿에 데이터 바인딩 할 수 있으므로 message의 값이 바뀌면 reversedMessage도 같이 변경되어 DOM에 업데이트 된다.

 

Computed의 캐싱 vs Methods
<div id="leffe">
	<p>원본 메시지 : "{{ message }}"</p>
    <p>역순 메시지 : "{{ reversedMessage }}"</p>
</div>
new Vue({
    el: '#leffe',
    data: {
    	message: 'leffe tistory'
    },
    methods:{
    	reversedMessage: function(){
        	return this.message.split('').reverse().join('')
        }
    }
})

computed와 methods를 사용하는 방법의 최종 결과는 동일하다. 다만 차이점이 한 가지 있다. computed는 종속 대상(reversedMessage의 종속 대상은 message)을 캐싱한다. 그렇기 때문에 computed는 종속 대상이 변경될 때만 함수를 호출한다.

즉, message 값이 변하지 않는다면 reversedMessage를 여러번 호출해도 다시 계산하지 않고 캐싱한 결과를 즉시 반환한다.

하지만, 호출할 때마다 새롭게 계산을 해야하는 경우에는 methods를 사용해야 할 것이다.

 

Computed vs Watch

Vue.js 에서는 데이터가 변경 되었을 때 콜백 함수를 정의하는 watch 속성을 제공한다. watch는 감시할 데이터를 지정하고 그 데이터가 바뀌면 어떠한 함수를 실행하라는 방식의 명령형 프로그래밍 방식이다. 일반적인 경우에서는 watch 보다는 선언형 프로그래밍인 computed를 사용하는 것이 더 좋다.

<div id="leffe">{{ fullName }}</div>
new Vue({
    el: '#leffe',
    data: {
    	firstName: 'Junki',
        lastName: 'Yoon',
        fullName: 'Junki Yoon'
    },
    watch: {
    	// firstName 바뀔 경우
    	firstName: fucntion(val){
        	this.fullName=val+''+this.lastName
        },
        // lastName 바뀔 경우
        lastName: function(val){
        	this.fullName=this.firstName+''+val
        }
    }
})

위의 예시는 watch를 사용한 예시이다.

<div id="leffe">{{ fullName }}</div>
new Vue({
    el: '#leffe',
    data: {
    	firstName: 'Junki',
        lastName: 'Yoon',
    },
    computed: {
    	fullName: function(){
        	return this.firstName+''+this.lastName
        }
    }
})

위의 예시는 computed를 사용한 예시이다. watch를 사용한 것 보다 코드가 더 간결해진 것을 확인할 수 있다.

 

computed의 setter 함수

computed는 기본적으로 getter 함수이다. 필요한 경우에는 setter 함수를 정의하여 사용할 수 있다.

<div id="leffe">{{ fullName }}</div>
new Vue({
    el: '#leffe',
    data: {
    	firstName: 'Junki',
        lastName: 'Yoon',
    },
    computed: {
    	fullName: {
            //getter
            get: function(){
            	return this.firstName + '' + this.lastName
            },
            //setter
            set: function(newVal){
            	var names = newVal.split('')
                this.firstName = names[0]
                this.lastName = names[names.length -1]
            }
        }
    }
})

 

Watch를 사용하는 경우

대부분의 경우 computed 속성을 사용하는 것이 좋다. 하지만 데이터 변경의 응답으로 비동기식 계산이 필요한 경우나 시간이 많이 소요되는 계산을 해야할 때 watch를 사용하는 것이 좋다. (데이터가 변경되어 API를 호출해야 할 때와 같은 경우)

<div id="watch-example">
  <p>
    yes/no 질문을 물어보세요:
    <input v-model="question">
  </p>
  <p>{{ answer }}</p>
</div>
<!-- 이미 Ajax 라이브러리의 풍부한 생태계와 범용 유틸리티 메소드 컬렉션이 있기 때문에, -->
<!-- Vue 코어는 다시 만들지 않아 작게 유지됩니다. -->
<!-- 이것은 이미 익숙한 것을 선택할 수 있는 자유를 줍니다. -->
<script src="https://unpkg.com/axios@0.12.0/dist/axios.min.js"></script>
<script src="https://unpkg.com/lodash@4.13.1/lodash.min.js"></script>
<script>
var watchExampleVM = new Vue({
  el: '#watch-example',
  data: {
    question: '',
    answer: '질문을 하기 전까지는 대답할 수 없습니다.'
  },
  watch: {
    // 질문이 변경될 때 마다 이 기능이 실행됩니다.
    question: function (newQuestion) {
      this.answer = '입력을 기다리는 중...'
      this.debouncedGetAnswer()
    }
  },
  created: function () {
    // _.debounce는 lodash가 제공하는 기능으로
    // 특히 시간이 많이 소요되는 작업을 실행할 수 있는 빈도를 제한합니다.
    // 이 경우, 우리는 yesno.wtf/api 에 액세스 하는 빈도를 제한하고,
    // 사용자가 ajax요청을 하기 전에 타이핑을 완전히 마칠 때까지 기다리길 바랍니다.
    // _.debounce 함수(또는 이와 유사한 _.throttle)에 대한
    // 자세한 내용을 보려면 https://lodash.com/docs#debounce 를 방문하세요.
    this.debouncedGetAnswer = _.debounce(this.getAnswer, 500)
  },
  methods: {
    getAnswer: function () {
      if (this.question.indexOf('?') === -1) {
        this.answer = '질문에는 일반적으로 물음표가 포함 됩니다. ;-)'
        return
      }
      this.answer = '생각중...'
      var vm = this
      axios.get('https://yesno.wtf/api')
        .then(function (response) {
          vm.answer = _.capitalize(response.data.answer)
        })
        .catch(function (error) {
          vm.answer = '에러! API 요청에 오류가 있습니다. ' + error
        })
    }
  }
})
</script>

위의 예시의 경우 watch 옵션을 사용하면 비동기 연산 (API 액세스)를 수행하고, 그 연산을 얼마나 자주 수행하는지를 제한하고, 최종 응답을 얻을 때까지 중간 상태를 설정할 수 있다. watch는 이러한 기능에 유용하다 (computed는 불가함)

 

지금까지 Vue.js의 공식 문서를 바탕으로 computed 와 watch에 대해 알아보았습니다.

간단히 정리하자면, watch는 비동기 연산 computed는 그 외의 부분에 사용하는 것이 유용합니다.

 

728x90