[Vue.js] watch ์™€ watchEffect

jadeยท2025๋…„ 2์›” 13์ผ
0

๐Ÿ“Œwatch

๐ŸŒŸ source๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด ์ฝœ๋ฐฑํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•œ๋‹ค.

watch(source, (newValue, oldValue) => {
   ........
});

1๏ธโƒฃย ์ฒซ๋ฒˆ์งธ ์ธ์ž : ๊ฐ์‹œํ•˜๋ ค๋Š” ์†Œ์Šค

โ‘  ref- ref๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐ˜์‘ํ˜• ๋ฐ์ดํ„ฐ

โ‘ก reactive- reactive์˜ getterํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐ˜์‘ํ˜• ๊ฐ์ฒด์˜ ์†์„ฑ

โ‘ข ๋‹ค์ค‘ ์†Œ์Šค

2๏ธโƒฃย ๋‘๋ฒˆ์งธ ์ธ์ž : ์ฝœ๋ฐฑํ•จ์ˆ˜๋กœ ๊ฐ์‹œํ•˜๋ ค๋Š” source๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ์‹คํ–‰

โ‘  ์ฝœ๋ฐฑํ•จ์ˆ˜์˜ ์ฒซ๋ฒˆ์งธ ์ธ์ž: ๋ณ€๊ฒฝ ์ดํ›„์˜ ๊ฐ’

โ‘ก ์ฝœ๋ฐฑํ•จ์ˆ˜์˜ ๋‘๋ฒˆ์งธ ์ธ์ž: ๋ณ€๊ฒฝ ์ด์ „์˜ ๊ฐ’

โ‘ข ์ฝœ๋ฐฑํ•จ์ˆ˜์˜ ์„ธ๋ฒˆ์งธ ์ธ์ž(์„ ํƒ์‚ฌํ•ญ): onInvalidate,ย ํ•„์š”ํ•  ๋•Œ ์ž‘์—…์„ ์ •๋ฆฌํ•˜๊ฑฐ๋‚˜ ์ทจ์†Œํ•˜๋Š” ๋“ฑ ๋ถ€์ž‘์šฉ์„ ๊ด€๋ฆฌ

  • ref ๊ฐ์ฒด๋‚˜ ๋ฐฐ์—ด ์กฐํ•ฉ์‹œ์—๋Š” ๋ฐ˜๋“œ์‹œ { deep: true } ์˜ต์…˜์ด ํ•„์ˆ˜

1. ref

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

const counter = ref(0);
const smile = ref('๐Ÿ˜€');

watch(counter, (newCount, oldCount) => {
  console.log(`Counter: ${counter.value} (from ${oldCount} to ${newCount} ${smile.value})`);
});
</script>

<template>
  <div>
    <h1>Counter: {{ counter }}</h1>
    <button @click="counter++">Increment</button>
  </div>
</template>

ref๋Š” ๋ฐ˜์‘์„ฑ์„ ๊ฐ€์ง„ ์ƒํƒœ ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์„ฑํ•  ๋•Œ, ๋ฐ˜์‘ ๊ฐ์ฒด ๋ฐ์ดํ„ฐ๋ฅผ ๋ž˜ํ•‘ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์‹ค์ œ ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•˜๋ ค๋ฉด .value ์†์„ฑ์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ watch ํ•จ์ˆ˜์˜ ๋‘๋ฒˆ์งธ ์ธ์ž์ธ ์ฝœ๋ฐฑํ•จ์ˆ˜์˜ newValue, oldValue๋Š” ๊ฐ์ฒด๊ฐ€ ์•„๋‹Œ ref.value ์— ํ•ด๋‹นํ•˜๋Š” ๊ฐ’์ด๋‹ค. ๊ทธ๋ž˜์„œ ๊ฐ์‹œ๋Œ€์ƒ์ด Ref๋ฅผ ์ด์šฉํ•ด์„œ ๋งŒ๋“  ๋ฐ˜์‘์„ฑ์„ ๊ฐ€์ง„ ๋ฐ์ดํ„ฐ ๊ฐ์ฒด๋ผ๋„ newValue, oldValue๋Š” .value ์†์„ฑ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค.

2. reactive

const cart = reactive({
  item: 'Pencil',
  count: 0
});

โ“ย cart์˜ count๊ฐ’์ด ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ๊ฐ์‹œํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด watch์˜ ์ฒซ๋ฒˆ์งธ ์ธ์ž๋กœ ๋ฌด์—‡์„ ์‚ฌ์šฉํ•ด์•ผ ํ• ๊นŒ?

