WEB/VUE.js

[Vue.js] computed와 method/ watch 의 차이

자바칩 프라푸치노 2021. 11. 7. 10:59

 

 

computed와 watch — Vue.js

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

kr.vuejs.org

[주제]

프로젝트를 만들면서 computed와 watch를 사용했는데 

둘다 어떤 데이터에 의존해서 그 값이 변경하면 실행되는 것이 비슷한 것이다!

프로젝트에서는  거의 store 에서 값을 가져올때 computed를 쓰고 watch는 자동완성 검색 기능을 만들때 썼는데

적합하게 쓰고 있는 것인지 궁금하다! 

computed속성과  watch속성에 대해 알아보자!

 


[computed 속성]

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

예시에 나온 코드이다. 

템플릿에 이렇게 복잡한 코드를 넣으면 좋지 않다. 

한눈에 무슨 값이 들어가는지도 어려울 것이다.

이럴때  computed속성을 사용해준다.

 

<div id="example">
  <p>원본 메시지: "{{ message }}"</p>
  <p>역순으로 표시한 메시지: "{{ reversedMessage }}"</p>
</div>
var vm = new Vue({
  el: '#example',
  data: {
    message: '안녕하세요'
  },
  computed: {
    // 계산된 getter
    reversedMessage: function () {
      // `this` 는 vm 인스턴스를 가리킵니다.
      return this.message.split('').reverse().join('')
    }
  }
})

reversedMessage라는 값은 항상 message의 값에 의존한다. 

그래서 message가 변하면 reversedMessage도 업데이트 된다. 

가장 중요한것은 선언적으로 의존관계를 만들었다는 것이다. (뒤에 나옴)

 

 

 

 


[Method와 computed의 차이]

표현식에서 메소드를 호출할 수도 있다. 

<p>뒤집힌 메시지: "{{ reversedMessage() }}"</p>
// 컴포넌트 내부
methods: {
  reversedMessage: function () {
    return this.message.split('').reverse().join('')
  }
}

아까와 하는 행동은 같은데 위는 computed안에 있고 지금은 method안에 있다. 

차이점은 computed 속성은 종속 대상을 따라 저장(캐싱) 된다는 점이 있다. 

computed속성은 종속된 대상이 변경될때만 함수를 실행한다. 

reversedMessage는 message에 의존하고 있으므로  message가 변하지 않는 이상 다시 계산을 하지 않는다. 

 

computed: {
  now: function () {
    return Date.now()
  }
}

Date.now() 처럼 아무곳에도 의존하지 않는 경우 computed는 업데이트 되지 않는다. 

하지만 method 를 호출하면 렌더링을 다시 할때마다 항상 함수를 실행한다. 

 

 

왜 캐싱이 필요할까?

계산이 복잡하고 오래걸리는 computed속성이 있다면 렌더링 될때마다 의존된 값이 변화가 없는데도 실행되는 것은 낭비이기 때문!

 

 

 

 

 


 

[watch와 computed의 차이]

그렇다면 의존하는 값을 관찰해서 변할때마다 실행하는 watch랑 computed 는 무엇이 다른 것일까?

공식 문서에서는 명력적인 watch콜백보다 computed속성을 사용하는 것이 더 좋다고 한다.

(역자 주: watch 속성은 감시할 데이터를 지정하고 그 데이터가 바뀌면 이런 함수를 실행하라는 방식으로 소프트웨어 공학에서 이야기하는 ‘명령형 프로그래밍’ 방식. computed 속성은 계산해야 하는 목표 데이터를 정의하는 방식으로 소프트웨어 공학에서 이야기하는 ‘선언형 프로그래밍’ 방식.) 
 

명령형 프로그래밍 VS 선언형 프로그래밍

명령형 프로그래밍과 선언형 프로그래밍에 대한 비교를 어디선가 한 번쯤은 접해봤을 거라 생각합니다. 그리고 그 둘이 실제로 무엇을 의미하는지 검색을 해보셨다면 아마 아래와 같은 정의를

boxfoxs.tistory.com

 

 

예시 코드를 보자

<div id="demo">{{ fullName }}</div>

 

<명령형 코드> watch

var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar',
    fullName: 'Foo Bar'
  },
  watch: {
    firstName: function (val) {
      this.fullName = val + ' ' + this.lastName
    },
    lastName: function (val) {
      this.fullName = this.firstName + ' ' + val
    }
  }
})

 

 

<선언형 코드> computed

var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar'
  },
  computed: {
    fullName: function () {
      return this.firstName + ' ' + this.lastName
    }
  }
})

위의 경우 같은 결과값이지만 computed가 더욱 간결하고 코드의 반복이 없다.

이처럼 이미 정의된 계산식에 따라 결과값을 반환할 때 computed를 사용하는 것이 좋다. 

 

watch는 어떤 특정 조건에서 함수를 실행시키기 위한 크리거로 사용할 수 있다. 

watch는 데이터 변경에 대한 응답으로 비동기식 또는 시간이 많이 소요되는 조작을 수행하려는 경우에 가장 유용하다.

watch옵션을 사용하면 비동기 api요청을 수행하고, 얼마나 연산을 자주하는지 제한하고 중간상태를 설정할 수 있다고 한다.

computed속성은 이러한 기능을 수행할 수 없다. 

 

예시

<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>

 

728x90