React

useState만들기(feat.클로저)

프론트 엄복동 2025. 6. 6. 19:26
728x90

목표

useState를 모방한 모듈을 만들어 클로저 공부하기

 

준비

liveserver 확장프로그램

 

lib/react.js

더보기
// lib/react.js
import { render as domRender } from './dom.js';

// 훅 관리를 위한 전역 변수들
let currentComponent = null;
let currentContainer = null;
let hookIndex = 0;
let hooks = [];

// useState 구현
export function useState(initialValue) {
  const currentIndex = hookIndex++;

  // 첫 실행이면 초기값 저장
  if (hooks[currentIndex] === undefined) {
    hooks[currentIndex] = initialValue;
  }

  const setState = (newValue) => {
    hooks[currentIndex] = newValue;
    // 컴포넌트 다시 렌더링
    rerender();
  };

  return [hooks[currentIndex], setState];
}

// 컴포넌트 렌더링
export function render(component, container) {
  currentComponent = component;
  currentContainer = container;

  rerender();
}

// 리렌더링 함수
function rerender() {
  if (!currentComponent || !currentContainer) return;

  hookIndex = 0; // 훅 인덱스 리셋

  // 컴포넌트 실행해서 가상 DOM 생성
  const vnode = currentComponent();

  // 실제 DOM으로 렌더링
  domRender(vnode, currentContainer);

  console.log('🔄 리렌더링 완료!', { hooks, hookIndex });
}

// 테스트용 컴포넌트
export function createTestComponent() {
  return function TestComponent() {
    const [count, setCount] = useState(0);

    // 가상 DOM 반환 (JSX 대신)
    return {
      tag: 'div',
      props: { className: 'test-component' },
      children: [
        {
          tag: 'h2',
          props: {},
          children: ['카운터 테스트'],
        },
        {
          tag: 'p',
          props: {},
          children: [`현재 카운트: ${count}`],
        },
        {
          tag: 'button',
          props: {
            onClick: () => setCount(count + 1),
          },
          children: ['카운트 증가'],
        },
        {
          tag: 'button',
          props: {
            onClick: () => setCount(count - 1),
          },
          children: ['카운트 감소'],
        },
      ],
    };
  };
}

 

lib/dom.js

더보기
// 가상 DOM 노드 생성
export function createElement(tag, props = {}, ...children) {
  return {
    tag,
    props: props || {},
    children: children.flat(), // 중첩 배열 평평하게
  };
}

// 가상 DOM을 실제 DOM으로 변환
export function createDOMElement(vnode) {
  // 문자열이면 텍스트 노드
  if (typeof vnode === 'string' || typeof vnode === 'number') {
    return document.createTextNode(vnode);
  }

  // 가상 DOM 객체라면
  if (vnode && typeof vnode === 'object' && vnode.tag) {
    const element = document.createElement(vnode.tag);

    // props 적용
    if (vnode.props) {
      Object.keys(vnode.props).forEach((key) => {
        if (key.startsWith('on')) {
          // 이벤트 핸들러
          const eventName = key.slice(2).toLowerCase();
          element.addEventListener(eventName, vnode.props[key]);
        } else if (key === 'className') {
          element.className = vnode.props[key];
        } else {
          element.setAttribute(key, vnode.props[key]);
        }
      });
    }

    // 자식 요소들 추가
    if (vnode.children) {
      vnode.children.forEach((child) => {
        const childElement = createDOMElement(child);
        if (childElement) {
          element.appendChild(childElement);
        }
      });
    }

    return element;
  }

  return null;
}

// 특정 DOM 요소에 렌더링
export function render(vnode, container) {
  // 기존 내용 제거
  container.innerHTML = '';

  // 새로운 DOM 요소 생성 및 추가
  const element = createDOMElement(vnode);
  if (element) {
    container.appendChild(element);
  }
}

// JSX 대신 사용할 헬퍼 함수 (h = hyperscript)
export const h = createElement;

 

build.js

더보기
import fs from 'fs';