reactive๋กœ ๋งŒ๋“  ๋ฐ˜์‘ํ˜• ๊ฐ์ฒด์˜ ํŠน์ • ์†์„ฑ์˜ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ๊ฐ์‹œํ•˜๋ ค๋ฉด watch์˜ ์ฒซ๋ฒˆ์งธ์ธ์ž๋กœ getter ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด๋œ๋‹ค. getterํ•จ์ˆ˜๋ฅผ ์“ฐ๋ฉด (selector์ฒ˜๋Ÿผ) ์›ํ•˜๋Š” ์†์„ฑ์˜ ๋ณ€๊ฒฝ์‚ฌํ•ญ๋งŒ ๊ฐ์ง€ํ•ด์„œ ์ฝœ๋ฐฑํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

import { reactive, watch } from 'vue';

const cart = reactive({
  item: 'Pencil',
  count: 0
});

watch(**() => cart.count**, (newCount, oldCount) => {
  console.log(`Total Item: ${cart.count} (from ${oldCount} to ${newCount})`);
});

</script>

<template>
  <div>
    <p>Item: {{ cart.item }}</p>
    <p>Count: {{ cart.count }}</p>
    <button @click="cart.count++">Increase</button>
  </div>
</template>

watch๊ฐ€ cart.count ์˜ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ๊ฐ์ง€ํ•˜๊ฒŒ ๋˜๋ฉด, count ์˜ ๋ณ€๊ฒฝ์ „, ํ›„ ๊ฐ’์„ ์ฝœ๋ฐฑํ•จ์ˆ˜์— ์ „๋‹ฌํ•ด์ค€๋‹ค.

