티스토리 뷰

⚠️ watch의 위험성

vue를 처음 시작했을 떄는 watch의 편리성 때문에, watch를 빈번하게 사용했었다.

그러나 프로젝트가 규모가 커져감에 따라 watch가 많으면 다음과 같은 문제가 발생하였다.

⚠️ 코드 가독성/유지보수성 저하

문제점 설명

로직이 분산됨 상태 변화에 따라 여러 watch가 동작하면, 전체 동작 흐름을 파악하기 어려움
사이드이펙트 위치 불명확 로직이 watch 내부에 숨어 있어서 디버깅/추적이 어려움
중복된 감시 로직 여러 상태가 유사한 방식으로 감시될 경우, 중복 로직이 반복됨

아래의 Bad Case를 통해서 문제점을 파악해보자

❌ watch간 상호 의존성

const category = ref('general')         // 기본 카테고리
const subCategory = ref(null)           // 하위 카테고리

// category 변경 시 동작
watch(category, async (newVal) => {
    // ❗특정 조건에서 subCategory 값 변경
  if (newVal !== 'custom') {
    subCategory.value = null
  } else {
    ...
  }
})

// subCategory 변경 시 조건부 감시
watch(subCategory, async (newVal) => {
    // ❗특정 조건에서 category 값 변경
  if (subCategory.value === 'custom2') {
    category.value = null
  }
})

위의 예제에서 특정 조건에 따라 서로의 watch 대상 데이터를 바꾸고 있는데, 이러한 상황은 프로세스 추적을 힘들게 만든다. watch가 꼬리에 꼬리를 무는 이야기(꼬꼬무…😭)로 변질되는 순간 디버깅 할 때 스트레스가 폭발하게 된다 😩.

위에 예제외에도 watch가 예상치 못하게 반복 호출 되어 성능이 저하된다던지와 같은 여러 예상치 못한 side effect가 존재할 수 있기 때문에 남용은 금물이다.

☀️ watch 대체 방안

✅ 1. 이벤트 기반 처리

watch 대신 요소 이벤트 핸들러 대체하는 방법이 있다.

아래는 아이디 기억하기 기능에 관한 예제이다.

Bad

import { ref, watch } from 'vue'

const username = ref('')
const rememberMe = ref(false)

// 👎 비효율: username 변경 때마다 저장 처리 → 불필요한 낭비 발생
watch([username, rememberMe], ([name, remember]) => {
  if (remember) {
    localStorage.setItem('saved-username', name)
  }
})

Good

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

const username = ref('')
const rememberMe = ref(false)

onMounted(() => {
  const saved = localStorage.getItem('saved-username')
  if (saved) {
    username.value = saved
    rememberMe.value = true
  }
})

// 👍로그인할때만 관련 처리를 진행.
function login() {
  if (rememberMe.value) {
    localStorage.setItem('saved-username', username.value)
  } else {
    localStorage.removeItem('saved-username')
  }
  alert(`로그인 시도: ${username.value}`)
}
</script>
<template>
    <input v-model="username" placeholder="아이디" />
    <label><input type="checkbox" v-model="rememberMe" /> 아이디 기억하기</label>
    <button @click="login">로그인</button>
</template>

✅ 2. Computed로 대체

Bad (watch 사용)

watch(() => user.age, (newAge) => {
  isAdult.value = newAge >= 20
})

Good (computed 사용)

 const isAdult = computed(() => user.age >= 20)

위와 같이 종속 데이터를 다루는게 주목적이라면 computed 를 사용하는 것이 훨씬 낫다.

🎯 결론

watch는 분명 유용한 기능이긴 하지만 무분별하게 남용되었을 때는 유지보수성을 저하시키는 요인이다.

watch를 적절하게 사용하여 side effect를 최소화하고 유지보수성을 높이도록 하자. 👏

댓글