[Vue] watch 및 ref안의 객체일 때 분석

Dev_sheep·2024년 9월 29일
0

개발을 진행하면서 watch를 사용하는 분들이 계셨는데, 데이터를 어떻게 넣느냐에 따라 감지가 되는지 안되는지 확인하다보니 궁금증이 생겨서 찾아보고 그에 대한 글을 작성해보고자 한다

watch의 개념

  • 상태 변화를 감시하고, 데이터의 변화를 추적을 하여 특정 값이 변할 때 마다 API 호출 등 필요한 로직을 처리할 수 있다.
import { ref, watch } from 'vue';

const count = ref(0);

watch(count, (newValue, oldValue) => {
  console.log(`count changed from ${oldValue} to ${newValue}`);
});

count 변화를 감시하며 newValue, oldValue를 통해 변화를 추적한다

ref안의 원시 값과 객체의 비교

원시 타입을 ref로 감싼 경우

const count = ref(0);

watch(count, (newValue, oldValue) => {
  console.log(`count changed from ${oldValue} to ${newValue}`);
});

count.value = 1;  // 감지됨
  • 원시 타입인 경우 첫 번째 인자에 .value를 전달하지 않고 count만 넣어도 vue 내부에서 처리를 한다.
  • 메모리 측면에서는 참조 값이 변했기 때문에 이를 감지한다.

객체를 ref로 감싼 경우

const user = ref({ name: 'Alice', age: 25 });

watch(user, (newValue, oldValue) => {
  console.log('user object changed');
});

user.value.name = 'Bob';  // 감지되지 않음
user.value = { name: 'Bob', age: 25 };  // 감지됨
  • 객체는 참조 타입이기 때문에 watch 첫 번째 인자에 user를 하면 ref로 감싸진 참조가 변경되지는 않았기 때문에 감지를 하지 못한다.
  • Vue는 객체의 참조값을 감지하기 때문이다.
  • 위의 상태에서 watch내 옵션으로 deep 속성에 true를 주면되긴 하지만 건너뛰어가본다.
  • deep 자체도 성능 상으로는 비용이 많이 들기 때문에 주의점으로 표현되어 있다.

메모리 상에서의 모습

(1) 원시 타입

  • 원시 값은 불변하기 때문에 할당 시 메모리 상에서 새롭게 값이 할당된다.

(2) 객체 타입

  • 참조 타입이며 메모리 상에서 주소를 저장한다. 참조 자체를 변경하면 새로운 메모리 공간에 할당되고, 내부 속성 등이 바뀌면 메모리 주소가 바뀌지 않는다.

watch(user) vs watch(user.value)

  • 위의 예시에서는 user를 했기에 watch 첫 번째 인자에 그렇게 해본다

user.value는 객체 자체이다. 이를 Proxy로 감싼다.

근데 ref를 하면 안에 vue 내부에서 RefImpl이라는 객체를 활용한다고 알고 있다.

근데 왜 객체에서 reactive로 감싸서 Proxy객체로 바꿔지는걸까?

Vue가 내부 속성의 변경을 Proxy를 통해 자동으로 감싸서 변화를 감지해준다.

const user = ref({ name: 'Alice', age: 25 });

// 객체 내부 속성의 변화를 감지할 수 있음 (deep 옵션 없이도 가능)
watch(user.value, (newValue, oldValue) => {
  console.log('user object changed');
});

user.value.name = 'Bob';  // 감지됨

RefImpl와 감지되는 이유

  • Vue3는 ref를 생성할 때 RefImpl 클래스 인스턴스를 반환하여 .value 프로퍼티를 통해 값이나 객체에 접근할 수 있다.

  • 원시 값의 경우 .value를 통해 해당 값을 반응형으로 만든다

  • 객체의 경우 ref로 감싸도 reactive로 감싸서 Proxy로 변환을 해준다.

Proxy의 경우에는 get, set트랩이 있는데, 속성 접근 및 변경을 추적해주는 역할을 해준다.

그래서 위의 예시처럼 객체 내부 속성의 변화도 감지할 수 있게 되어 첫 번째 인자에 user.value를 넣어서 안의 모든 프로퍼티를 감지할 수 있는 것이다.

profile
기록과 공유

0개의 댓글