---
id: wiki-2026-0508-computed-properties-watchers
title: Computed Properties & Watchers
category: 10_Wiki/Topics
status: verified
canonical_id: self
aliases: [Vue computed, Vue watch, reactive-derivations]
duplicate_of: none
source_trust_level: A
confidence_score: 0.9
verification_status: applied
tags: [vue, reactivity, composition-api, computed, watch]
raw_sources: []
last_reinforced: 2026-05-10
github_commit: pending
tech_stack:
  language: typescript
  framework: vue3
---

# Computed Properties & Watchers

## 매 한 줄
> **"매 derived state 의 declarative cache (`computed`), 매 side-effect 의 explicit subscription (`watch`)"**. 매 Vue 3.5 (2024) reactive system 의 두 축. 매 computed 는 pull-based memoization, watch 는 push-based callback.

## 매 핵심

### 매 computed 의 본질
- 매 dependency 의 자동 추적 → 매 read 시점 lazy evaluation.
- 매 동일 input 의 cached return — Object.is 비교.
- 매 ref 처럼 `.value` 의 unwrap.
- 매 writable computed 의 setter 정의 가능.

### 매 watch / watchEffect 의 차이
- `watch(source, cb)`: 매 explicit source — 매 old/new value 의 access.
- `watchEffect(fn)`: 매 자동 dep tracking — 매 deps mutation 시 fn 의 re-run.
- 매 flush timing: `'pre'` (default, before render) / `'post'` (after DOM) / `'sync'` (즉시).

### 매 Vue 3.5 reactivity 개선
- 매 `computed` 의 lazy invalidation — 매 unused chain 의 skip.
- 매 SSR friendly — server 에서 watch 의 no-op.
- 매 onWatcherCleanup() 의 자동 cleanup hook.

## 💻 패턴

### Basic computed
```typescript
import { ref, computed } from 'vue';

const firstName = ref('Yuna');
const lastName = ref('Kim');
const fullName = computed(() => `${firstName.value} ${lastName.value}`);

console.log(fullName.value); // "Yuna Kim" — cached
firstName.value = 'Jihoon';
console.log(fullName.value); // 매 invalidate → recompute
```

### Writable computed
```typescript
const count = ref(0);
const double = computed({
  get: () => count.value * 2,
  set: (v) => { count.value = v / 2; },
});

double.value = 10; // count.value === 5
```

### watch (explicit source)
```typescript
import { ref, watch } from 'vue';

const userId = ref(1);
const user = ref<User | null>(null);

watch(userId, async (id, oldId, onCleanup) => {
  const ctrl = new AbortController();
  onCleanup(() => ctrl.abort()); // 매 stale request 의 cancel
  user.value = await fetchUser(id, { signal: ctrl.signal });
}, { immediate: true });
```

### watchEffect (auto-track)
```typescript
import { ref, watchEffect } from 'vue';

const query = ref('');
const results = ref<Item[]>([]);

watchEffect(async (onCleanup) => {
  const ctrl = new AbortController();
  onCleanup(() => ctrl.abort());
  results.value = await searchAPI(query.value, { signal: ctrl.signal });
});
```

### Deep watch (object/array)
```typescript
const filters = reactive({ q: '', tags: [] });

watch(filters, (next) => {
  console.log('filters changed', JSON.parse(JSON.stringify(next)));
}, { deep: true });
```

### Multiple sources
```typescript
const x = ref(0), y = ref(0);
watch([x, y], ([nx, ny], [ox, oy]) => {
  console.log(`(${ox},${oy}) → (${nx},${ny})`);
});
```

### Flush timing — DOM 접근
```typescript
const list = ref<string[]>([]);
const containerRef = ref<HTMLElement>();

watch(list, () => {
  // 매 DOM 의 update 후 scrollTop 의 read
  containerRef.value!.scrollTop = containerRef.value!.scrollHeight;
}, { flush: 'post' });
```

## 매 결정 기준

| 상황 | Approach |
|---|---|
| 매 derived value (template render) | `computed` |
| 매 expensive computation + cache | `computed` |
| 매 external side-effect (fetch, DOM) | `watch` / `watchEffect` |
| 매 conditional dep tracking | `watch` (explicit source) |
| 매 모든 reactive read 의 react | `watchEffect` |

**기본값**: 매 derive 는 `computed`, 매 effect 는 `watch` with explicit source.

## 🔗 Graph
- 부모: [[Composition API]] · [[Vue_Single-File_Components_SFC]]
- 변형: [[Composables]] · [[Options API]]
- 응용: [[Pinia]] · [[Server_State_Management]]
- Adjacent: [[State_Management]]

## 🤖 LLM 활용
**언제**: 매 Vue 3 component 의 derived data 정의, 매 async data fetch 의 dependency 변경 추적, 매 form-validation reactive chain.
**언제 X**: 매 React/Solid 의 코드 (각 framework 의 hook/signal 사용), 매 single-shot computation (그냥 함수).

## ❌ 안티패턴
- **매 computed side-effect**: 매 getter 안에서 mutation/fetch — 매 cache invalidation 의 chaos.
- **매 watch deep 의 남용**: 매 large object deep watch — 매 expensive equality check.
- **매 immediate + cleanup 누락**: 매 첫 fetch 의 race — 매 onCleanup 의 mandatory.
- **매 watchEffect 의 conditional dep**: 매 첫 run 에서 안 읽힌 ref 의 untracked.

## 🧪 검증 / 중복
- Verified (Vue 3.5 docs vuejs.org/api/reactivity-core, Evan You blog 2024).
- 신뢰도 A.

## 🕓 Changelog
| 날짜 | 변경 |
|---|---|
| 2026-05-08 | Phase 1 |
| 2026-05-10 | Manual cleanup — computed/watch 의 timing/cleanup 패턴 정리 |
