Vue와 React의 Reactivity

date
May 16, 2023
slug
reactivity-in-vue-and-react
author
status
Public
tags
summary
type
Post
thumbnail
category
updatedAt
Mar 7, 2024 01:51 PM

Vue의 반응성 시스템

Vue의 반응성 시스템의 핵심 원리를 이해하는 것이 중요합니다. Vue는 객체의 프로퍼티에 대한 getter와 setter를 사용하여 해당 프로퍼티의 변경을 감지합니다. 이 감지는 JavaScript의 Object.defineProperty 또는 Vue 3의 Proxy를 사용하여 이루어집니다.
이러한 반응성의 핵심 원리를 기반으로 설명하면 다음과 같습니다:
  1. 기본 값 (Primitive Values): JavaScript의 기본 값들 (예: 문자열, 숫자, 불린 값)은 불변(immutable)합니다. 따라서, 이러한 값을 직접 변경할 수 없으며, 새 값을 할당하는 방식으로만 변경됩니다. 이러한 변경은 Vue의 반응성 시스템에 의해 감지되지 않습니다.
    1. javascriptCopy code let value = "original"; value = "changed"; // 새 값을 할당합니다.
  1. 객체의 프로퍼티: Vue는 객체의 프로퍼티에 반응성을 부여하기 위해 해당 객체에 getter와 setter를 추가합니다. 프로퍼티의 값이 변경되면 Vue는 이를 감지하고 연관된 컴포넌트를 다시 렌더링합니다.
    1. javascriptCopy code let obj = { property: "original" }; obj.property = "changed"; // 프로퍼티의 값을 변경합니다. Vue는 이 변경을 감지합니다.
injectprovide 메커니즘이 작동하는 방식을 고려하면, 주입된 값 자체가 기본 값일 경우 해당 값을 변경하면 원본 객체와 연결이 끊기게 됩니다. 따라서 반응성을 잃게 됩니다. 하지만 주입된 값이 객체의 프로퍼티로 참조되면 Vue의 반응성 시스템은 여전히 해당 객체의 프로퍼티 변경을 추적하고 있기 때문에 반응성을 유지할 수 있습니다.
간단히 말해, 객체 프로퍼티를 사용하면 Vue의 반응성 시스템과 함께 작동하며, 기본 값으로 주입하면 해당 반응성을 잃게 됩니다.
 
Object.defineProperty는 JavaScript에서 객체의 속성을 정의하거나 수정하는 메서드입니다. 이 메서드는 Vue 2의 반응성 시스템의 핵심 요소 중 하나입니다. 이를 통해 객체의 속성에 대한 getter와 setter를 정의할 수 있으며, 이를 활용해 Vue는 데이터 변경을 감지하고 반응형 업데이트를 수행합니다.

기본 사용법:

javascriptCopy code Object.defineProperty(obj, prop, descriptor)
  • obj: 대상 객체.
  • prop: 정의하거나 수정할 속성의 이름.
  • descriptor: 해당 속성에 대한 설명자 객체.
 

반응성에서의 사용:

Vue 2의 반응성은 대략적으로 Object.defineProperty를 사용하여 데이터 속성에 getter와 setter를 추가하는 방식으로 작동합니다. 데이터가 접근되거나 수정될 때, Vue는 이러한 getter와 setter를 사용하여 변경 사항을 감지하고 DOM 업데이트를 수행합니다.
javascriptCopy code const data = { price: 100 }; Object.defineProperty(data, 'price', { get: function() { console.log('Getting price value'); return this._price; }, set: function(newVal) { console.log('Setting new price value'); this._price = newVal; } });
위 코드는 price 속성에 접근하거나 수정할 때마다 콘솔에 메시지를 출력합니다.
 

 

Vue3의 반응성

Proxy는 ECMAScript 6 (ES6)에서 도입된 JavaScript의 기본 객체로, 객체와 함수의 동작을 사용자 정의 할 수 있게 해주는 객체입니다. Vue 3는 이 Proxy 객체를 사용하여 데이터 반응성을 구현하며, 이를 통해 Vue 2의 Object.defineProperty 기반의 반응성에 비해 여러 개선점을 가집니다.

Proxy 기반의 반응성의 장점:

  1. 깊게 중첩된 객체에 대한 반응성: Vue 2에서는 깊게 중첩된 객체에 반응성을 추가하기 위해 각 중첩 레벨에서 getter와 setter를 재귀적으로 설정해야 했습니다. Proxy를 사용하면 전체 객체를 감싸 단 한 번의 작업으로 깊은 중첩에 대한 반응성을 처리할 수 있습니다.
  1. 배열의 반응성: Vue 2에서는 배열의 변경을 감지하기 위해 배열 메서드를 다시 정의하여야 했습니다. Proxy를 사용하면 원래의 배열 메서드를 변경할 필요 없이 배열 변경을 직접 감지할 수 있습니다.
  1. 성능 향상: Object.defineProperty는 객체의 각 속성에 대해 따로 설정해야 하기 때문에 초기 렌더링시 비효율적일 수 있습니다. 반면 Proxy는 전체 객체를 감싸므로 초기 설정이 더 빠르고 효율적입니다.

Proxy의 기본 사용법:

