상태관리와 렌더링 메커니즘 다 똑같을까?

Mincho·2024년 9월 18일
5

경험

목록 보기
5/5
post-thumbnail

이 글은 당신은 프론트엔드 개발자가 맞나요?에서 이어져 특정 프레임워크에 종속되지 말자는 생각으로 작성하게 되었습니다. 🧐
많은 프레임워크 중 아직 능숙하게 다룰 수 있는 React와 Vue정도만 다뤄봤습니다..🥲

현대의 웹 애플리케이션에 대해

웹 애플리케이션은 시간이 지남에 따라 더욱 복잡해지고, 정적인 웹 페이지에서 새로고침 없이 변화하는 동적 인터랙션이 주목받으면서, 상태 관리 메커니즘은 사용자 경험을 향상하는 중요한 요소로 자리 잡고 있다. 효율적인 상태 관리 메커니즘을 구현하면 애플리케이션의 성능과 유지보수성이 향상되는 큰 장점을 지닐 수 있다.

사용자 기기의 성능이 비약적으로 상승하면서, 기존의 서버 사이드에서 사용자 기기에 렌더링 책임을 부여하는 클라이언트 사이드 렌더링(CSR) 방식이 유행하기 시작했다.

흔히 사용하는 CSR 기반의 JavaScript 프레임워크(라이브러리)들은 모두 바닐라 JavaScript로 구현되어 있지만, 프레임워크마다 구현 방식에 차이가 있다. 대표적으로 사용되는 ReactVue의 상태 관리 방식에 대해 자세히 알아보자. (React에 대한 reference는 많아. Vue를 좀 더 다뤄봤다.)


상태관리

React

React에서 자주 사용되는 상태관리 훅인 useState는 클로저 기반으로 이루어져 있다.

클로저렉시컬 스코프(lexical scope)의 원리를 이용하여, 함수가 생성될 당시의 외부 변수에 접근할 수 있는 함수를 의미한다. 클로저는 함수가 선언된 시점의 렉시컬 환경을 기억하여, 외부 함수가 종료된 후에도 그 환경에 접근할 수 있게 해준다.

const closure = () => {
    let count = 0
    const countUp = () => {
        count++
        console.log(count)
    }
    return countUp
}

const counter = closure()

counter() // 1
counter() // 2
counter() // 3

이렇게 되면 정보를 은닉할 수 있으며, 현재 스코프의 상태를 보존할 수 있다.

클로저기반으로 구현된 useState

function reactHook() {
  // 상태를 저장하고 상태 인덱스를 관리하는 객체
  const stateContext = {
    currentIndex: 0, // 현재 상태의 인덱스
    states: []       // 상태를 저장하는 배열
  };

  /**
   * useState 훅의 구현
   * @param {Function|any} initialState - 상태의 초기값 또는 초기화 함수
   * @returns {[any, Function]} - 현재 상태와 상태를 업데이트하는 함수
   */
  const useState = (initialState) => {
    // 게으른 초기화: initialState가 함수인 경우 실행하여 상태를 결정
    const currentState = typeof initialState === "function" ? initialState() : initialState;

    // 현재 상태 인덱스
    const currentIndex = stateContext.currentIndex;

    // 상태 배열에서 현재 인덱스의 상태를 가져오거나 초기 상태를 저장
    stateContext.states[currentIndex] = stateContext.states[currentIndex] || currentState;

    /**
     * 상태를 업데이트하는 함수
     * @param {any} newState - 새로운 상태 값
     */
    const setState = (newState) => {
      if (!Object.is(stateContext.states[currentIndex], newState)) {
        // 상태가 변경된 경우 새로운 상태로 업데이트
        stateContext.states[currentIndex] = newState;
        
        // 리렌더링 로직: 상태가 변경되었을 때 호출
        // 이 부분에 리렌더링 로직을 추가해야 함
      }
    };

    // 상태 인덱스 증가
    stateContext.currentIndex++;

    // 현재 상태와 상태 업데이트 함수를 반환
    return [stateContext.states[currentIndex], setState];
  };

  // useState 훅을 외부에 노출
  return { useState };
}

클로져 기반으로 간단히 useState를 구현했다. 코드를 하나씩 순차적으로 살펴보자.

state global context

const stateContext = {
  currentIndex: 0, // 현재 상태의 인덱스
  states: []       // 상태를 저장하는 배열
};

