vue의 watch 활용(watch vs computed)

김서영·2025년 2월 13일
0

Vue 공부

목록 보기
1/5

오늘은 vue에서 onInput 이벤트로 작성된 기능을 watch로 변경하는 것이 좀 더 나을 것 같아 이를 수정해 보았다.

vue의 Watch 란?

watch는 데이터 변경 시 호출될 감시 콜백을 선언하는 것이다.

옵션은 키가 감시할 반응형 컴포넌트 인스턴스 속성(data 또는 computed를 통해 선언된 속성) 이고, 값이 해당 콜백으로 이루어진 객체이다.
그리고 콜백은 감시되는 소스의 새 값과 이전 값을 인자로 수신한다.

추가 옵션

  • immediate : 감시자 생성 시 즉시 콜백 트리거, 첫 호출 시 이전 값은 undefined
  • deep : 객체 또는 배열인 경우 내부 깊숙한 곳에서 변경 사항 발생 시에도 콜백이 실행되도록 함
  • flush : 콜백 실행 타이밍을 조정
  • onTrack/onTrigger : 감시자의 의존성을 디버그

기존에는 이벤트를 정의해, input 값이 변경될 때마다 함수를 실행시키는 방식으로 코드를 작성했으나 watch로 해당 값을 지켜보고 변경될 때마다 훅을 실행시키는 방법이 나을 것 같아 변경하려고 한다.

때문에 기존에 실행시키던 함수를 삭제하고 아래와 같이 watch를 작성해주었다.

/**
 * - props.text와 props.modelValue를 바라보고 이를 반응형으로 감지해 props.text나 props.modelValue 값이 변경될 때마다 실행됩니다
 */
watch(
    [
      () => props.text,
      () => props.modelValue
    ],
    ( [ newText, newModelValue ] ) => {
      const newValue = newText || newModelValue;
      if ( _model.value !== newValue ) {
        _model.value = newValue;
      }
    }
);

이 코드는 () => props.text, () => props.modelValue를 감시하고 둘 중 하나라도 변경되면 콜백함수를 실행시킨다.
그리고 newValue로 변경된 값을 저장하고, _model.value와 같이 다르다면 _model.value 값을 newValue 값으로 변경해준다.

여기에서 () => props.text, () => props.modelValue로 작성해주는 이유는 "반응형 참조"를 만들기 위해서이다.
만약 함수로 감싸주지 않고

watch(
  [
    props.text,          // ❌ 잘못된 방법
    props.modelValue     // ❌ 잘못된 방법
  ],
  // ...
)

이렇게 작성한다면, props의 현재 값만 한번 가져오고 끝나버린다.
즉, props의 "값" 자체만 감시하게 되는 것이다.

하지만, 함수로 감싸주어

watch(
  [
    () => props.text,        // ✅ 올바른 방법
    () => props.modelValue   // ✅ 올바른 방법
  ],
  // ...
)

이렇게 작성해줄 경우 prps의 속성에 대한 "참조"를 감시하게 된다.
즉, props.text나 props.modelValue가 변경될 때마다 함수가 실행되어 새로운 값을 가져오는 것이다.

간단히 말해서,
화살표 함수 없이: "현재 값"만 한 번 보고 끝
화살표 함수로: "값이 있는 위치"를 계속 지켜봄

추가 학습 : Computed 란?

vue에서 watch와 computed의 기능이 비슷해 헷갈려 하는 경우가 많다.
computed는 보통 템플릿에서 복잡한 수식을 사용하지 않기 위해서 사용된다.
이는 기본적으로 읽기 전용이고, 이 속성에 새 값을 할당하려고 하면 에러가 발생한다.
하지만, 수정이 필요한 경우 getter, setter를 사용해 속성 수정이 가능하다.

Computed 기본 예시)

<script setup>
import { reactive, computed } from 'vue'

const author = reactive({
  name: 'John Doe',
  books: [
    'Vue 2 - Advanced Guide',
    'Vue 3 - Basic Guide',
    'Vue 4 - The Mystery'
  ]
})

// a computed ref
const publishedBooksMessage = computed(() => {
  return author.books.length > 0 ? 'Yes' : 'No'
})
</script>

<template>
  <p>Has published books:</p>
  <span>{{ publishedBooksMessage }}</span>
</template>

위와 같이 publishedBookMessage computed를 선언하면, 종속하는 대상이 변경할 경우 실행해 변경된 계산 값을 반환한다.

표현식에서 메서드를 호출해 동일한 결과를 얻을 수도 있겠지만, 이는 분명한 차이가 있다.

함수로 작성한 예시)

<p>{{ calculateBooksMessage() }}</p> // 렌더링 될 때마다 함수가 실행됨!

// in component
function calculateBooksMessage() {
  return author.books.length > 0 ? 'Yes' : 'No'
}

위와 같이 작성한다면, 렌더링이 발생할 때마다 항상 함수를 실행하게 된다.

하지만, computed는 계산된 속성이 반응 종속성에 따라 캐시가 된다는 것이다.
쉽게 말하자면 computed는 종속하는 대상이 변경하지 않으면 publishedBooksMessage getter 함수를 다시 실행하지 않고도 이전에 계산된 결과를 즉시 반환한다는 것이다.

Computed getter&setter 활용 예시)

computed: {
  fullName: {
    // getter
    get: function () {
      return this.firstName + ' ' + this.lastName
    },
    // setter
    set: function (newValue) { // 원래는 computed의 속성에 새 값을 할당하면 오류가 발생하지만, setter를 활용하면 새 값을 할당할 수 있음!
      var names = newValue.split(' ')
      this.firstName = names[0]
      this.lastName = names[names.length - 1]
    }
  }
}

위에서 말했다싶이, setter를 사용하는 경우는 Computed 속성에 새 값을 할당하려고 할 때이다.
이렇게 작성하면 vm.fullName = 'John Doe'를 실행하면 설정자가 호출되고 vm.firstName과 vm.lastName이 그에 따라 업데이트 된다.

Watch와 Computed 비교 결과

Watch => 인스턴스의 데이터 변경을 관찰하고 이에 반응함
Computed => 종속하는 대상이 변경될 때 실행되어 재 계산된 값을 반환함

vue에서는 명령적인 watch 콜백보다 computed 속성을 사용하는것이 더 좋다고 한다.
watch는 감시할 데이터를 지정하고 그 데이터가 바뀌면 이런 함수를 실행하라는 방식으로 "명령형 프로그래밍" 방식,
computed는 계산해야 하는 목표 데이터를 정의하는 방식으로 "선언형 프로그래밍" 방식이기 때문!

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

위의 방식보다 코드 반복이 적다.

profile
개발과 지식의 성장을 즐기는 개발자

0개의 댓글