GitHub Pages
자바스크립트 프록시로 초경량 반응형 상태 관리 구현하기
Proxy 의 set 트랩만으로 "상태 변경 → UI 업데이트" 자동화를 구현하는 패턴. 미디어 폼 예제로 라이브러리 없이 반응성을 만든다.
핵심 요약
구성: ① 원본 상태 객체, ② set 트랩이 정의된 핸들러, ③ new Proxy(state, handler) 로 만든 반응형 객체. 사용처에서 proxy.foo = x 하면 트랩이 발동해 ① 실제 값 갱신, ② 등록된 UI 업데이트 함수들 실행. 추가로 공개 API 함수(addPhoto, setVideo, reorderPhotos) 와 상태 조회 모듈(StateQueries) 로 캡슐화. 장점: 의존성 0, 보일러플레이트 거의 없음, 관심사 분리. 단점: 깊은 객체 nested 변경, 비동기/effect 의존 추적은 라이브러리 수준이 아님.
반응성(reactive)의 본질은 "값이 바뀔 때 부수효과를 자동 실행" 이다. Proxy 의 set 트랩은 이 부수효과를 할당 연산자에 정확히 한 번 끼워 넣는 가장 작은 단위의 후크다. Vue/Mobx/Solid 같은 라이브러리도 같은 원리다.
면접에서 "상태 관리 라이브러리 없이 반응성 어떻게 만들지?" 또는 "Vue 의 ref 는 어떻게 동작하나" 류 질문이 자주 나온다. Proxy set 트랩 한 단락만 정확히 이해해도 Vue 3 의 reactive , signal 라이브러리 의 코어를 같은 메커니즘으로 설명할 수 있다.
학습 포인트
면접 답변으로 연결할 학습 포인트입니다.
`Proxy` 의 `set` 트랩은 "할당 연산자 = 에 끼어드는 후크"
const handler = {
set(target, prop, value) {
target[prop] = value;
triggerUpdates();
return true;
},
};
const state = new Proxy(raw, handler);
state.foo = 1; // ← 트랩 발동
set 은 반드시 true 반환(strict mode 에서 오류 방지). 이 한 패턴이 모든 reactive 라이브러리의 코어다.
return true 누락. strict mode 에서 TypeError: 'set' on proxy: trap returned falsish 발생.
공개 API 와 직접 변경의 분리 — 왜 `addPhoto` 같은 함수가 필요한가
// ❌ 호출처에 흩뿌려진 직접 변경
state.photos['1'] = url;
state.mediaType = 'photo';
// ✅ 의도가 드러나는 공개 API
addPhoto(url);
공개 API 가 도메인 액션 을 명시하면 트랩 + 액션 함수만 보면 "상태가 어디서·왜 바뀌나" 를 추적 가능. Redux 의 action 과 같은 역할.
Proxy 만 만들고 호출처에서 마음대로 mutate. 결과적으로 디버깅 시 "누가 photos[2] 바꿨지" 를 grep 해야 한다.
한계 — 깊은 nested 변경과 동적 의존성 추적
기본 set 트랩은 얕은(shallow) 트랩만 잡는다. state.photos['1'] = url 은 photos 객체에 대한 set 이 아니라 그 안 객체의 '1' 키 set 이라 트랩이 발동하지 않을 수도 있다(아래 reorderPhotos 처럼 photos 자체 재할당이 트리거되는 형태로 우회). 깊은 변경 추적·의존성 자동 추적은 Vue/Mobx 처럼 재귀 Proxy + 의존성 그래프 가 필요하다.
단순 패턴으로 "리덕스 대체" 라고 단정. 실제론 간단한 폼 상태 수준에 적합하다.
읽는 순서
- 1이론
MDN Proxy 트랩 목록을 훑고,
set/get/has/deleteProperty가 각각 어떤 연산을 가로채는지 표로 정리. - 2구현
본문 미디어 폼 예제를 직접 작성. nested 변경(
state.photos.1 = url) 이 트랩에 잡히는지 확인하고 안 잡히는 케이스를 찾는다. - 3실무
현재 프로젝트의 작은 폼/모달 상태 한 곳을 골라 라이브러리 없이 이 패턴으로 치환 가능한지 평가. 가능하면 의존성 1개를 없애 본다.
- 4설명
"Vue 3 ref 는 내부적으로 어떻게 동작하나" 를 본 패턴에서 출발해 5분 안에 설명. 기본 Proxy → 재귀 → effect dependency 순으로.
면접 연결 질문
[감점 답변] "Proxy 가 알아서 다 잡아 준다". [좋은 답변] 기본은 shallow 라 state.user.name = x 같은 nested set 은 잡히지 않는다. 해법은 get 트랩에서 nested 값을 다시 Proxy 로 감싸 반환 하는 재귀 Proxy. Vue 3 가 이 패턴이다.
[좋은 답변] defineProperty 는 이미 존재하는 키만 가로챔 → 새 키 추가/삭제 불가. Proxy 는 임의 키, 메서드 호출, has/deleteProperty 까지 잡는다. 그래서 Vue 2 → 3 로 옮기면서 Proxy 로 갈아탔다.
[좋은 답변] 깊은 nested 상태, 비동기/derived state, 시간여행 디버깅이 필요하면 Zustand/Jotai/Redux Toolkit 권장. 본문 패턴은 간단한 폼/UI 상태 까지.
자기 점검
"별일 없다" — 실제론 strict mode 에서 throw 한다.
"동일하다" — Vue 3 는 재귀 Proxy + 자동 의존성 추적 이라는 두 단계가 더 있다.