본문 바로가기

FrontEnd

[Vue3] watch vs watchEffect 사용사례 비교

반응형

해당 글을 번역한 글입니다. 

Vue3 Logo

https://www.vuemastery.com/blog/vues-watch-vs-watcheffect-which-should-i-use/

 

Vue’s watch vs watchEffect, which should I use? | Vue Mastery

Let’s unwrap the core differences between watch and watchEffect so that you can use the best tool for the job in your day-to-day code.

www.vuemastery.com

Vue에서 reactive value(ref, reactive, computed)이 변경될 때마다, 특정 함수(사이드이펙트를 포함한 함수)를 실행하고 싶다면,
watch를 사용하여 반응적 속성의 변경 사항에 반응할 수 있습니다.

watch는 computed의 대안이 아닙니다.
  • watch function 내에서 다른 reactive property의 값을 설정하고 있나요? computed 속성을 사용하세요
  • watch function 내에서 무언가를 반환하는 방법을 생각하고 있나요? computed 속성을 사용하세요

Vue.js의 watch는 환상적이며 많은 사용 사례가 있습니다.
예를 들어 속성이 변경될 때 Swift.js와 같은 외부 라이브러리에서 사이드 이펙트를 트리거해야 할 때 특히 유용할 수 있습니다.


watchEffect

watchEffect 부터 시작하겠습니다.
반응적 의존성 감시(reactive dependency watching)의 가장 기초이기 때문입니다.

watchEffect는 두 개의 매개변수를 받아들이는 함수이며, 가장 중요한 것은 첫 번째인 함수입니다.
두 번째 매개변수는 이 문서의 범위를 벗어나는 고급 사용 사례를 위한 것입니다.
import { watchEffect } from 'vue'

watchEffect(() => {
  // CODE HERE
})

JavaScript가 이 코드 에 도달하면 함수 내부의 모든 코드(첫 번째 매개 변수)가 즉시 트리거됩니다.
watchEffect의 가장 중요한 부분은, 이 함수 내에서 발견되는 모든 반응 값이 "반응적 종속성"이 된다는 것입니다.

 

아래 예제를 볼까요?

import { watchEffect, ref, computed } from 'vue'
import SomeSliderAPI from 'third-party-slider'

const props = defineProps({
  slidesPerGroup: { type: Number, default: 5 },
  touchEnabled: { type: Boolean, default: true },
  smoothScrolling: { type: Boolean, default: true }
})

watchEffect(() => {
  SomeSliderAPI.setOptions({
    slidesPerGroup: props.slidesPerGroup,
    touchEnabled: props.touchEnabled,
    smoothScrolling: props.smoothScrolling
  })
})
앞서 언급했듯이 이 watchEffect 호출에 도달할 때마다 방금 작성한 이펙트 함수의 내용을 트리거합니다.
즉, SomeSliderAPI.setOptions 메서드가 속성의 현재 값으로 실행된다는 의미입니다.

이 함수 내에는 세 가지 반응 값(slidesPerGroup, touchEnabled 및 smoothScrolling)이 "사용"되었습니다.
그것들은 모두 props 반응적 객체의 일부입니다.

 

watchEffect의 힘은 우리가 보고자 하는 반응 값을 수동으로 선언할 필요가 없다는 데 있습니다.
 
컴포넌트에 더 많은 props를 추가해야 한다고 상상해 보세요.
해당 프롭을 일일히 구독하는 것은 번거로울 수 있으므로 스프레드 연산자를 사용하도록 코드를 리팩터링해 보겠습니다.
import { watchEffect, ref, computed } from 'vue'
import SomeSliderAPI from 'third-party-slider'

const props = defineProps({
  slidesPerGroup: { type: Number, default: 5 },
  touchEnabled: { type: Boolean, default: true },
  smoothScrolling: { type: Boolean, default: true }
})

watchEffect(() => {
  SomeSliderAPI.setOptions({ ...props })
})​
이 코드는 첫 번째 예제와 똑같이 동작하지만,
한 가지 중요한 이점이 있습니다.
 
이제 컴포넌트의 모든 prop이 "감시"되고 있으며
효과 트리거를 위해 watchEffect 호출 내에서 모든 prop을 다시 선언할 필요가 없습니다.
(주 : prop이 변경되면 watchEffect가 자동으로 실행)
 
prop 대신 reactive를 사용하여 간단한 예제를 가지고 놀고 싶다면 여기를 살펴보세요

watch

Vue 2의 옵션 API watch로 알고 있는 것과 거의 동일하게 작동합니다.
Options API에서 watch(er)를 watch 속성 내부에 함수로 선언하여 작성합니다.
함수의 이름은 변경 사항을 "track"하려는 반응형 데이터의 이름을 반영합니다.

