조아마시

쓸모 있는 상세페이지 만들기

웹개발/vuejs

[Vue] Vue3에서 watch와 watchEffect 비교 알아보기

joamashi 2024. 7. 22. 17:43

Vue3에서 reactive 데이터 변화를 감지하고 반응하는 데 사용되는 두 가지 주요 방법인 watch와 watchEffect에 대한 차이점을 살펴보겠습니다.

1. 기본 동작:

  • watch: 특정 reactive 값을 명시적으로 감시하고 해당 값이 변경될 때 콜백 함수를 실행합니다. 이전 값과 현재 값을 모두 콜백 함수에 제공합니다.
  • watchEffect: 콜백 함수 내부에서 사용되는 모든 reactive 의존성을 자동으로 추적합니다. 명시적인 값 지정 없이 변화에 반응합니다. 콜백 함수에는 현재 값만 제공됩니다.

2. 사용 시나리오:

  • watch:
    • 이전 값과 현재 값을 모두 사용하여 계산해야 하는 경우
    • 특정 reactive 값에만 반응해야 하는 경우
    • 코드 간 명확성을 위해 의도적으로 값을 지정하고 싶은 경우
  • watchEffect:
    • 이전 값에 접근할 필요가 없는 경우
    • 사용되는 reactive 의존성을 명확하게 파악하기 어려운 경우
    • 간결하고 명료한 코드를 선호하는 경우

3. 추가 비교:

이전 값 제공
명시적 값 지정
의존성 추적 수동 자동
성능 watchEffect보다 느릴 수 있음 watch보다 빠름
코드 간 명확성 상황에 따라 다름 간결하고 명료

watch와 watchEffect는 모두 Vue3에서 reactive 데이터 변화에 반응하는 데 유용한 도구입니다. 사용 상황에 따라 적절한 방법을 선택하는 것이 중요합니다.

  • 명확성이 중요하고 이전 값에 액세스해야 하는 경우 watch를 사용하는 것이 좋습니다.
  • 간결성과 성능이 중요하고 이전 값에 액세스할 필요가 없는 경우 watchEffect를 사용하는 것이 좋습니다.

1. 기본 예제

watch:

const count = ref(0);

watch(count, (newValue, oldValue) => {
  console.log(`count 변경: ${oldValue} -> ${newValue}`);
});

count.value++; // count 값 변경 시 콜백 함수 실행

watchEffect:

const count = ref(0);

watchEffect(() => {
  console.log(`count 값: ${count.value}`);
});

count.value++; // count 값 변경 시 콜백 함수 실행
  • 두 예제 모두 count 값이 변경될 때마다 콘솔 로그를 출력합니다.
  • watch는 이전 값과 현재 값을 모두 제공하는 반면, watchEffect는 현재 값만 제공합니다.

2. 객체 속성 감시

watch:

const obj = reactive({ name: "Vue3", age: 30 });

watch(obj, (newValue, oldValue) => {
  console.log(`객체 변경: ${JSON.stringify(oldValue)} -> ${JSON.stringify(newValue)}`);
});

obj.name = "Vue"; // obj.name 변경 시 콜백 함수 실행

watchEffect:

const obj = reactive({ name: "Vue3", age: 30 });

watchEffect(() => {
  console.log(`객체 속성: name: ${obj.name}, age: ${obj.age}`);
});

obj.name = "Vue"; // obj.name 변경 시 콜백 함수 실행
  • 두 예제 모두 obj 객체의 변경 내용을 콘솔 로그에 출력합니다.
  • watch는 객체 전체를 감시하고 변경된 모든 속성을 추적하는 반면, watchEffect는 콜백 함수 내에서 사용되는 속성만 추적합니다.

3. 계산된 값 사용

watch:

const age = ref(30);
const computedName = computed(() => `나의 이름은 ${user.name}이고, 나이는 ${age.value}살입니다.`);

watch(computedName, (newValue, oldValue) => {
  console.log(`계산된 값 변경: ${oldValue} -> ${newValue}`);
});

user.name = "Vue"; // user.name 변경 시 콜백 함수 실행
age.value++; // age 값 변경 시 콜백 함수 실행

watchEffect:

const age = ref(30);
const computedName = computed(() => `나의 이름은 ${user.name}이고, 나이는 ${age.value}살입니다.`);

watchEffect(() => {
  console.log(computedName.value); // 계산된 값 변화 시 콜백 함수 실행
});

user.name = "Vue"; // user.name 변경 시 콜백 함수 실행
age.value++; // age 값 변경 시 콜백 함수 실행
  • 두 예제 모두 computedName의 변화를 감지하고 콘솔 로그를 출력합니다.
  • watch는 computedName 자체를 감시하고 변경될 때마다 콜백 함수를 실행하는 반면, watchEffect는 computedName 내에서 사용되는 user.name과 age 값을 추적하고 변경될 때마다 콜백 함수를 실행합니다.

4. watchEffect 옵션

flush 옵션

watchEffect 함수의 두 번째 인자로 전달되는 옵션 객체의 속성 중 하나입니다. 이 옵션은 watchEffect가 실행되는 시점을 조절하는 역할을 합니다.

flush 옵션의 종류와 의미

  • flush: 'pre' (기본값): 컴포넌트 렌더링 직전에 실행됩니다. 즉, 컴포넌트가 업데이트되기 전에 watchEffect가 먼저 실행되어 새로운 값을 반영한 후에 컴포넌트가 렌더링됩니다.
  • flush: 'post': 컴포넌트 렌더링 직후에 실행됩니다. 컴포넌트가 한 번 렌더링된 후에 watchEffect가 실행되어 DOM이 업데이트된 상태에서 추가적인 작업을 수행할 수 있습니다.
  • flush: 'sync': 동기적으로 실행됩니다. 즉, watchEffect가 호출되는 즉시 실행됩니다. 이 옵션은 watchSyncEffect라는 별도의 함수로 제공되기도 합니다.