(count๋Š” reactive๊ฐ์ฒด์˜ ์†์„ฑ์ด๋ฏ€๋กœ value๊ฐ€ ์—†๋‹ค.

watch ๋‚ด๋ถ€์—์„œ cart.count, oldCount, newCount๋“ฑ ์—ฌ๋Ÿฌ๊ฐœ์˜ ๋ณ€์ˆ˜๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ value๋ผ๋Š” ์ด๋ฆ„๋ณด๋‹ค๋Š” old**Count ์ฒ˜๋Ÿผ ๋ช…ํ™•ํžˆ ๋„ค์ด๋ฐํ•˜๋Š”๊ฒŒ ์ค‘์š”ํ•˜๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ ๋‹ค!**

3. ๋‹ค์ค‘๊ฐ์‹œ์ž

์—ฌ๋Ÿฌ๊ฐœ์˜ ๊ฐ’์„ ๊ฐ์‹œํ•˜๋‹ค๊ฐ€ ํ•˜๋‚˜์˜ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ์‹ถ์„ ๋•Œ

watchํ•จ์ˆ˜์˜ ์ธ์ž๋กœ ์—ฌ๋Ÿฌ๊ฐœ์˜ ๊ฐ’์„ ์ค„๋•Œ๋Š” ๋ฐฐ์—ด๋กœ ๊ฐ์‹ผ๋‹ค.

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

const temperature = ref(25);
const humidity = ref(50);
const finedust = reactive({
  quality: 'Good'
});

watch(
  [temperature, humidity, () => finedust.quality],
  ([newTemperature, newHumidity, newFinedust], [oldTemperature, oldHumidity, oldFinedust]) => {
    console.log(`Temperature: ${temperature.value} (from ${oldTemperature}ยฐC to ${newTemperature}ยฐC)`);
    console.log(`Humidity: ${humidity.value} (from ${oldHumidity}% to ${newHumidity}%)`);
    console.log(`finedust: ${finedust.quality} (from ${oldFinedust} to ${newFinedust})`);
  }
);

function showYesterday(temp, humid, quality) {
  temperature.value += temp;
  humidity.value += humid;
  finedust.quality = quality;
}
</script>

<template>
  <div>
    <h1>Weather</h1>
    <p>Temperature: {{ temperature }}ยฐC</p>
    <p>Humidity: {{ humidity }}%</p>
    <p> Quality: {{ finedust.quality }}</p>
    <button @click="showYesterday(3, 5, 'Bad')">Show Yesterday</button>
  </div>
</template>

source

์˜จ๋„์™€ ์Šต๋„๋Š” ref๋กœ ๊ด€๋ฆฌํ•˜๊ณ  ๋ฏธ์„ธ๋จผ์ง€๋Š” reactive๋กœ ๊ด€๋ฆฌํ•˜๊ณ ์žˆ๋‹ค.

watchํ•จ์ˆ˜์˜ ์ฒซ๋ฒˆ์งธ ์ธ์ž์—๋Š” source๊ฐ’์„ ๋ฐฐ์—ด๋กœ ๋ฌถ์–ด์„œ ๋„ฃ์„์ˆ˜ ์žˆ๊ณ , reactive ๋ฐ˜์‘์„ฑ ๊ฐ์ฒด๋Š” getterํ•จ์ˆ˜๋กœ ์›ํ•˜๋Š” ๊ฐ’๋Š” ํŠน์ •ํ•˜์—ฌ ๋„ฃ๋Š”๋‹ค.

watch(
  [temperature, humidity, () => finedust.quality]

์ฝœ๋ฐฑํ•จ์ˆ˜

์ฝœ๋ฐฑํ•จ์ˆ˜์˜ ๊ฐ ์ธ์ž์—๋Š” ๊ฐ source๋“ค์˜ ๋ณ€๊ฒฝ์ „, ํ›„ ๊ฐ’๋“ค์„ ๋ฐฐ์—ด๋กœ ๋ฌถ์–ด์„œ ๋„ฃ๋Š”๋‹ค.

([newTemperature, newHumidity, newFinedust],
 [oldTemperature, oldHumidity, oldFinedust]) => { ... }

๐Ÿš€ย deep/ immediate option

Deep

๐Ÿ’ก ์ง€์ •ํ•œ ์†์„ฑ์˜ ์†์„ฑ๊ฐ’๋„ ๊ฐ์‹œํ•œ๋‹ค.

๋ฐ˜์‘ํ˜• ๊ฐ์ฒด๋ฅผ ์ง์ ‘ watchํ•˜๋ฉด ์•”์‹œ์ ์œผ๋กœ ๊นŠ์€ ๊ฐ์‹œ์ž๊ฐ€ ์ƒ์„ฑ๋œ๋‹ค.

์ฆ‰, ์†์„ฑ ๋ฟ ์•„๋‹ˆ๋ผ ์ค‘์ฒฉ๋œ ์†์„ฑ์—๋„ ํŠธ๋ฆฌ๊ฑฐ๋œ๋‹ค.

const person = reactive({
  name: 'ํ™๊ธธ๋™',
  age: 30,
  hobby: '์šด๋™',
  obj: {
    count: 0,
  },
});
  • getterํ•จ์ˆ˜๋กœ ๊ฐ์ฒดobj ์˜ ๊ฐ’์„ ๋„˜๊ธธ ๊ฒฝ์šฐ obj ์˜ ๊ฐ’์ด ๋ฐ”๋€”๊ฒฝ์šฐ์—๋งŒ ํŠธ๋ฆฌ๊ฑฐ๋œ๋‹ค.
    • ex) obj: "Hello!" ๋กœ obj๊ฐ€ ๋ณ€๊ฒฝ๋์„ ๊ฒฝ์šฐ์—๋งŒ ๋™์ž‘.
    • count๊ฐ€ ๋ฐ”๋€Œ์–ด๋„ watch์˜ ์ฝœ๋ฐฑํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค.
watch(
	() => person.obj,
	(newValue) => {
		// ๊ฐ์ฒด์˜ ๊ฐ’์ด ๋ฐ”๋€” ๊ฒฝ์šฐ์—๋งŒ ํŠธ๋ฆฌ๊ฑฐ ๋œ๋‹ค.
	}
);
  • deep ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด ๊นŠ์€ ๊ฐ์‹œ์ž๋กœ ๊ฐ•์ œํ•  ์ˆ˜ ์žˆ๋‹ค.
watch(
  () => person.obj,
  (newValue) => {
    console.log('newValue: ', newValue);
  },
  { deep: true } //๋งˆ์ง€๋ง‰ ๋งค๊ฐœ๋ณ€์ˆ˜
);

๐Ÿšจย deep ์˜ต์…˜์„ ํฐ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ์—์„œ ์‚ฌ์šฉ์‹œ ๋น„์šฉ์ด ๋งŽ์ด ๋“ค ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ, ํ•„์š”ํ•œ ๊ฒฝ์šฐ์—๋งŒ ์‚ฌ์šฉํ•™ ์„ฑ๋Šฅ ์˜ํ–ฅ์— ์ฃผ์˜ํ•˜์ž.


immediate

๐Ÿ’ก ์ตœ์ดˆ์— ์ฆ‰์‹œ ์‹คํ–‰๋˜๋„๋กํ•˜๋Š” ์˜ต์…˜

watch๋Š” ๋ฐ˜์‘ํ˜• ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ์‹œํ•˜๋‹ค๊ฐ€, ๋ฐ˜์‘ํ˜• ๋ฐ์ดํ„ฐ์˜ โ€˜๋ณ€ํ™”โ€™๊ฐ€ ์ผ์–ด๋‚ฌ์„ ๋•Œ ์ตœ์ดˆ๋กœ ์‹คํ–‰๋œ๋‹ค.

immediate ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด ์ตœ์ดˆ์— ํŽ˜์ด์ง€๋ฅผ ์ฝ์—ˆ์„ ๋•Œ ์ฆ‰์‹œ์‹คํ–‰๋˜๋„๋ก ํ•  ์ˆ˜ ์žˆ๋‹ค.

watch(
  () => person.obj,
  (newObj) => {
    console.log('newCount: ', newObj.count);
  },
  { immediate: true },
);

computed์™€ watch์˜ ๋น„๊ต ๋ฐ ์‚ฌ์šฉ์‚ฌ๋ก€

computed()๋Š” ๊ฒฐ๊ณผ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜์ง€๋งŒ, watch()๋Š”๋ฐ˜์‘ํ˜• ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€๊ฒฝํ–ˆ์„ ๋•Œ ํŠน์ • ๋™์ž‘์„ ์‹คํ–‰

computed โ†’ vue ์ธ์Šคํ„ด์Šค์˜ ์ƒํƒœ(ref, reactive ๋ณ€์ˆ˜)์— ์ข…์†๊ด€๊ณ„๋ฅผ ์„ธํŒ…ํ•  ๋•Œ.
ex) ย reverseMessage๋Š”ย messageย ๊ฐ’์— ๋”ฐ๋ผ ๊ฒฐ์ •๋˜์–ด์ง€๋Š” ์ข…์†๊ด€๊ณ„์— ์žˆ๋‹ค