javascriptCopy code const target = { message: 'hello' }; const handler = { get: function(target, prop, receiver) { console.log(`Getting ${prop}`); return Reflect.get(target, prop, receiver); }, set: function(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 \n hello proxy.message = 'world'; // Setting message to world

Vue 3에서의 Proxy 사용:

Vue 3의 반응성 시스템은 ProxyReflect API를 사용하여 구현됩니다. 데이터 객체를 Proxy로 감싸면 Vue는 이 프록시를 통해 데이터의 변경을 감지하고 필요한 경우 DOM 업데이트를 수행합니다.
물론, 개발자가 직접 Proxy 객체를 작성할 필요는 없습니다. Vue는 내부적으로 이를 처리하며, API 수준에서는 Vue 2와 매우 유사하게 동작합니다.
다만, Proxy는 IE11과 같은 오래된 브라우저에서는 지원되지 않기 때문에 Vue 3의 반응성 시스템은 IE11에서 작동하지 않습니다.
React와 Vue 3의 반응성 시스템은 내부 메커니즘이 다르며, 각각의 접근 방식에는 특정한 장단점이 있습니다. 먼저 각 프레임워크의 반응성 시스템에 대한 간략한 설명을 제공하겠습니다.
 

 

React의 반응성 시스템

  1. 상태 변경의 추적: React는 setState 또는 hooks에서 제공하는 useStateuseReducer 같은 메서드를 통해 상태 변경을 추적합니다.
  1. 반응성의 방식: React는 변경된 상태를 기반으로 가상 DOM을 업데이트하고, 이를 실제 DOM과 비교(diffing)하여 필요한 변경만 실제 DOM에 반영합니다.
  1. 명시적인 상태 관리: React는 상태 변경을 명시적으로 관리합니다. 개발자는 상태를 변경할 때마다 setState 또는 useState의 setter 함수를 호출해야 합니다.
 

Vue 3의 반응성 시스템

  1. Proxy 기반의 반응성: Vue 3는 Proxy를 사용하여 데이터 객체의 변경을 감지합니다. 이를 통해 데이터의 속성에 접근하거나 수정할 때마다 자동으로 반응성을 처리할 수 있습니다.
  1. 내부적인 디펜던시 추적: Vue 3의 반응성 시스템은 "dependency"라는 개념을 사용하여 어떤 컴포넌트나 computed 속성이 어떤 데이터에 의존하는지를 자동으로 추적합니다. 데이터가 변경될 때 의존하는 컴포넌트만 다시 렌더링됩니다.
  1. 자동적인 상태 관리: Vue의 데이터는 dataref를 통해 선언되며, 이 데이터는 자동으로 반응형으로 만들어집니다. 상태 변경을 위해 별도의 메서드 호출이 필요하지 않습니다.
 

 

주요 차이점

  1. 반응성의 구현: React는 가상 DOM과 diffing 알고리즘을 사용하며, Vue 3는 Proxy를 사용하여 데이터의 변경을 직접 감지합니다.
  1. 상태 관리의 명시성: React는 상태 변경을 명시적으로 처리해야 합니다 (예: setState), 반면 Vue는 데이터 변경이 자동으로 반응형으로 처리됩니다.
  1. 최적화 전략: React의 재렌더링은 상태 변경 시 전체 컴포넌트 트리에 영향을 줄 수 있으나, 최적화를 위해 shouldComponentUpdateReact.memo와 같은 메커니즘을 사용할 수 있습니다. Vue는 의존성 추적을 통해 자동으로 최적화를 수행하며, 변경된 데이터에 의존하는 컴포넌트만 재렌더링됩니다.
둘 다 훌륭한 프레임워크이며, 각자의 사용 사례와 선호에 따라 선택할 수 있습니다.
 
Vue.js 2는 많은 웹 개발자들에게 인기 있는 프론트엔드 프레임워크지만, 그동안 사용하면서 몇몇 한계점들이 드러났습니다. Vue 3의 등장은 이러한 한계점들을 해결하기 위한 목적도 포함하고 있습니다. Vue 2의 주요 한계점은 다음과 같습니다.
 
  1. Reactivity System:
      • Vue 2의 반응성 시스템은 ES5의 Object.defineProperty를 사용합니다. 이 방식은 배열 인덱스의 변경이나 객체의 새로운 속성 추가에 대한 감지에 한계를 보였습니다.
      • Vue 3에서는 ES6의 Proxy를 활용하여 더 향상된 반응성 시스템을 제공합니다.
  1. Code Organization:
      • Vue 2에서는 큰 컴포넌트를 관리할 때 로직이 여러 옵션(properties, methods, computed 등)에 걸쳐 분산되는 문제가 있었습니다.
      • Vue 3의 Composition API는 로직을 기능별로 묶어 코드의 조직성과 재사용성을 향상시킵니다.
  1. Tree Shaking Support:
      • Vue 2는 Tree shaking에 완벽하게 최적화되어 있지 않아, 불필요한 코드가 최종 번들에 포함될 수 있습니다.
      • Vue 3는 이러한 문제를 개선하여 더 효율적인 트리 쉐이킹을 지원합니다.
  1. Template Limitations:
      • Vue 2 템플릿에서는 하나의 루트 엘리먼트를 가져야만 했습니다.
      • Vue 3에서는 이러한 제한을 제거하였습니다.
  1. Virtual DOM:
      • Vue 2의 가상 DOM 알고리즘이 항상 최적의 성능을 보장하는 것은 아니었습니다.
      • Vue 3에서는 가상 DOM의 리팩토링과 최적화를 통해 성능을 향상시켰습니다.
  1. Size:
      • Vue 3는 Vue 2보다 더 작고, 빠르며, 트리 쉐이킹에 더 최적화되어 있어, 최종 빌드 크기를 줄일 수 있습니다.
이러한 한계점들에도 불구하고, Vue 2는 그 자체로도 강력하고 확장성 있는 프레임워크입니다. 그러나 Vue 3의 등장은 이러한 한계점들을 극복하고 더 많은 기능과 최적화를 제공하기 위한 목적이 있습니다.