Vue의 Reactivity와 약간의 React
Vue의 반응성 시스템 이해하기
Vue의 핵심은 바로반응성(Reactivity)입니다. Vue는 상태(state)의 변화를 감지하고, 이에 따라 DOM을 자동으로 갱신합니다. Vue 2와 Vue 3는 각각 다른 방식으로 반응성을 구현하며, 그 내부 작동 방식은 프레임워크의 성능과 개발 편의성에 큰 영향을 미칩니다.
Vue 2: Object.defineProperty
기반 반응성
Vue 2는 ES5의
Object.defineProperty
를 활용해 객체의 속성에 대해 getter/setter를 정의하고, 이를 통해 속성의 변경을 감지합니다.기본 개념
const data = { price: 100 }; Object.defineProperty(data, 'price', { get() { console.log('Getting price'); return this._price; }, set(newVal) { console.log('Setting price to', newVal); this._price = newVal; } });
이처럼 Vue는 속성에 접근(get)하거나 값을 변경(set)할 때 자동으로 트래킹하여 UI를 갱신합니다.
제한점
- 신규 속성 추가 감지 불가:
data.newProp = 'test'
처럼 나중에 추가된 속성은 반응형이 아닙니다.
- 배열 인덱스 감지 한계: 배열의 인덱스를 직접 변경하거나
length
를 수정할 경우 Vue가 변경을 감지하지 못할 수 있습니다.
- 중첩 객체의 반응성 구현이 복잡: 모든 하위 속성에 일일이
defineProperty
를 설정해야 합니다.
Vue 3: Proxy
기반 반응성
Vue 3는 ES6의
Proxy
API를 이용하여 전체 객체를 감싸는 방식으로 반응성을 구현합니다. 이로 인해 Vue 2에서의 여러 한계를 극복하게 되었습니다.기본 예시
const target = { message: 'hello' }; const handler = { get(target, prop, receiver) { console.log(`Getting ${prop}`); return Reflect.get(target, prop, receiver); }, set(target, prop, value, receiver) { console.log(`Setting ${prop} to ${value}`); return Reflect.set(target, prop, value, receiver); } }; const proxy = new Proxy(target, handler); console.log(proxy.message); // Getting message proxy.message = 'world'; // Setting message to world
Vue 3 내부에서는 위와 유사한 방식으로 모든 reactive 객체를 감싸고, 속성 접근 및 변경을 추적합니다.
Proxy의 장점
- 중첩 객체 자동 반응성: 깊은 중첩 구조도 자동으로 감지.
- 배열 지원 향상: 배열 메서드를 재정의하지 않고도 반응성 제공.
- 신규 속성 감지: 런타임에서 추가된 속성도 자동으로 반응형 처리.
- 성능 개선: 초기 렌더링과 메모리 효율성에서 이점이 있음.
- 더 간결한 내부 구현: 프레임워크 유지보수 및 확장성 향상.
❗ 참고: Proxy는 IE11에서는 지원되지 않으므로, Vue 3는 IE11과 호환되지 않습니다.
반응성과 기본형(Primitive Values)의 한계
JavaScript의 문자열, 숫자, 불린 값 등 기본형 값은 불변이며, 참조가 아닌 값으로 전달됩니다. Vue는 이러한 기본값을 감지할 수 없습니다.
❌ 반응성 비지원 예시
let count = 1; count = 2; // Vue는 이 변경을 추적할 수 없습니다.
✅ 반응성 유지 예시 (객체로 감싸기)
const state = reactive({ count: 1 }); state.count = 2; // 이 변경은 Vue가 감지합니다.
provide
와 inject
에서의 반응성 주의사항
Vue의
provide/inject
메커니즘을 사용할 때, 기본형 값을 주입하면 해당 값은 반응형이 아닙니다. 하지만 객체를 주입하면 객체의 속성은 여전히 반응성을 유지합니다.❌ 기본형 값 주입 시
// 부모 provide('count', 1); // 자식 const count = inject('count'); // 반응성 없음
객체 주입으로 반응성 유지
// 부모 const state = reactive({ count: 1 }); provide('state', state); // 자식 const state = inject('state'); watchEffect(() => { console.log(state.count); // 반응형 추적 가능 });
React vs Vue 반응성 비교
항목 | Vue 3 | React |
반응성 도입 방식 | Proxy 기반 | 명시적 상태 관리 (useState 등) |
상태 변경 감지 | 자동 (getter/setter로 추적) | 개발자가 직접 setState 호출 |
중첩 상태 감지 | 자동으로 추적 | 수동 처리 필요 |
의존성 추적 | 자동 | 없음 (컴포넌트 전체 리렌더) |
최적화 방식 | 변경된 부분만 리렌더 | diff 알고리즘 기반 |
코드 작성 스타일 | Composition API 도입 (유연함) | Hooks 사용 (명시적이고 선언적) |
⛓ Vue 2 → Vue 3 주요 개선점
Vue 3는 Vue 2의 구조적 한계와 성능 문제를 해결하고자 다음과 같은 개선을 도입했습니다.
- 더 강력한 반응성:
Proxy
기반 시스템 도입.
- Composition API 도입: 코드 재사용성과 모듈화 향상.
- Tree Shaking 최적화: 번들 크기 감소.
- 멀티 루트 템플릿 지원:
<template>
태그 내 여러 루트 엘리먼트 허용.
- 가상 DOM 성능 향상: 재작성된 가상 DOM 엔진.
- 더 작고 빠른 런타임: 런타임 크기 감소, 로딩 속도 향상.