Vue 3 반응형 시스템: watch와 watchEffect를 사용할 시점 파악하기
James Reed
Infrastructure Engineer · Leapcell

소개
프론트엔드 개발의 활기찬 환경에서 Vue.js는 계속해서 강력한 힘을 발휘하고 있으며, 그 반응형 시스템은 매력의 초석입니다. 개발자로서 우리는 효율적이고 유지보수 가능하며 성능이 뛰어난 애플리케이션을 끊임없이 추구합니다. Vue 3에서 이를 달성하는 데 핵심적인 측면은 특히 데이터 변경에 반응하는 데 있어 반응성을 효과적으로 관리하는 것입니다. Vue 3은 이 목적을 위해 두 가지 강력한 도구를 제공합니다: watch와 watchEffect. 둘 다 반응형 상태 변경에 응답하여 부작용을 수행할 수 있지만, 그 미묘한 차이와 적절한 사용 사례는 때때로 혼란의 원인이 될 수 있습니다. 이들의 정확한 적용을 이해하는 것은 단순히 학문적인 연습이 아닙니다. 이는 더 깔끔하고, 더 최적화되며, 오류가 적은 Vue 애플리케이션을 작성하는 것으로 직접 이어집니다. 이 탐구는 이러한 구분을 명확히 하여 특정 반응형 요구 사항에 어떤 watcher를 배포할지에 대한 정보에 입각한 결정을 내릴 수 있도록 하는 것을 목표로 합니다.
Vue 3의 반응형 Watcher 이해하기
watch와 watchEffect의 구체적인 내용으로 들어가기 전에, Vue 3의 반응성 시스템의 근간을 이루는 핵심 개념을 간략히 살펴보겠습니다.
반응성(Reactivity): 본질적으로 Vue에서의 반응성이란 데이터가 변경될 때 해당 데이터에 의존하는 애플리케이션의 부분이 자동으로 업데이트된다는 것을 의미합니다. 이는 속성 접근 및 수정을 가로채는 프록시 기반 시스템을 통해 달성됩니다.
부작용(Side Effects): Watcher의 맥락에서 부작용은 반응형 종속성의 변경에 대한 직접적인 결과로 발생하는 모든 작업입니다. 이는 콘솔 로깅, API 호출, DOM 직접 업데이트, 다른 반응형 상태 수정 등 무엇이든 될 수 있습니다.
이제 두 가지 주요 플레이어를 살펴보겠습니다:
watch
watch 함수는 특정 반응형 데이터 소스의 변경에 응답하는 보다 명시적이고 구성 가능한 방법입니다. 무엇을 감시할지 명시적으로 지정해야 합니다.
원리: watch는 소스(또는 소스 배열)와 콜백 함수를 받습니다. 콜백 함수는 소스의 값이 변경될 때마다 실행됩니다. 또한 감시되는 소스의 새 값과 이전 값 모두에 액세스할 수 있습니다.
구현 예제:
import { ref, watch } from 'vue'; export default { setup() { const count = ref(0); const searchKeyword = ref(''); // 단일 ref 감시 watch(count, (newCount, oldCount) => { console.log(`Count changed from ${oldCount} to ${newCount}`); // 부작용 수행, 예: API 호출 // if (newCount > 5) { // console.log('Count is getting high!'); // } }); // 여러 소스 감시 (ref 배열) watch([count, searchKeyword], ([newCount, newKeyword], [oldCount, oldKeyword]) => { console.log(`Count changed from ${oldCount} to ${newCount}`); console.log(`Keyword changed from '${oldKeyword}' to '${newKeyword}'`); // 일부 파생 데이터 업데이트 또는 복잡한 로직 트리거 }); // 객체 깊은 감시 (명시적 활성화) const user = ref({ name: 'Alice', age: 30 }); watch(user, (newUser, oldUser) => { console.log('User object changed:', newUser); }, { deep: true }); // 즉시 실행 (컴포넌트 설정 시 콜백이 즉시 실행되고, 이후 변경 시 실행됨) watch(count, (newCount) => { console.log(`Initial count or count changed: ${newCount}`); }, { immediate: true }); const increment = () => { count.value++; }; const updateKeyword = (event) => { searchKeyword.value = event.target.value; }; const changeUserName = () => { user.value.name = 'Bob'; // 이것은 깊은 감시를 트리거합니다. }; return { count, searchKeyword, user, increment, updateKeyword, changeUserName }; } };
watch의 애플리케이션 시나리오:
- 이전 값과 새 값을 가진 특정 데이터 변경에 대한 반응: 이전 상태와 현재 상태를 비교해야 할 때 (예: 값이 증가하거나 감소했는지 확인 또는 로깅 목적).
- 불필요할 때만 비싼 작업 수행: 특정 데이터가 변경될 때만 트리거되어야 하는 복잡한 계산 또는 API 호출이 있는 경우,
watch를 사용하면 해당 종속성을 정확히 지정할 수 있습니다. - 여러 독립 소스 감시: 여러 반응형 속성을 동시에 관찰하고 그 중 하나라도 변경될 때 반응할 수 있으며, 이는 상호 종속적인 로직에 유용합니다.
- 중첩된 객체 깊은 감시:
{ deep: true }를 명시적으로 설정하면,watch는 객체 내 중첩된 속성의 변경을 감지할 수 있으며, 이는watchEffect에 대해 자동적이지 않습니다 (단,watchEffect내에서 내부 속성이 직접 접근되지 않는 한). - Watcher 실행 시점 제어 (예:
immediate옵션): 컴포넌트 마운트 시 부작용을 즉시 한 번 실행하고, 후속 변경 시에도 실행되도록 하고 싶을 때.
watchEffect
watchEffect 함수는 변경에 반응하는 더 간단하고 자동적인 방법입니다. 미리 종속성을 지정할 필요가 없습니다. 대신, 초기 실행 중에 액세스된 반응형 종속성을 자동으로 추적합니다.
원리: watchEffect는 제공된 함수를 즉시 실행하며, 반응성 시스템은 해당 실행 중에 액세스된 모든 반응형 속성을 자동으로 추적합니다. 추적된 종속성 중 하나라도 변경될 때마다 함수가 다시 실행됩니다.
구현 예제:
import { ref, watchEffect } from 'vue'; export default { setup() { const firstName = ref('John'); const lastName = ref('Doe'); const age = ref(30); // firstName과 lastName에 반응하는 WatchEffect watchEffect(() => { console.log(`Full Name: ${firstName.value} ${lastName.value}`); // 이 watchEffect는 firstName 또는 lastName이 변경될 때마다 다시 실행됩니다. // 'age'는 콜백 내에서 액세스되지 않으므로 추적하지 않습니다. }); // 정리(cleanup) 함수가 있는 WatchEffect let timer; watchEffect((onCleanup) => { console.log(`Current age: ${age.value}`); // 비동기 작업 시뮬레이션 timer = setTimeout(() => { console.log(`Age updated to ${age.value} after delay`); }, 1000); onCleanup(() => { // 이 함수는 효과가 다시 실행되기 전이나 컴포넌트가 마운트 해제될 때 실행됩니다. console.log('Cleaning up previous age effect...'); clearTimeout(timer); }); }); const updateName = () => { firstName.value = 'Jane'; lastName.value = 'Smith'; }; const increaseAge = () => { age.value++; }; return { firstName, lastName, age, updateName, increaseAge }; } };
watchEffect의 애플리케이션 시나리오:
- 자동 종속성 추적: 수동으로 나열하지 않고 콜백 내에서 액세스된 모든 반응형 종속성이 변경될 때마다 효과를 다시 실행하고 싶을 때. 이는 종종 더 간결한 코드로 이어집니다.
- 이전 값이 필요하지 않은 부작용 수행: 현재 상태 (예: 파생된 ref 업데이트, 현재 데이터로 API 호출, 로깅)만을 사용하여 작업을 수행해야 할 때.
- 데이터 동기화 간소화: 여러 내부 반응형 소스와 동기화 상태를 유지해야 하는 파생 상태 또는 외부 효과가 있을 때.
- 부작용 자체가 계산일 때: 종속성이 변경될 때 해제되어야 하는 구독 또는 리소스를 설정하는 경우 (
onCleanup콜백 사용).
주요 차이점 및 선택 시점
| 기능 | watch | watchEffect |
|---|---|---|
| 종속성 | 첫 번째 인수로 명시적으로 선언 (예: ref, computed, getter 함수, 소스 배열). | 콜백 함수 내에서 실행되는 동기 코드로부터 자동으로 추론됩니다. |
| 콜백 인수 | (newValue, oldValue, onCleanup) 또는 ([newVals], [oldVals], onCleanup) (여러 소스의 경우)를 받습니다. | (onCleanup)만 받습니다. newValue 또는 oldValue를 받지 않습니다. |
| 초기 실행 | 기본적으로 실행되지 않습니다. 컴포넌트 설정 시 실행하려면 { immediate: true } 옵션이 필요합니다. | 초기 종속성을 수집하기 위해 컴포넌트 설정 시 즉시 실행됩니다. |
| 깊은 감시 | 객체의 경우 { deep: true } 옵션이 필요합니다. | 속성 접근을 추적합니다. obj.prop이 액세스되면 obj.prop을 추적합니다. obj만 액세스되면 내부 변경 사항은 추적하지 않습니다. |
| 사용 사례 | 특정 종속성에 반응하고, 이전 값에 액세스하거나, 실행을 더 정확하게 제어해야 할 때. | 액세스된 모든 반응형 종속성이 변경될 때마다 효과를 자동으로 다시 실행하고 싶을 때, 간단한 동기화 로직의 경우. |
| 단순성 | 더 명시적이고 구성 가능합니다. | 간단한 부작용의 경우 더 간단하고 자동화됩니다. |
정확한 사용 사례는 다음으로 귀결됩니다:
watch를 선택하세요: 특정 데이터 소스의 이전 값을 알고 싶거나, 명시적으로 정의된 특정 변경 사항에만 반응하고 싶을 때.* 이는 세분화된 제어를 제공하며 값 변경에 기반한 조건부 로직에 필수적입니다.watchEffect를 선택하세요: 이 블록 내에서 액세스된 모든 반응형 상태가 변경될 때마다 코드 블록을 다시 실행하고, 정확한 이전 값은 관련이 없을 때. 이는 외부 상태 동기화 또는 여러 반응형 종속성의 현재 상태에 의존하는 부작용 수행에 이상적입니다.
결론
Vue 3의 watch와 watchEffect는 개발자의 반응성 관리 도구 상자에서 모두 필수적인 도구입니다. watch는 정확한 종속성을 정의하고 이전 값에 액세스할 수 있도록 하여 명시적인 제어를 제공하는 반면, watchEffect는 액세스된 모든 반응형 상태에 자동으로 반응하는 우아하고 종속성 없는 접근 방식을 제공합니다. 이들의 고유한 메커니즘과 사용 사례를 이해함으로써 더 효율적이고 유지보수 가능하며 선언적인 Vue 애플리케이션을 작성할 수 있습니다. 궁극적으로 watch는 제어를 위해 특정 추적된 변경에 명시적으로 반응하는 것이고, watchEffect는 단순성을 위해 모든 액세스된 종속성에 암묵적으로 반응하는 것입니다.