각 옵션의 사용 시나리오

  • flush: 'pre' (기본값):
    • 일반적인 상황에서 가장 많이 사용되는 옵션입니다.
    • 반응형 값이 변경될 때마다 즉시 계산 결과를 업데이트하고, 이를 바탕으로 컴포넌트를 다시 렌더링해야 할 때 유용합니다.
    • 예시: 계산된 값을 다른 곳에 전달하거나, 상태를 업데이트하는 경우
  • flush: 'post':
    • DOM이 완전히 업데이트된 후에 추가적인 작업을 수행해야 할 때 사용합니다.
    • 예시:
      • DOM 요소의 크기를 측정하여 스타일을 적용하는 경우
      • 외부 라이브러리(예: Chart.js)를 초기화하는 경우
      • 스크롤 위치를 조정하는 경우
    • nextTick과 비슷한 효과를 내지만, watchEffect와 결합하여 더욱 간결하게 코드를 작성할 수 있습니다.
  • flush: 'sync':
    • 동기적으로 실행되어야 하는 로직을 구현할 때 사용합니다.
    • 예시:
      • 특정 값이 변경될 때마다 즉시 다른 값을 업데이트해야 하는 경우
      • 폼 유효성 검사를 수행하는 경우
<script setup>
import { ref, watchEffect } from 'vue';

const count = ref(0);

watchEffect(() => {
  console.log('count:', count.value);
}, { flush: 'post' });
</script>

위 코드는 count 값이 변경될 때마다 console.log가 실행되도록 합니다. flush: 'post' 옵션을 사용했으므로, 컴포넌트가 한 번 렌더링된 후에 console.log가 실행됩니다.

deep 옵션

watchEffect에서 감시하는 대상이 객체나 배열일 때, 해당 객체나 배열 내부의 값이 변경되더라도 watchEffect가 실행되도록 하는 옵션입니다.

왜 deep 옵션이 필요한가요?

  • 객체나 배열 내부 값 변경 감지: 객체나 배열 자체가 아닌, 내부의 특정 프로퍼티 값이 변경될 때에도 반응하도록 하기 위해 deep 옵션을 사용합니다.
  • 깊은 레벨의 변경 감지: 중첩된 객체나 배열의 경우, 깊은 레벨까지 변경 사항을 감지해야 할 때 유용합니다.

deep 옵션 사용 예시

<script setup>
import { ref, watchEffect } from 'vue';

const person = ref({ name: '홍길동', age: 30 });

watchEffect(() => {
  console.log(person.value.name);
}, { deep: true });
</script>

위 코드에서 person 객체의 name 프로퍼티 값이 변경될 때마다 console.log가 실행됩니다. deep: true 옵션을 사용하지 않으면 person 객체 자체가 바뀌는 경우에만 watchEffect가 실행됩니다.

deep 옵션의 주의사항

  • 성능: deep 옵션을 사용하면 성능이 저하될 수 있습니다. 깊은 객체를 감시하는 것은 많은 연산을 필요로 하기 때문입니다.
  • 필요한 경우에만 사용: 꼭 필요한 경우에만 deep 옵션을 사용하고, 불필요한 감시는 피해야 합니다.
  • computed 프로퍼티와의 조합: deep 옵션 대신 computed 프로퍼티를 활용하여 원하는 값을 계산하고, 이를 감시하는 방법도 고려해볼 수 있습니다.

deep 옵션 vs. shallow 감시

  • shallow 감시: 기본적으로 watchEffect는 shallow 감시를 수행합니다. 즉, 객체나 배열의 참조가 변경될 때만 반응합니다.
  • deep 감시: deep 옵션을 사용하면 객체나 배열 내부의 값이 변경될 때도 반응합니다.
 

5. Once 감시자

watchEffect에서 감시하는 대상이 변경될 때 딱 한 번만 실행되도록 하는 옵션입니다. 즉, 처음 한 번만 실행되고, 이후에는 다시 실행되지 않습니다.

왜 once 옵션이 필요한가요?

  • 한 번만 실행해야 하는 로직: 특정 값이 변경될 때 처음 한 번만 특정 작업을 수행하고, 이후에는 더 이상 실행할 필요가 없는 경우에 사용합니다.
  • 성능 최적화: 불필요하게 반복적으로 실행되는 것을 방지하여 성능을 향상시킬 수 있습니다.

once 옵션 사용 예시

<script setup>
import { ref, watchEffect } from 'vue';

const count = ref(0);

watchEffect(() => {
  console.log('count가 변경되었습니다.');
}, { once: true });
</script>

위 코드에서 count 값이 변경될 때마다 console.log가 실행됩니다. 하지만 once: true 옵션을 사용했기 때문에, count 값이 처음 변경될 때 한 번만 실행되고, 이후에는 더 이상 실행되지 않습니다.

once 옵션의 주의사항

  • 한 번만 실행: once 옵션을 사용하면 watchEffect는 한 번만 실행되므로, 이후에 값이 변경되어도 다시 실행되지 않습니다.
  • 다른 옵션과의 조합: once 옵션은 deep 옵션과 함께 사용할 수 있습니다. 즉, 객체 내부 값이 처음 변경될 때 한 번만 실행되는 효과를 얻을 수 있습니다.

once 옵션 vs. 일반 watchEffect

  • 일반 watchEffect: 값이 변경될 때마다 계속해서 실행됩니다.
  • once 옵션: 값이 변경될 때 처음 한 번만 실행됩니다.
728x90