Watch 와 WatchEffect에 관해서
Vue에서 watch와 watchEffect는 반응형 데이터 변화를 감지하고 처리하는 두 가지 중요한 기능입니다.
watch
- 특정 반응형 데이터(ref, reactive, computed 등)의 변화를 감시합니다.
- 명시적으로 감시할 대상을 지정해야 합니다.
- 이전 값과 현재 값에 모두 접근할 수 있습니다.
- 옵션을 통해 동작을 세밀하게 제어할 수 있습니다(immediate, deep 등).
1
2
3
4
5
6
7
import { ref, watch } from 'vue'
const count = ref(0)
watch(count, (newValue, oldValue) => {
console.log(`카운트가 ${oldValue}에서 ${newValue}로 변경되었습니다.`)
}, { immediate: true, deep: true })
watchEffect
- Vue 3에서 새롭게 도입된 기능입니다.
- 함수 내부에서 사용된 모든 반응형 데이터를 자동으로 추적합니다.
- 명시적으로 감시할 속성을 지정할 필요가 없습니다.
- 이전 값에 접근할 수 없고, 현재 값만 사용할 수 있습니다.
- 컴포넌트 마운트 시 즉시 실행됩니다.
1
2
3
4
5
6
7
8
import { ref, watchEffect } from 'vue'
const count = ref(0)
const name = ref('홍길동')
watchEffect(() => {
console.log(`카운트: ${count.value}, 이름: ${name.value}`)
})
Vue 3에서도 계속 사용되는 이유
- 각각의 유스케이스 존재:
- watch: 특정 데이터 변화에만 반응하고 이전 값과 비교가 필요할 때
- watchEffect: 여러 반응형 데이터를 한번에 추적하고 간결하게 사용하고 싶을 때
- 세밀한 제어 필요성:
- watch는 deep 옵션, immediate 옵션 등을 통해 더 세밀한 제어가 가능합니다.
- 복잡한 객체의 중첩된 속성 변화를 감지하는 경우 watch + deep 옵션이 유용합니다.
- 이전 값 접근 필요성:
- 데이터 변화의 이전 값과 현재 값을 비교하는 로직이 필요한 경우 watch가 필수적입니다.
- 호환성과 마이그레이션 용이성:
- Vue 2에서 Vue 3로 마이그레이션하는 프로젝트들을 위해 익숙한 API를 유지했습니다.
- 발동 타이밍 제어:
- watch는 flush 옵션을 통해 DOM 업데이트 전후에 콜백이 실행되는 시점을 제어할 수 있습니다.
Vue 3에서는 Composition API와 함께 이러한 기능들이 더욱 유연하게 사용될 수 있어, 두 함수 모두 각자의 장점과 사용 시나리오에 맞게 계속 활용되고 있습니다.
네, 정확한 지적입니다. Vue에서 ref, reactive, computed 등의 반응형 API는 기본적으로 값의 변화를 감지하고 이에 따라 UI를 자동으로 업데이트하기 위한 것입니다. Swift의 @State와 개념적으로 유사합니다.
Vue의 반응형 시스템은 크게 두 가지 측면으로 작동합니다:
- 반응형 데이터 정의 (ref, reactive, computed):
- 데이터의 변화를 Vue가 추적할 수 있게 만듭니다.
- 이 데이터가 변경되면 해당 데이터를 사용하는 템플릿이나 계산된 속성이 자동으로 업데이트됩니다.
- Swift의 @State처럼 상태 변경 시 UI 업데이트를 자동화합니다.
- 반응형 효과 정의 (watch, watchEffect):
- 데이터 변화에 대응하여 추가적인 사이드 이펙트를 실행하기 위한 도구입니다.
- 데이터 변화 시 API 호출, 로컬 스토리지 업데이트 등의 작업을 수행합니다.
예를 들어:
1
2
3
4
5
6
7
8
9
10
11
12
// 반응형 데이터 정의
const count = ref(0)
// 템플릿에서 자동으로 업데이트됨
// <div>9</div>
// 버튼 클릭 시 count 증가
function increment() {
count.value++
// 여기서 별도의 DOM 업데이트 코드가 필요 없음
// Vue가 자동으로 count를 사용하는 모든 부분을 업데이트
}
따라서:
- ref, reactive, computed는 기본 반응형 데이터를 정의합니다 (Swift의 @State와 유사)
- watch와 watchEffect는 반응형 데이터 변화에 따른 추가적인 로직을 실행하기 위한 도구입니다 (Swift의 .onChange 수정자와 유사)
반응형 데이터만으로도 UI는 자동 업데이트되지만, 데이터 변화에 따른 복잡한 로직, 비동기 작업, 다른 상태 업데이트 등이 필요할 때 watch와 watchEffect가 필요해집니다.
Vue의 반응형 시스템 설명하기
Vue의 반응형 시스템을 이해하기 위한 실용적인 예시를 들어보겠습니다.
예시 1: 쇼핑 카트 애플리케이션
반응형 데이터 (ref, reactive, computed)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { ref, computed } from 'vue'
// 반응형 데이터 정의
const cartItems = ref([
{ id: 1, name: '노트북', price: 1200000, quantity: 1 },
{ id: 2, name: '헤드폰', price: 300000, quantity: 1 }
])
// 계산된 속성
const totalPrice = computed(() => {
return cartItems.value.reduce((sum, item) => sum + (item.price * item.quantity), 0)
})
// 아이템 수량 변경 함수
function updateQuantity(itemId, newQuantity) {
const item = cartItems.value.find(item => item.id === itemId)
if (item) {
item.quantity = newQuantity
// 👉 여기서 특별한 업데이트 코드가 없어도 UI는 자동으로 업데이트됨!
}
}
템플릿:
1
2
3
4
5
6
7
<div>
<div v-for="item in cartItems" :key="item.id">
- 원 x
<input type="number" v-model="item.quantity" min="1">
</div>
<div>총 금액: 원</div>
</div>
여기서 cartItems를 변경하면 Vue가 자동으로 UI를 업데이트합니다. 수량을 변경하면 totalPrice도 자동으로 재계산됩니다. 이것이 바로 Swift의 @State와 유사한 개념입니다.
예시 2: 검색 기능 구현
이제 watch와 watchEffect가 필요한 상황을 봅시다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import { ref, watch } from 'vue'
const searchQuery = ref('')
const searchResults = ref([])
const isLoading = ref(false)
// 검색어 변경 시 API 호출하기
watch(searchQuery, async (newQuery, oldQuery) => {
if (newQuery.trim() === '') {
searchResults.value = []
return
}
// 타이핑 중간에 매번 API를 호출하는 것 방지
if (newQuery.length < 3) return
try {
isLoading.value = true
// API 호출
const response = await fetch(`/api/search?q=${newQuery}`)
searchResults.value = await response.json()
} catch (error) {
console.error('검색 오류:', error)
} finally {
isLoading.value = false
}
}, { debounce: 300 }) // 300ms 디바운스
여기서 watch는:
- 사용자가 입력을 멈춘 후 300ms 기다림
- 검색어가 3글자 이상인지 확인
- API를 호출하여 결과를 가져옴
UI는 searchResults와 isLoading이 변경될 때 자동으로 업데이트됩니다.
예시 3: 양식 저장 (watchEffect 사용)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { ref, reactive, watchEffect } from 'vue'
// 양식 데이터
const form = reactive({
name: '',
email: '',
message: ''
})
// 로컬 스토리지에 자동 저장
watchEffect(() => {
// form 객체의 어떤 속성이 변경되든 이 함수가 실행됨
localStorage.setItem('contactForm', JSON.stringify(form))
console.log('양식이 자동 저장되었습니다.')
})
// 초기화 함수
function loadSavedForm() {
const saved = localStorage.getItem('contactForm')
if (saved) {
const data = JSON.parse(saved)
Object.assign(form, data)
}
}
watchEffect는 form 객체의 어떤 속성이 변경되든 자동으로 감지하고 로컬 스토리지에 저장합니다.
정리
반응형 데이터 (ref, reactive, computed)
- UI와 연결된 데이터를 관리
- Swift의
@State처럼 데이터가 변경되면 UI가 자동으로 업데이트됨 - 데이터 <-> UI 간의 자동 동기화
watch와 watchEffect
- 데이터 변경에 따른 추가 작업을 처리
- API 호출, 로컬 스토리지 저장, 복잡한 계산 등 수행
- Swift의
.onChange수정자와 유사한 역할
이렇게 보면 반응형 데이터는 “상태 관리”를, watch와 watchEffect는 “상태 변화에 따른 사이드 이펙트 관리”를 담당한다고 이해할 수 있습니다.
이해했습니다. 더 일관된 예시로 개념을 설명해보겠습니다.
Vue의 반응형 시스템 이해하기
온라인 쇼핑몰 앱을 만든다고 가정하고, 하나의 예시를 통해 Vue의 반응형 시스템을 단계별로 설명하겠습니다.
1단계: 기본 반응형 데이터 (ref, reactive)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { ref, reactive } from 'vue'
// 장바구니 아이템 (반응형 배열)
const cartItems = ref([
{ id: 1, name: '스마트폰', price: 1000000, quantity: 1 },
{ id: 2, name: '블루투스 이어폰', price: 200000, quantity: 1 }
])
// 사용자 정보 (반응형 객체)
const user = reactive({
name: '홍길동',
address: '서울시 강남구',
isPremium: false
})
이 상태에서 사용자가 UI를 통해 cartItems나 user 정보를 변경하면 Vue는 자동으로 이를 감지하고 화면을 업데이트합니다.
1
2
3
4
5
6
7
// 수량 증가 함수
function increaseQuantity(itemId) {
const item = cartItems.value.find(item => item.id === itemId)
if (item) {
item.quantity++ // 👈 이 변경만으로 UI가 자동 업데이트됨
}
}
템플릿:
1
2
3
4
5
6
7
8
9
<div>
<h2>님의 장바구니</h2>
<div v-for="item in cartItems" :key="item.id">
- 원
<button @click="increaseQuantity(item.id)">+</button>
<button @click="item.quantity > 1 ? item.quantity-- : null">-</button>
</div>
</div>
2단계: computed로 파생 데이터 생성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { ref, reactive, computed } from 'vue'
// 이전 코드 유지...
// 계산된 반응형 데이터
const totalPrice = computed(() => {
return cartItems.value.reduce((sum, item) => sum + (item.price * item.quantity), 0)
})
const shipping = computed(() => {
// 프리미엄 회원이거나 5만원 이상 구매시 무료 배송
return (user.isPremium || totalPrice.value >= 50000) ? 0 : 3000
})
const finalPrice = computed(() => {
return totalPrice.value + shipping.value
})
computed는 다른 반응형 데이터를 기반으로 새로운 반응형 값을 생성합니다. 기본 데이터가 변경될 때마다 자동으로 재계산됩니다.
3단계: watch로 변화 감지 및 처리
이제 watch를 사용하여 특정 데이터 변화에 반응하는 사이드 이펙트를 추가해봅시다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { ref, reactive, computed, watch } from 'vue'
// 이전 코드 유지...
// 장바구니 변경 시 로컬 스토리지에 저장
watch(cartItems, (newItems) => {
localStorage.setItem('cart', JSON.stringify(newItems))
console.log('장바구니가 업데이트되어 저장되었습니다.')
}, { deep: true }) // 중첩 객체의 변화까지 감지
// 총 가격이 10만원을 넘으면 할인 쿠폰 알림
watch(totalPrice, (newTotal, oldTotal) => {
if (oldTotal < 100000 && newTotal >= 100000) {
alert('10만원 이상 구매하셨네요! 1만원 할인 쿠폰이 발급되었습니다.')
coupons.value.push({ id: Date.now(), amount: 10000, name: '10만원 이상 구매 특별 쿠폰' })
}
})
여기서 watch는 특정 데이터(cartItems, totalPrice)의 변화를 감시하고, 변화가 발생할 때 추가 작업을 수행합니다.
4단계: watchEffect로 여러 반응형 데이터 동시 감시
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import { ref, reactive, computed, watch, watchEffect } from 'vue'
// 이전 코드 유지...
const recentlyViewed = ref([])
const recommendations = ref([])
// 여러 데이터를 함께 감시하여 추천 상품 업데이트
watchEffect(async () => {
// 사용자 정보, 장바구니, 최근 본 상품이 변경될 때마다 실행
if (user.name && cartItems.value.length > 0) {
try {
const response = await fetch('/api/recommendations', {
method: 'POST',
body: JSON.stringify({
userId: user.id,
cartItems: cartItems.value,
recentlyViewed: recentlyViewed.value,
isPremium: user.isPremium
})
})
recommendations.value = await response.json()
} catch (error) {
console.error('추천 상품 가져오기 실패:', error)
}
}
})
watchEffect는 함수 내에서 사용된 모든 반응형 데이터(user, cartItems, recentlyViewed)를 자동으로 감시하고, 어느 하나라도 변경되면 함수를 다시 실행합니다.
정리: 각 반응형 API의 역할
- ref/reactive: 기본 반응형 데이터 생성
- 장바구니 아이템, 사용자 정보와 같은 기본 상태 관리
- Swift의
@State와 유사
- computed: 기존 반응형 데이터로부터 파생된 값 계산
- 총 가격, 배송비, 최종 가격 계산
- 원본 데이터가 변경될 때마다 자동 재계산
- watch: 특정 데이터 변화에 반응하는 사이드 이펙트 실행
- 장바구니 변경 시 로컬 스토리지에 저장
- 총 가격 변화에 따른 특별 이벤트 처리
- Swift의
.onChange수정자와 유사
- watchEffect: 여러 반응형 데이터를 동시에 감시하고 변화 시 처리
- 사용자 정보, 장바구니, 최근 본 상품 등이 변경될 때 추천 상품 업데이트
- 함수 내에서 사용된 모든 반응형 데이터를 자동으로 추적
이렇게 함께 연결된 하나의 예시를 통해 Vue의 반응형 시스템이 어떻게 작동하는지 더 명확하게 이해할 수 있습니다.