Proxy

const target = { x: 10, y: 20 };

const handler = { 
	get: function(obj, prop) {
		console.log("속성에 접근했습니다.")
		return obj[prop];
	}
}

let proxy = new Proxy(target, handler);
console.log(proxy.x); // "속성에 접근했습니다." 출력 후 10 반환

Proxy 메모리할당은?

Proxy 사용 사례 - immer

import produce from 'immer';

const baseState = [
  { title: "Learn TypeScript", done: true },
  { title: "Try Immer", done: false }
];

const nextState = produce(baseState, draft => {
  draft[1].done = true;
  draft.push({ title: "Tweet about it" });
});
function produce(baseState, recipe) {
  const draft = createProxy(baseState); // 여기서 프록시를 만든다.
  // 프록시로 만드는 이유는 아래에 createProxy 에서
  // set 메서드 실행 시, 변경사항을추적하기 위함임.
  const changes = [];   // set 에 가해진 변경사항들은 요 changes 배열에 모두 저장된다.

  const recordChange = (path, value) => {
    changes.push({ path, value });
  };

// 그리고 handler 함수가 실행되고, draft (프록시 객체)를 인자로 넘긴다.
  recipe(draft);
  // 고러면 요 handler 함수에서는 알아서 쿵짝쿵짝 draft 객체 변경하고 이러쿵저러쿵 한다.
  // draft 객체 내 속성을 변경했다면 위의 chnages 배열에 담겼을거고

// 마지막으로 원본 객체에 변경사항들을 적용한다.
  return applyChanges(baseState, changes);
}

function createProxy(target) {
  return new Proxy(target, {
    get(target, prop) {
      // 속성 접근 로직
    },
    set(target, prop, value) {
      // 속성 설정 로직 및 변경 기록
      recordChange([prop], value); // 🎃 변경 기록
      return true; // 🎃 실제로 해당 원본객체를 변경하지는 않는다 (target[prop] 변경X)
    }
    // 기타 필요한 트랩들...
  });
}

function applyChanges(baseState, changes) {
 // 🎃 원본 객체와 변경사항을 기반으로 아예 새로운 객체를 만든다.
  const newState = Array.isArray(baseState) ? [...baseState] : {...baseState};
  
  for (const change of changes) {
    let current = newState;
    const { path, value } = change;
    
    // 마지막 속성 전까지 순회
    for (let i = 0; i < path.length - 1; i++) {
      const key = path[i];
      if (!(key in current)) {
        current[key] = typeof path[i + 1] === 'number' ? [] : {};
      }
      current = current[key];
    }
    
    // 마지막 속성에 값 할당
    const lastKey = path[path.length - 1];
    if (value === undefined) {
      if (Array.isArray(current)) {
        current.splice(lastKey, 1);
      } else {
        delete current[lastKey];
      }
    } else {
      current[lastKey] = value;
    }
  }
  
  return newState;
}