stateContext는 현재 상태의 인덱스와 상태를 전역으로 관리한다고 볼 수 있다. react는 순차적으로 실행되기 때문에 useState를 선언한 순서대로 index를 부여하여 고유 state를 구분할 수 있게 한다.

초기상태에 따른 분기처리와 global state에 상태 넣어주기

const useState = (initialState) => {
  const currentState = typeof initialState === "function" ? initialState() : initialState;
  const currentIndex = stateContext.currentIndex;
  stateContext.states[currentIndex] = stateContext.states[currentIndex] || currentState;

currentState에서 initialState의 타입에 따른 분기처리를 해준다. (리액트의 useState와 lazy initialization)

그리고 현재 인덱스를 내부에 정의하고, global상태인 stateContext.states배열에 설정한다.

setState

const setState = (newState) => {
  if (!Object.is(stateContext.states[currentIndex], newState)) {
    stateContext.states[currentIndex] = newState;
    // 리렌더링 로직: 상태가 변경되었을 때 호출
    // 이 부분에 리렌더링 로직을 추가해야 함
  }
};

상태를 변경해 주는 setState다. 현재의 상태와 새로운 상태를 비교하여 값이 다르다면 새로운 값으로 갱신해주며, 리렌더링을 해준다. 여기서 값을 비교 할때는 얕은 비교를 통해 수행한다.

다음 상태관리를 위한 세팅과 return

stateContext.currentIndex++;
return [stateContext.states[currentIndex], setState];

마지막으로 index를 하나 증가 시켜 다음 상태관리에 대한 스코프 관리를 하고, return해줍니다. [state, setState] = useState() 형태


Vue

이와 달리 Vue는 클로저가 아닌 좀 더 직관적인 느낌의 상태 관리 메커니즘을 채택했다. javascript 객체 정적 메서드인 Object.defineProperty를 기반으로 반응형 데이터(vue에서는 상태관리보다 반응형이라는 용어를 사용하는 것 같다, Vue에서는 상태라는 용어 대신 반응형으로 대체하겠다!)를 추적하고 이를 템플릿에 반영한다.

Object.defineProperty는 객체 속성의 descriptor를 정의 한다.(Object.defineProperty에 대해서)

즉, Vue에서는 Object.defineProperty의 getter와 setter를 사용하여 반응형 데이터를 관리한다.

모든 컴포넌트 인스턴스에는 반응형 데이터를 감시하는 watcher가 있고, 반응형 데이터에 따라 watcher는 변화를 감지하여 리렌더링이 트리거 된다.

Object.defineProperty

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Reactive Data Example</title>
  </head>
  <body>
    <div>
      <label for="nameInput">Name:</label>
      <input type="text" id="nameInput" placeholder="Enter your name" />
    </div>
    <div>
      <p class="output">Hello, <span id="nameDisplay"></span></p>
    </div>

    <script>
      // 반응형 객체를 생성하는 함수
      const reactive = (obj) => {
        Object.keys(obj).forEach((key) => {
          let internalValue = obj[key];

          Object.defineProperty(obj, key, {
            get() {
              return internalValue;
            },
            set(newValue) {
              internalValue = newValue;
              // DOM 업데이트
              if (key === "name") {
                document.getElementById("nameDisplay").textContent = newValue;
              }
            },
          });
        });

        return obj;
      };

      // 반응형 데이터 객체 생성(like vue3)
      const data = reactive({
        name: "",
      });

      // 입력 필드와 데이터 바인딩
      const nameInput = document.getElementById("nameInput");
      nameInput.addEventListener("input", (event) => {
        data.name = event.target.value; // 데이터 변경 시 DOM 자동 업데이트
      });
    </script>
  </body>
</html>

다음과 같이 reactive라는 반응형 데이터를 생성해 주는 함수를 만들었다. reactive의 특정 속성을 설정할 때(set) 의존성을 갖는 템플릿이 리렌더링 되어 반영되는 것이다.

여기서는 name이라는 속성이 변경된다면 관련 템플릿이 변경된다. (여기서는 nameDisplay라는 id를 가진 span태그)

바닐라 Js로 임의로 구현했지만, 실제로 vue에서는 {{}}형태의 mustachev-bind문법을 이용해 반응형 데이터에 의존성을 갖는 템플릿을 인식시켜준다.

그런데 vue2(Option API)에서 vue3(Composition API)로 업데이트가 되면서 반응형 데이터를 업데이트하는 방식이 변경되었다.


Proxy

javascript es6에 Proxy객체가 추가 되었는데, 기존의 객체의 getter/setter처럼 속성 변경 감지를 가로채서 동작할 수 있게 되었다. 말그대로 우리가 흔히 아는 그 proxy의 의미이다.

const target = {
	message1 : "hello",
    message2 : "everyone"
}

const handler = {
	get(target, prop, receiver) {
    	return "world"
    }
}

const proxy = new Proxy(target, handler)

proxy.message1 // "world"
proxy.message2 // "world"

그렇다면 vue3에서는 왜 Object.defineProperty대신 Proxy를 기반으로 반응형 데이터를 구현했을까?

적은 메모리 사용 간편한 사용방식

// Object.defineProperty를 이용한 방식
let data = { count: 0, name : "" };
Object.defineProperty(data, 'count', {
  get() {
    console.log('count가 읽혔습니다.');
    return this._count;
  },
  set(newValue) {
    console.log('count가 변경되었습니다.');
    this._count = newValue;
  }
});

Object.defineProperty(data, 'name', {
  get() {
    console.log('name이 읽혔습니다.');
    return this._name;
  },
  set(newValue) {
    console.log('count가 변경되었습니다.');
    this._name = newValue;
  }
});

기존의 방식에서는 정의한 객체의 속성마다 getter와 setter를 설정해 주어야 한다. 물론 반복문이나 Object.defineProperties로 모든 속성을 설정해 줄 수 있기는 하지만 번거롭다. 또한 속성마다 설정해준다면 메모리 사용 측면에서도 효율적이지 못한 것 같다.

// Proxy를 사용한 방식
let data = { count: 0, name : "choi" };
let proxyData = new Proxy(data, {
  get(target, key) {
    console.log(`${key}가 읽혔습니다.`);
    return target[key];
  },
  set(target, key, value) {
    console.log(`${key}가 변경되었습니다.`);
    target[key] = value;
    return true;
  }
});

proxyData.count // count가 읽혔습니다. 0
proxyData.count = 100 // count가 변경되었습니다. 100

proxyData.name // name가 읽혔습니다. 'choi'
proxyData.name = "mincho" // name가 변경되었습니다. mincho

반면 Proxy를 사용한 방법은 전체 객체를 래핑하여 정의하는 방식이라 더 효율적이다.(좀 더 직관적인 느낌이 더 크다.)

완전한 객체 감시

// Object.defineProperty를 이용한 방식
let data = { existingProp: 'I exist' };
Object.defineProperty(data, 'existingProp', {
    get() {
    console.log('getter!');
    return this._existingProp;
  },
  set(newValue) {
    console.log('setter!');
    this._existingProp = newValue;
  }
});

// 새 속성 추가 - 반응형으로 동작하지 않음
data.newProp = 'I am new';

Object.defineProperty는 기존에 정의된 속성을 기반으로 동작하기 때문에 새로운 속성이 추가된다면 다시 getter/setter를 정의해 주지 않는다면 원하는 대로 동작이 되지 않는다.

// Proxy를 사용한 방식
let data = { existingProp: 'I exist' };
let proxyData = new Proxy(data, {
  get(target, key) {
    console.log(`${key}가 읽혔습니다.`);
    return target[key];
  },
  set(target, key, value) {
    console.log(`${key}가 설정되었습니다.`);
    target[key] = value;
    return true;
  }
});

// 새 속성 추가 - 반응형으로 동작함
proxyData.newProp = 'I am new'; // "newProp가 설정되었습니다." 

Proxy는 새로운 속성을 추가한다면 proxy객체로 정의한대로 getter/setter를 다시 정의하지 않아도 된다. 즉 동적으로 속성을 추가해도 반응형 값으로 유지할 수 있다.

그렇다면 위에서 Object.defineProperty로 구현했던 것을 Proxy를 이용해 재구성해보자.

Proxy로 재구성한 반응형 데이터

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Reactive Data Example</title>
  </head>
  <body>
    <div>
      <label for="nameInput">Name:</label>
      <input type="text" id="nameInput" placeholder="Enter your name" />
      <input
        type="text"
        id="descriptionInput"
        placeholder="Enter your description"
      />
    </div>
    <div>
      <p class="output">Hello, <span id="nameDisplay"></span></p>
      <p>
        description :
        <span id="descriptionDisplay"></span>
      </p>
    </div>

    <script>
      // Proxy 기반의 반응형 객체 생성 함수
      const reactive = (obj) => {
        return new Proxy(obj, {
          set(target, key, value) {
            target[key] = value;

            // DOM 업데이트
            if (key === "name") {
              document.getElementById("nameDisplay").textContent = value;
      		  return;
            }

            if (key === "description") {
              document.getElementById("descriptionDisplay").textContent = value;
      		  return;
            }
          },
        });
      };

      const data = reactive({
        name: "",
        description: "",
      });

      // 입력 필드와 데이터 바인딩
      const nameInput = document.getElementById("nameInput");
      nameInput.addEventListener("input", (event) => {
        data.name = event.target.value;
      });

      const descriptionInput = document.getElementById("descriptionInput");
      descriptionInput.addEventListener("input", (event) => {
        data.description = event.target.value;
      });
    </script>
  </body>
</html>

객체를 Proxy로 래핑하여 reactive반응형 객체를 생성했다. 기존 예제에서 description속성을 추가하였다. 객체의 속성마다 getter/setter를 생성해주지 않아 좀 더 직관적인 코드가 되었다.


상태 관리에 따른 렌더링 방식

지금까지 React와 Vue의 간단한 상태관리 메커니즘에 대해 알아봤다. 그렇다면 예제 코드와 함께 상태 혹은 반응형 데이터에 따라 어떻게 렌더링되는지 알아보자.

React Counter

단순히 CounterButton을 클릭하면 count를 하나씩 늘려나가는 구조이다.
그렇다면 한번 실행을 시켜보자.

누구나 간단하게 예상가능하듯 버튼을 클릭하여 setCount가 트리거 되며 App컴포넌트가 다시 호출되며 재평가 되는 것을 알 수 있다.

Vue Counter

Vue에서도 다음과 같이 React와 같은 기능을 구현했다.그렇다면 Vue에서는 어떻게 동작할까??

콘솔에는 예상과는 다르게 초기 렌더링으로 인한 Current Count : 0만 표시되고 상태를 변경한다 한들 컴포넌트가 재평가가 되지 않는 것처럼 보인다.

React와 Vue는 모두 가상Dom(사실을 객체)을 이용해, Dom변경을 효율적으로 처리한다. 흔히 Diffing알고리즘을 통해, 기존의 가상 Dom과 새롭게 들어온 가상 Dom을 비교해 변경된 부분만 업데이트를 하여 렌더링한다.

그런데 위의 테스트 결과에서는 왜 이런 차이를 보이는 것일까??
어떻게 보면 간단한 문제이다. 두 기술스택의 철학의 차이(?)라고 볼 수 있다.

React

React는 16.8버전부터 훅이 도입되면서 함수형 컴포넌트가 떠오르기 시작했다.(사실 React초창기에도 함수형 컴포넌트를 사용할수는 있었다고 한다.) 함수는 말그대로 순수해야하며 부수효과가 발생해서는 안된다. 이 논리를 컴포넌트에도 적용한 것인데, state와 props에만 의존하고 다른 외부의 것들에는 변함이 없어야 하는 것이다.

물론 함수형 컴포넌트에서 useEffect와 같이 부수효과를 발생시키는 경우도 있지만, 기본 전제가 순수해야한다는 철학이다.

그렇기 때문에 state나 props의 변경이 일어난다면 컴포넌트는 재실행된다. 결국 리렌더링된 컴포넌트를 바탕으로 가상 Dom이 생성되며 기존 Dom이랑 비교하게 된다.

여기서 오해하면 안되는 것은 렌더링이라는 말이다. React에서는 렌더링의 과정을 렌더커밋단계로 설명하고 있다.

렌더 단계 : 컴포넌트가 어떻게 보여야 하는지를 결정하는 순수한 계산 과정. 실제 Dom에는 반영이 되지 않는다.
커밋 단계 : 렌더링된 결과를 실제로 DOM에 반영하는 단계. Dom을 업데이트한 이후에 사이드 이펙트를 수행한다.

👉 즉 렌더단계에서 기존의 가상 Dom과 변경된 가상 Dom을 비교하고 커밋단계에서 반영한다.

import { useState } from "react";
import CounterButton from "./component/CounterButton";
import CounterDisplay from "./component/CounterDisplay";

function App() {
  // 렌더 단계
  const [count, setCount] = useState(0);

  const handleIncrement = () => {
    setCount((prevCount) => prevCount + 1);
  };

  console.log(
    `%cCurrent Count : ${count}`,
    "background-color: yellow; color: black"
  );
  
  return (
    <div className="app">
      <h1>Counter</h1>
      <CounterDisplay count={count} />
      <CounterButton onIncrement={handleIncrement} />
    </div>
  );

	//~~여기 까지 (단 jsx는 가상 Dom으로 비교하여 실제 변경된 부분만 업데이트 한다.)
}

export default App;

실제 dom에 반영이 되지 않아도 state와 props의 변화에 따라 반복적으로 렌더단계에 진입하여 실행될 수 밖에 없고 console.log도 반복해서 보여지는 이유다.

규모가 큰 애플리케이션에서는 불필요한 렌더링은 큰 비용이 든다. 그래서 React에서는 이를 인지하고 메모이제이션을 위해 useMemo, useCallback, React.memo를 제공한다. 리렌더링이 되더라도 캐싱을 통해 같은 주소를 참조하게끔 하여 최적화를 진행하는 것이다.


Vue

컴포넌트를 함수처럼 순수하게 동작하는 건 좋은데, 굳이 불필요한 렌더단계의 반복적인 연산이 꼭 필요할까?? 사실 실제 변경되는 반응형 데이터만 추적하여 비교하면 되지 않을까??

실제로 리액트처럼 리렌더링시 발생하는 불필요한 연산을 모두 메모이제이션하는 과정은 개발자에게 꽤나 번거로운 일이다.

실제 Vue에서는 반응형 데이터를 기반으로 가상 Dom비교를 진행한다. 앞에서 살펴봤듯이 Vue의 반응형 시스템은 데이터가 사용되는 컴포넌트나 템플릿 부분을 추적하, 실제로 업데이트 된 곳만 반영한다.

Vue는 데이터를 사용하는 템플릿 부분을 기억하기 때문에, 불필요한 컴포넌트 전체의 재실행을 방지하고 최소한의 부분으로 업데이트 한다. 즉, 컴포넌트 전체를 업데이트 하는 것이 아니라, 반응형 데이터와 직접적으로 연관된 부분만 업데이트가 되는 것이다.

Vue에서는 어떠한 원리로 처리하는 것인지, Vue공식문서에서 렌더링 메커니즘과 관련된 내용을 좀 더 살펴보자.

템플릿의 정적 호이스팅

<div>
  <div>foo</div> <!-- 호이스트(hoist) 됨 -->
  <div>bar</div> <!-- 호이스트 됨 -->
  <div>{{ dynamic }}</div>
</div>

Vue는 컴파일러를 통해 기존 정적 노드를 호이스팅하여 최적화할 수 있다. 반응형 데이터를 사용하지 않은 foobar의 경우 호이스팅되어 렌더링 되어도 같은 값을 바라보며 비교할 필요도 없게 된다.

위의 템플릿을 템플릿 분석기를 통해 한번 살펴보자. Vue 템플릿 분석기

import { createElementVNode as _createElementVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"

export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createElementBlock("div", null, [
    _cache[0] || (_cache[0] = _createElementVNode("div", null, "foo", -1 /* HOISTED */)),
    _cache[1] || (_cache[1] = _createElementVNode("div", null, "bar", -1 /* HOISTED */)),
    _createElementVNode("div", null, _toDisplayString(_ctx.dynamic), 1 /* TEXT */)
  ]))
}
// Check the console for the AST