function generateHTML() {
  return `
<!DOCTYPE html>
<html lang="ko">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>두 개 useState 테스트</title>
  <style>
    body {
      font-family: Arial, sans-serif;
      max-width: 600px;
      margin: 50px auto;
      padding: 20px;
      background: #f5f5f5;
    }
    .test-component {
      background: white;
      padding: 20px;
      border-radius: 8px;
      margin: 20px 0;
      box-shadow: 0 2px 4px rgba(0,0,0,0.1);
    }
    button {
      margin: 5px;
      padding: 8px 16px;
      border: none;
      border-radius: 4px;
      background: #007bff;
      color: white;
      cursor: pointer;
    }
  </style>
</head>
<body>
  <h1>두 개 useState 테스트 🧪</h1>
  
  <div id="root"></div>
  
  <script type="module">
    import { render, useState } from './lib/react.js';
    
    // 두 개의 useState를 사용하는 컴포넌트
    function DoubleStateComponent() {
      const [count, setCount] = useState(0);      // 첫 번째 useState
      const [name, setName] = useState('안녕');   // 두 번째 useState
      
      console.log('🔄 컴포넌트 실행 - count:', count, 'name:', name);
      
      return {
        tag: 'div',
        props: { className: 'test-component' },
        children: [
          {
            tag: 'h2',
            children: ['두 개 useState 테스트']
          },
          {
            tag: 'p',
            children: [\`카운트: \${count}\`]
          },
          {
            tag: 'p', 
            children: [\`이름: \${name}\`]
          },
          {
            tag: 'button',
            props: {
              onClick: () => {
                console.log('카운트 증가 버튼 클릭');
                setCount(count + 1);
              }
            },
            children: ['카운트 +1']
          },
          {
            tag: 'button',
            props: {
              onClick: () => {
                console.log('이름 변경 버튼 클릭');
                setName(name === '안녕' ? '바이' : '안녕');
              }
            },
            children: ['이름 변경']
          }
        ]
      };
    }
    
    // 렌더링
    const container = document.getElementById('root');
    render(DoubleStateComponent, container);
    
  </script>
</body>
</html>
  `;
}

const html = generateHTML();
fs.writeFileSync('index.html', html);
console.log('✅ 두 개 useState 테스트 파일 생성 완료!');

 

빌드후, 라이브서버 실행.

 

결과물

 

 

 

단계적으로 확인해보자.

 

liverServer가 index.html을 열고 파싱을 시작하며 body 하단의 script태그를 만나 JS모듈을 로드한다.

그리고 script태그의 첫줄인

import { render, useState } from './lib/react.js';

 

모듈 import문을 가져와 실행하게된다.

 

이때 lib/react.js에서는

// 전역 변수들이 초기화됨
let currentComponent = null;
let currentContainer = null;
let hookIndex = 0;        ← 0으로 초기화
let hooks = [];           ← 빈 배열로 초기화
🌐 전역 상태
┌─────────────────────────────────────────┐
│  hookIndex = 0                          │
│  hooks = []                             │
│  currentComponent = null                │
│  currentContainer = null                │
└─────────────────────────────────────────┘

다음과 같게 된다.

 

다시, builder.js에선  

function DoubleStateComponent() {
  // 아직 실행되지 않음, 그냥 정의만 됨
}

 

DoubleStateComponent가 정의가 되고, builder.js의 script태그 제일 하단에서

const container = document.getElementById('root'); // DOM 요소 찾기
render(DoubleStateComponent, container);           // 렌더링 시작!

 

상수 container를 정의하고 render함수를 실행하게된다.

 

 

lib/react.js의 render함수에서 그 하위의에 정해놓은 rerender를 호출하여 첫 렌더링로직이 시작된다.

export function render(component, container) {
  currentComponent = component;     // DoubleStateComponent 저장
  currentContainer = container;     // root div 저장
  
  rerender();                       // 첫 렌더링 시작!
}
🌐 전역 상태 업데이트
┌─────────────────────────────────────────┐
│  hookIndex = 0                          │
│  hooks = []                             │
│  currentComponent = DoubleStateComponent│
│  currentContainer = <div id="root">     │
└─────────────────────────────────────────┘

 

render함수 내 rerender호출 을 통해 rerender함수가 아래와 같이 실행된다.

function rerender() {
  if (!currentComponent || !currentContainer) return; // 통과!
  
  hookIndex = 0; // 훅 인덱스 리셋 (이미 0이지만)
  
  // 컴포넌트 실행해서 가상 DOM 생성
  const vnode = currentComponent(); // DoubleStateComponent() 호출!
  
  // 실제 DOM으로 렌더링
  domRender(vnode, currentContainer);
}

 

이렇게 되면 DoubleStateComponent()가 첫 실행이 된다.