watch โ†’ vue ์ธ์Šคํ„ด์Šค์˜ ์ƒํƒœ(ref, reactive ๋ณ€์ˆ˜)๋ณ€๊ฒฝ์‹œ์ ์— ํŠน์ • Action( api ํ˜ธ์ถœ, route push ๋“ฑ)์„ ์ทจํ•  ๋•Œ


๐Ÿ“ŒwatchEffect

1๏ธโƒฃย ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ƒ์„ฑ๋  ๋•Œ, 2๏ธโƒฃ์ฝœ๋ฐฑํ•จ์ˆ˜ ๋‚ด๋ถ€์˜ ๋ฐ˜์‘์„ฑ ๋ฐ์ดํ„ฐ์— ๋ณ€ํ™”๊ฐ€ ๊ฐ์ง€๋ ๋•Œ, ์ž๋™์œผ๋กœ ์‹คํ–‰ํ•œ๋‹ค.

const message = ref('');

watchEffect(()=> {
	console.log(message.value);
})

messageย ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ์ฝ˜์†”์ด ์ถœ๋ ฅ๋œ๋‹ค.

์ฝœ๋ฐฑ ํ•จ์ˆ˜ ์•ˆ์˜ ๋ฐ˜์‘ํ˜• ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด ์ž๋™์œผ๋กœ ๊ฐ์ง€ํ•˜์—ฌ ์‹คํ–‰

์‚ฌ์šฉ์˜ˆ์‹œ

โœ๏ธย ์ž๋™์ €์žฅ ๊ธฐ๋Šฅ์„ ๋งŒ๋“ค๋•Œ

save()ย ๋กœ ์ €์žฅํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด ๋†“๊ณ , ์ด์™€ ๊ฐ™์ด watchEffect๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

const message = ref('');

watchEffect(()=> {
	save(message.value);
})

์œ„์™€ ๊ฐ™์ด ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด, ๋‚ด์šฉ์ด ์ˆ˜์ •๋ ๋•Œ๋งˆ๋‹ค ์ž๋™์œผ๋กœ ์ €์žฅํ•จ์ˆ˜ย save()๊ฐ€ ์‹คํ–‰๋œ๋‹ค.

TIP!ย ๋ฐ”์ธ๋”ฉ๋œv-model์—ย .lazyย ์˜ต์…˜์„ ์ค€๋‹ค๋ฉด ํฌ์ปค์Šค๊ฐ€ ์ด๋™๋˜์—ˆ์„๋•Œ ์ €์žฅํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ๋„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค.

profile
keep on pushing

0๊ฐœ์˜ ๋Œ“๊ธ€

๊ด€๋ จ ์ฑ„์šฉ ์ •๋ณด