foobar모두 Vue context의 cache에 쌓이는 것을 확인할 수 있다. 이 cache의 내용은 정적으로 재사용된다.

반면 {{ dynamic }}의 내용은 _toDisplayString(_ctx.dynamic)으로 변환된 것을 알 수 있는데, 즉 동적인 값이므로 렌더링 되었을 때 추적해야하는 값으로 인식된다고 오해할 수 있는데, 반은 맞고 반은 틀리다.

패치 플래그

사실 우리가 주목해야하는 건 _toDisplayString(_ctx.dynamic)뒤에 오는 숫자 1이다.

Vue.js 깃헙 레포지토리에서 패치플래그 부분를 보면 다음과 같은 부분을 확인할 수 있다.

/**
 * dev only flag -> name mapping
 */
export const PatchFlagNames: Record<PatchFlags, string> = {
  [PatchFlags.TEXT]: `TEXT`,
  [PatchFlags.CLASS]: `CLASS`,
  [PatchFlags.STYLE]: `STYLE`,
  [PatchFlags.PROPS]: `PROPS`,
  [PatchFlags.FULL_PROPS]: `FULL_PROPS`,
  [PatchFlags.NEED_HYDRATION]: `NEED_HYDRATION`,
  [PatchFlags.STABLE_FRAGMENT]: `STABLE_FRAGMENT`,
  [PatchFlags.KEYED_FRAGMENT]: `KEYED_FRAGMENT`,
  [PatchFlags.UNKEYED_FRAGMENT]: `UNKEYED_FRAGMENT`,
  [PatchFlags.NEED_PATCH]: `NEED_PATCH`,
  [PatchFlags.DYNAMIC_SLOTS]: `DYNAMIC_SLOTS`,
  [PatchFlags.DEV_ROOT_FRAGMENT]: `DEV_ROOT_FRAGMENT`,
  [PatchFlags.CACHED]: `HOISTED`,
  [PatchFlags.BAIL]: `BAIL`,
}