const [count, setCount] = useState(0);

 

useState에 초기값 0을 넣어 실행하면 내부에선 다음과 같이 동작된다.

// 원본
let currentComponent = null;
let currentContainer = null;
let hookIndex = 0;
let hooks = [];

export function useState(initialValue) {
  const currentIndex = hookIndex++;

  if (hooks[currentIndex] === undefined) {
    hooks[currentIndex] = initialValue; // 클로저
  }

  const setState = (newValue) => {
    hooks[currentIndex] = newValue;
    rerender();
  };

  return [hooks[currentIndex], setState];
}
////////////////////////////////////////////////////////////////////////////////
// 주석설명
export function useState(initialValue) { // initialValue = 0
  const currentIndex = hookIndex++;      // currentIndex = 0, hookIndex = 1
  
  if (hooks[0] === undefined) {          // true! (빈 배열이니까)
    hooks[0] = initialValue;             // hooks[0] = 0
  }
  
  const setState = (newValue) => {
    hooks[0] = newValue;                 // 0번 위치 기억! (클로저)
    rerender();
  };
  
  return [hooks[0], setState];           // [0, setCount함수] 반환
}

 

전역변수 hookIndex는 증감연산자 중, 후위증가를 통해 currentIndex에 0을 할당하게된다.

그리고 빈배열인 hooks배열에 if 조건문을 통해 0번째 인덱스의 값을 undefined와 비교하여 hooks[0]에 0을 할당하게 된다.

 

그리고 다음 함수 setState를 선언하고 newValue라는 파라미터를 받아 이 hooks[0] 에 할당한다. 그리고 rerender()함수를 실행한다.

물론 setState함수는 실행되지않고 생성만 하게된다. (여기서 함수라는 개념을 짚자면 함수는 값 자체이다. 함수는 일급객체로 함수를 변수에 담을 수 있고(setState처럼 화살표함수를 변수에 할당하는 함수 표현식) useState처럼 setState라는 함수자체를 리턴할 수 있다)

 

그리고 setState함수는 자신이 만들어진 순간의 currentIndex를 평생 기억하게 된다. 이것이 Js의 클로저다.

closer란?

 정의로서는  함수와 함수가 선언된 어휘적 환경(lexical environment)의 조합을 의미한다.

저기 setSate함수는 자신이 만들어지는 순간의 currentIndex를 평생 기억하게 된다. 왜냐하면 const currentIndex는 useState 함수가 실행될 때마다 달라질 수 있는 지역 상수지만, setState는 그 함수가 만들어질 때의 currentIndex 값을 내부적으로 캡처(보존)해서, 훗날 setState()가 호출되더라도 그때의 currentIndex를 정확히 사용할 수 있는 것이다. 이는 자바스크립트에서 함수가 외부 스코프(lexical environment)의 변수에 접근할 수 있는 특징 때문이며, 그 접근 권한은 함수가 생성된 시점 기준으로 결정된다.

이것이 매우 중요한 개념인점이, 여기서 리엑트의 불변성 개념과도 상통하고, 이 클로저 + hookIndex++ 구조 때문에 늘 조건문 내부에서 리엑트훅을 사용하지 못하는 이유가 여기서 출발한다.

 캡처라는 말은 잘 알다시피 사진을 찍듯이 그 순간의 환경을 저장해두는 것이다. 위의 setState가 만들어지는 그 찰나에, 주 변에 있는 모든 변수들의 상태를 스냅샷처럼 찍어서 보관하게 되는 것을 의미한다. 그렇다면 왜 setState는 만들어지는 순간을 기억하게 되는지 의문이 생긴다.

 JS엔진이 함수를 생성과정을 요약하자면 

 

  • 함수 객체 생성: setState 함수 객체를 메모리에 만들고
  • 스코프 체인 설정: 이 함수가 접근할 수 있는 변수들의 "지도"를 만든다. 그리고
  • 변수 참조 저장: currentIndex 같은 외부 변수들의 주소를 함수 내부에 저장해두기 때문이다.

외부 변수들의 주소라 표현함은 그 변수에 할당된 값이 아닌 메모리 주소를 참조하고 있기 떄문이다. 즉, currentIndex라는 이름의 변수를 복사해오는 게 아니라, 그 변수가 어디 있는지를 기억하고 있는 것이다. 이 덕분에 React의 useState 같은 훅 시스템도 각 상태가 고유한 위치를 기억하고 독립적으로 동작할 수 있게 되는 거고, 이런 구조 때문에 조건문 안에서는 훅을 쓰면 안 되는 이유도 설명된다.

 

