레슨 2 / 9·20분
반응성과 computed
Vue의 반응성 시스템
Vue의 핵심은 반응성(Reactivity) 시스템입니다. 데이터가 변경되면 이를 사용하는 모든 곳(템플릿, computed, watcher 등)이 자동으로 업데이트됩니다. Composition API에서는 ref()와 reactive()를 사용하여 반응형 데이터를 선언합니다.
ref()와 reactive()
ref()는 원시 값(문자열, 숫자 등)을 반응형으로 만들며, .value로 접근합니다. reactive()는 객체 자체를 반응형으로 만들어 .value 없이 직접 속성에 접근합니다. 템플릿에서는 ref도 자동 언래핑되어 .value 없이 사용할 수 있습니다.
vue
<script setup lang="ts">
import { ref, reactive } from 'vue';
// ref: 원시 값 감싸기 (.value로 접근)
const count = ref(0);
const name = ref('Vue 학습자');
// reactive: 객체 자체를 반응형으로
const user = reactive({
email: 'user@example.com',
age: 25,
settings: {
theme: 'dark',
language: 'ko',
},
});
function increment() {
count.value++; // ref는 .value 필요
}
function updateEmail(newEmail: string) {
user.email = newEmail; // reactive는 직접 접근
}
</script>
<template>
<div>
<!-- 템플릿에서는 ref도 .value 불필요 -->
<p>{{ name }}님의 카운트: {{ count }}</p>
<p>이메일: {{ user.email }}</p>
<button @click="increment">증가</button>
</div>
</template>computed()
computed()는 다른 반응형 데이터로부터 파생되는 값을 선언합니다. 의존하는 데이터가 변경될 때만 다시 계산되며, 결과가 캐싱됩니다. 동일한 로직을 메서드로 작성할 수도 있지만, computed는 의존성이 바뀌지 않으면 이전 결과를 재사용하므로 더 효율적입니다.
vue
<script setup lang="ts">
import { ref, computed } from 'vue';
const items = ref([
{ name: '사과', price: 3000, quantity: 2 },
{ name: '바나나', price: 1500, quantity: 5 },
{ name: '딸기', price: 5000, quantity: 1 },
]);
// computed: 의존 데이터가 바뀔 때만 재계산
const totalPrice = computed(() => {
return items.value.reduce(
(sum, item) => sum + item.price * item.quantity,
0
);
});
const formattedTotal = computed(() => {
return totalPrice.value.toLocaleString('ko-KR') + '원';
});
const expensiveItems = computed(() => {
return items.value.filter((item) => item.price >= 3000);
});
</script>
<template>
<ul>
<li v-for="item in items" :key="item.name">
{{ item.name }}: {{ item.price }}원 x {{ item.quantity }}개
</li>
</ul>
<p>합계: {{ formattedTotal }}</p>
<p>3000원 이상 품목: {{ expensiveItems.length }}개</p>
</template>watch()와 watchEffect()
watch()는 특정 반응형 데이터의 변경을 감시하여 부수 효과(side effect)를 실행합니다. API 호출이나 로컬 스토리지 저장 같은 작업에 적합합니다. watchEffect()는 콜백 내부에서 사용하는 모든 반응형 데이터를 자동으로 추적합니다.
vue
<script setup lang="ts">
import { ref, watch, watchEffect } from 'vue';
const searchQuery = ref('');
const selectedCategory = ref('all');
// watch: 특정 대상을 명시적으로 감시
watch(searchQuery, (newValue, oldValue) => {
console.log("검색어 변경: " + oldValue + " → " + newValue);
// 예: 디바운싱 후 API 호출
});
// 여러 소스를 동시에 감시
watch([searchQuery, selectedCategory], ([newQuery, newCat]) => {
console.log("필터 변경: " + newQuery + ", " + newCat);
});
// watchEffect: 내부에서 사용하는 반응형 데이터를 자동 추적
watchEffect(() => {
// searchQuery와 selectedCategory가 자동으로 추적됨
console.log("현재 필터: " + searchQuery.value + ", " + selectedCategory.value);
});
</script>💡
단순히 데이터를 변환하여 표시하는 경우에는 computed를 사용하세요. API 호출, 타이머 설정, DOM 직접 조작 등 부수 효과가 필요한 경우에만 watch 또는 watchEffect를 사용합니다. 불필요한 watcher는 성능 저하의 원인이 될 수 있습니다.