PatchFlagNames를 정의해주고 이에 따라 어떤 타입으로 매핑되는지 확인 할 수 있는데, 아래와 같이 enum타입으로 정의된PatchFlags와 매핑 관계가 있다.

export enum PatchFlags {
  CACHED = -1, // HOISTED
  TEXT = 1, // TEXT
  STYLE = 1 << 2, // STYLED
  PROPS = 1 << 3, // PROPS
  NEED_HYDRATION = 1 << 5, // NEED_HYDRATION
  DYNAMIC_SLOTS = 1 << 10, // DYNAMIC_SLOTS
  //...등등
}

그래서 패치 플래그를 통해 각 Node의 타입 및 유형을 미리 지정해주고, Vue는 컴파일 시 많은 정보를 추론할 수 있게 된다.

연속적인 정적노드 처리

또한 정적 노드가 연속적으로 들어온다면, 아예 템플릿으로 캐싱해 버린다.

<div>
  <div class="foo">foo</div>
  <div class="foo">foo</div>
  <div class="foo">foo</div>
  <div class="foo">foo</div>
  <div class="foo">foo</div>
  <div>{{ dynamic }}</div>
</div>
import { createElementVNode as _createElementVNode, toDisplayString as _toDisplayString, createStaticVNode as _createStaticVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from "vue"

export function render(_ctx, _cache, $props, $setup, $data, $options) {
  return (_openBlock(), _createElementBlock("div", null, [
    _cache[0] || (_cache[0] = _createStaticVNode("<div class=\"foo\">foo</div><div class=\"foo\">foo</div><div class=\"foo\">foo</div><div class=\"foo\">foo</div><div class=\"foo\">foo</div>", 5)),
    _createElementVNode("div", null, _toDisplayString(_ctx.dynamic), 1 /* TEXT */)
  ]))
}

// Check the console for the AST

이 모든 정밀한 제어는 Vue가 다른 Js 기술 스택과는 달리 컴파일러를 제어하여 가능한 것이다.


++추가적으로 보면 좋은 것

React에서도 시범적으로 Compiler를 신경쓰고 있는 느낌이다. (리액트 컴파일러) React 컴파일러도 자동으로 코드를 메모이제이션하여, 보다 개발자의 수고를 덜어주는 방향을 잡아가려는 것 처럼 보였다.

컴파일러의 도입은 우리가 기존에 알고 있던 리액트의 리렌더링 조건이 달라질지도 모르지만 리액트 생태계가 크게 개선될 것으로 기대된다.😄

예제 레포 링크는 여기서 확인가능합니다.

reference

Vue 공식문서
반응형에 대해 깊이 알아보기
참조 동일성을 위한 메모이제이션
Vanilla Javascript로 상태관리 시스템 만들기

👍피드백은 언제든지 환영입니다~!

profile
www.mincho130.xyz <-- 블로그 이사했습니당

0개의 댓글