왜 조건문 내부에서 훅을 사용하면 안될까?

더보기

만약 조건문 내부에서 훅(useState같은)을 사용하게 된다면, 조건에 의하여 배열의 인덱스가 바뀌게 될 수 있기 때문이다. 배열의 인덱스는 hookIndex++로 결정되기 때문에 순서를 반드시 보장해야한다. 따라 리엑트가 컴포넌트를 렌더링할 때 hooks[] 배열의 인덱스를 보장하기 위해선 컴포넌트들의 훅이 항상 같은 순서로 실행되어야 한다.(어떤조건에선 실행되고 어떤 조건에선 실행안되면 안된다는 말이다.)

 

코드로 직접 보면, 아래의 myComponent로 예시를 들면 

function MyComponent() {
  const [a, setA] = useState(1);

  if (어떠한 조건) {
    const [b, setB] = useState(2); 
  }

  const [c, setC] = useState(3);
}

 

 

첫 번째 렌더링에 어떠한 조건이 true가 되어 조건문이 통과가 된다면 

 

  • useState(1) → hooks[0]
  • useState(2) → hooks[1]
  • useState(3) → hooks[2]
hooks = [
  1,         // ← hooks[0], 즉 a의 값
  2,         // ← hooks[1], 즉 b의 값
  3          // ← hooks[2], 즉 c의 값
]

 

이렇게 될 것이고, 


두 번쨰 렌더링 어떠한 조건이 false가 되어 조건문이 통과되지 않는다면

 

  • useState(1) → hooks[0]
  • useState(3) → ❌ 이제 useState(3)이 hooks[1]에 덮어써지게 된다.

이는 c가 b의 자리에 들어가서 상태가 꼬이게 된다. 말했듯이 훅의 인덱스 넘버는 currentIndex로 받기 떄문이다.

 

 

 

++) useState코드 구경

react 깃허브 

https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberHooks.js

 

 

 사실 앞서 내가 만든 미니useState보다 원본의 useState는 복잡한 시스템으로 연결되어있고 더욱 높은 성능과 기능, 그리고 안정성을 가지고 있다. 

 

Dispatch

 한마디로 정리하면 Dispatcher는 리엑트가 지금 어떤 상황인지 에 따라 다른 함수를 호출하는 스위치보드 같은 시스템이다. 훅은 4가지의 가장 큰 상황으로 현재 상황을 판단한다. 이 dispatch는 쉽게말하면 useState에서의 setState다. "이 상태를 이렇게 바꿔줘!" 라는 요청(액션)을 전달하는 함수이다.

 

앞서 내가 만든 useState와 원본을 비교해볼 떄, 나의 useState는 첫 렌더링이든 업데이트든 모두 동일한 로직이고 잘못사용하더라도 경고도 없다. 그러나 React에서의 useState는 이러한 모든경우를 고려하는 복잡한 시스템으로 구성된다.

 

 

1. 첫 렌더링 - HooksDispatcherOnMountInDEV

더보기
    // HooksDispatcherOnMountInDEV

useState<S>(
      initialState: (() => S) | S,
    ): [S, Dispatch<BasicStateAction<S>>] {
      currentHookNameInDev = 'useState'; // 🐛 디버깅: 현재 Hook 이름
      mountHookTypesDev();  // 🐛 Hook 타입 검사
      const prevDispatcher = ReactSharedInternals.H; // 🔒 현재 dispatcher 백업
      ReactSharedInternals.H = InvalidNestedHooksDispatcherOnMountInDEV; // 🔒 중첩 방지
      try {
        return mountState(initialState);   // 🎯 실제 Mount 로직
      } finally {
        ReactSharedInternals.H = prevDispatcher;  // 🔒 dispatcher 복원
      }
    }
    
    // HooksDispatcherOnMountWithHookTypesInDEV
    
useState<S>(
      initialState: (() => S) | S,
    ): [S, Dispatch<BasicStateAction<S>>] {
      currentHookNameInDev = 'useState';
      updateHookTypesDev();
      const prevDispatcher = ReactSharedInternals.H;
      ReactSharedInternals.H = InvalidNestedHooksDispatcherOnMountInDEV;
      try {
        return mountState(initialState);
      } finally {
        ReactSharedInternals.H = prevDispatcher;
      }
    }