예를 들어, slidesPerGroup prop이 있는 컴포넌트에서 watch를 다음과 같이 작성합니다.
{
  props: { slidesPerGroup: { type: Number, default: 5 },
  watch: {
    slidesPerGroup (slides) {
      SomeSliderAPI.setOptions({ slidesPerGroup: slides })
    }
  }
}​

Vue 3에서는 Composition API를 통해 watch API를 더 강력하고 유연하게 사용할 수 있습니다.
마지막 예제의 watchEffect 호출을 watch로 간단히 전환하려면 몇 가지 단계를 수행해야 합니다.

import { watch, ref, computed } from 'vue'
import SomeSliderAPI from 'third-party-slider'

const props = defineProps({
  slidesPerGroup: { type: Number, default: 5 },
  touchEnabled: { type: Boolean, default: true },
  smoothScrolling: { type: Boolean, default: true }
})

watch(props,
  () => {
    SomeSliderAPI.setOptions({ ...props })
})

가장 먼저 주목해야 할 것은 watch에 전달하는 함수가 더 이상 첫 번째 매개변수가 아닙니다.
대신 우리는 변경의 원천(source)를 파라미터로 전달합니다.

 
watch 함수의 source는 문서에 명시된 대로 다음 중 하나일 수 있습니다.
  • 구독할 값을 리턴하는 getter 함수 (ex : ()=>ref.value)
  • 반응형 값(ref)
  • 반응형 객체(reactive)
  • ...위 모든 것들을 포함한 배열

여기에는 분명 단점처럼 보이는 요쇼가 있습니다.
효과 함수를 다시 실행하기 위해 함수가 "감시"하기 원하는 반응 값을 정확히 수동으로 선언해야 합니다.

하지만, 이것은 다른 측면에선 매우 중요한 이점이 있습니다.
어떤 반응적 속성이 이 효과를 유발하는지 수동으로 제어할 수 있습니다.

smoothScrolling 속성이 watch 함수를 트리거하는 것을 원하지 않는다고 생각해 보세요.
source 매개변수에서 간단하게 제거할 수 있습니다.
 
그러나 props는 반응 객체이기 때문에(위 목록에서 본 것처럼 ref 또는 reactive 객체를 사용해야 함)
추가 단계를 추가하여 refs에 사용하려는 props를 추출해야 합니다.
import { watch, ref, computed, toRefs } from 'vue'
import SomeSliderAPI from 'third-party-slider'

const props = defineProps({
  slidesPerGroup: { type: Number, default: 5 },
  touchEnabled: { type: Boolean, default: true },
  smoothScrolling: { type: Boolean, default: true }
})

const { slidesPerGroup, touchEnabled } = toRefs(props)

watch([
  slidesPerGroup, touchEnabled
],
() => {
  SomeSliderAPI.setOptions({ ...props })
})
실행해 보기 : in this SFC playground example
반응형
이제 smoothScrolling 속성이 watch의 효과 함수를 트리거할 때마다 SomeSliderAPI.setOptions 메서드가 실행되지 않습니다.

명심할 작은 주의 사항이 있습니다.
이것이 아마도 watch와 watchEffect의 차이점에 대해 기억해야 할 가장 중요한 사항 중 하나일 것입니다.

  • watch는 기본적으로 lazy 합니다.
    • watchEffect와 달리 watch는 watch 메서드 선언에 도달하자마자 효과 함수를 실행하지 않습니다!

Vue 2의 옵션 API에는 세 번째 매개변수(configuration 옵션이 있는 객체)를 전달하고
Immediate: true 속성을 설정할 수 있는 옵션이 있습니다.

watch(
  [
    slidesPerGroup, touchEnabled
  ],
  () => {
    SomeSliderAPI.setOptions({ ...props })
  }, 
  { 
    immediate: true // Not lazy anymore
  }
)

결론

솔직히 대부분의 사용 사례에서 문제 없이 서로 바꾸어 사용 할 수 있습니다만, 약간의 차이점이 있습니다.

Watch 를 사용하면 좋은 경우

  • watch는 명시적으로 종속성을 선언하여 effect를 실행하길 원하는 경우 사용합니다.
  • watch는 기본적으로 lazy 합니다.
    • 즉, 반응적 값의 변화 이전에 effect가 트리거 되지 않습니다.

WatchEffect를 사용하면 좋은 경우

  • watchEffect는 자동으로 반응적 의존성을 파악하여 효과를 실행해 줍니다.
  • watchEffect는 바로 실행(eager)됩니다.
    • pre, post와 같은 옵션을 통해 실행 시점을 제어할 수 있습니다.
    • mount 되지 않은 dom에 접근할 때 주의하세요.

반응형