2. 리 렌더링 - HooksDispatcherOnUpdateInDEV

더보기

 

// HooksDispatcherOnUpdateInDEV

useState<S>(
      initialState: (() => S) | S,
    ): [S, Dispatch<BasicStateAction<S>>] {
      currentHookNameInDev = 'useState';
      updateHookTypesDev();
      const prevDispatcher = ReactSharedInternals.H;
      ReactSharedInternals.H = InvalidNestedHooksDispatcherOnUpdateInDEV;
      try {
        return updateState(initialState);
      } finally {
        ReactSharedInternals.H = prevDispatcher;
      }
    }

 

 

3. 재 렌더링 - HooksDispatcherOnRerenderInDEV

더보기
// HooksDispatcherOnRerenderInDEV
    
    useState<S>(
      initialState: (() => S) | S,
    ): [S, Dispatch<BasicStateAction<S>>] {
      currentHookNameInDev = 'useState';
      updateHookTypesDev();
      const prevDispatcher = ReactSharedInternals.H;
      ReactSharedInternals.H = InvalidNestedHooksDispatcherOnRerenderInDEV;
      try {
        return rerenderState(initialState);
      } finally {
        ReactSharedInternals.H = prevDispatcher;
      }
    }

4. 잘못된 사용 - InvalidNestedHooksDispatcher

더보기
// InvalidNestedHooksDispatcherOnMountInDEV

useState<S>(
      initialState: (() => S) | S,
    ): [S, Dispatch<BasicStateAction<S>>] {
      currentHookNameInDev = 'useState';
      warnInvalidHookAccess();
      mountHookTypesDev();
      const prevDispatcher = ReactSharedInternals.H;
      ReactSharedInternals.H = InvalidNestedHooksDispatcherOnMountInDEV;
      try {
        return mountState(initialState);
      } finally {
        ReactSharedInternals.H = prevDispatcher;
      }
    }
    
  //  InvalidNestedHooksDispatcherOnUpdateInDEV
  
useState<S>(
      initialState: (() => S) | S,
    ): [S, Dispatch<BasicStateAction<S>>] {
      currentHookNameInDev = 'useState';
      warnInvalidHookAccess();
      updateHookTypesDev();
      const prevDispatcher = ReactSharedInternals.H;
      ReactSharedInternals.H = InvalidNestedHooksDispatcherOnUpdateInDEV;
      try {
        return updateState(initialState);
      } finally {
        ReactSharedInternals.H = prevDispatcher;
      }
    }
    
 // InvalidNestedHooksDispatcherOnRerenderInDEV
 
useState<S>(
      initialState: (() => S) | S,
    ): [S, Dispatch<BasicStateAction<S>>] {
      currentHookNameInDev = 'useState';
      warnInvalidHookAccess();
      updateHookTypesDev();
      const prevDispatcher = ReactSharedInternals.H;
      ReactSharedInternals.H = InvalidNestedHooksDispatcherOnUpdateInDEV;
      try {
        return rerenderState(initialState);
      } finally {
        ReactSharedInternals.H = prevDispatcher;
      }
    }

모든 훅

더보기
const ContextOnlyDispatcher: Dispatcher = {
  readContext,

  use,
  useCallback: throwInvalidHookError,
  useContext: throwInvalidHookError,
  useEffect: throwInvalidHookError,
  useImperativeHandle: throwInvalidHookError,
  useLayoutEffect: throwInvalidHookError,
  useInsertionEffect: throwInvalidHookError,
  useMemo: throwInvalidHookError,
  useReducer: throwInvalidHookError,
  useRef: throwInvalidHookError,
  useState: throwInvalidHookError,
  useDebugValue: throwInvalidHookError,
  useDeferredValue: throwInvalidHookError,
  useTransition: throwInvalidHookError,
  useSyncExternalStore: throwInvalidHookError,
  useId: throwInvalidHookError,
  useHostTransitionStatus: throwInvalidHookError,
  useFormState: throwInvalidHookError,
  useActionState: throwInvalidHookError,
  useOptimistic: throwInvalidHookError,
  useMemoCache: throwInvalidHookError,
  useCacheRefresh: throwInvalidHookError,
};

const HooksDispatcherOnMount: Dispatcher = {
  readContext,

  use,
  useCallback: mountCallback,
  useContext: readContext,
  useEffect: mountEffect,
  useImperativeHandle: mountImperativeHandle,
  useLayoutEffect: mountLayoutEffect,
  useInsertionEffect: mountInsertionEffect,
  useMemo: mountMemo,
  useReducer: mountReducer,
  useRef: mountRef,
  useState: mountState,
  useDebugValue: mountDebugValue,
  useDeferredValue: mountDeferredValue,
  useTransition: mountTransition,
  useSyncExternalStore: mountSyncExternalStore,
  useId: mountId,
  useHostTransitionStatus: useHostTransitionStatus,
  useFormState: mountActionState,
  useActionState: mountActionState,
  useOptimistic: mountOptimistic,
  useMemoCache,
  useCacheRefresh: mountRefresh,
};

const HooksDispatcherOnUpdate: Dispatcher = {
  readContext,

  use,
  useCallback: updateCallback,
  useContext: readContext,
  useEffect: updateEffect,
  useImperativeHandle: updateImperativeHandle,
  useInsertionEffect: updateInsertionEffect,
  useLayoutEffect: updateLayoutEffect,
  useMemo: updateMemo,
  useReducer: updateReducer,
  useRef: updateRef,
  useState: updateState,
  useDebugValue: updateDebugValue,
  useDeferredValue: updateDeferredValue,
  useTransition: updateTransition,
  useSyncExternalStore: updateSyncExternalStore,
  useId: updateId,
  useHostTransitionStatus: useHostTransitionStatus,
  useFormState: updateActionState,
  useActionState: updateActionState,
  useOptimistic: updateOptimistic,
  useMemoCache,
  useCacheRefresh: updateRefresh,
};

const HooksDispatcherOnRerender: Dispatcher = {
  readContext,

  use,
  useCallback: updateCallback,
  useContext: readContext,
  useEffect: updateEffect,
  useImperativeHandle: updateImperativeHandle,
  useInsertionEffect: updateInsertionEffect,
  useLayoutEffect: updateLayoutEffect,
  useMemo: updateMemo,
  useReducer: rerenderReducer,
  useRef: updateRef,
  useState: rerenderState,
  useDebugValue: updateDebugValue,
  useDeferredValue: rerenderDeferredValue,
  useTransition: rerenderTransition,
  useSyncExternalStore: updateSyncExternalStore,
  useId: updateId,
  useHostTransitionStatus: useHostTransitionStatus,
  useFormState: rerenderActionState,
  useActionState: rerenderActionState,
  useOptimistic: rerenderOptimistic,
  useMemoCache,
  useCacheRefresh: updateRefresh,
};

 

내코드와 비교하면 어마무시하게 차이가 나는걸 느꼈다.

기능 내 코드 React 코드
기본 useState ✅ 동작 ✅ 완벽
초기화 함수 ✅ useState(() => init())
함수형 업데이트 ✅ setState(prev => prev + 1)
Object.is 최적화 ✅ 같은 값 스킵
에러 처리 ✅ 7가지 상황별
HOok 규칙 검증 ✅ 개발모드 경고
업데이트 배치 ✅ 여러 setState 묶음
우선순위 시스템 ✅ Lane 기반
메모리 관리 ❌ 수동 ✅ 자동
디버깅 지 ✅ 개발자 도구

 

 

내가 얻은 것

useState를 사용하여 단순히 상태를 저장하고 바꾸는 함수라고만 생각했으나 직접 구현해보니, 그 안에는 많은것들이 숨겨져 있었다.

 

 const [state, setState] = useState(0)

 

이코드 한줄이 이제는

  •  클로저를 이용한 상태 격리
  • 7가지 상황별 dispatcher 시스템
  • Fiber 기반 메모리 관리
  • 우선순위 기반 업데이트 스케줄링
  • Object.is 최적화
  • 에러 복구 시스템
  • 개발자 친화적 디버깅 도구

이렇게 확대되어 보인다. 얼마나 새심하고 모든관점에서 치밀하게 설계된건지 감이조금 잡혔다.

사실 단순히 어떻게 상태를 관리하는가 에 대한 물음을 시작으로 간단한 코드를 구현해보고 원본을 보니 또 거대한 벽앞에 서있는 기분이다. 갈길이 멀구나 싶다.

728x90