요즘IT
위시켓
AIDP
콘텐츠프로덕트 밸리
요즘 작가들컬렉션물어봐
놀이터
콘텐츠
프로덕트 밸리
요즘 작가들
컬렉션
물어봐
놀이터
새로 나온
인기
개발
AI
IT서비스
기획
디자인
비즈니스
프로덕트
커리어
트렌드
스타트업
서비스 전체보기
위시켓요즘ITAIDP
고객 문의
02-6925-4867
10:00-18:00주말·공휴일 제외
yozm_help@wishket.com
요즘IT
요즘IT 소개작가 지원
기타 문의
콘텐츠 제안하기광고 상품 보기
요즘IT 슬랙봇크롬 확장 프로그램
이용약관
개인정보 처리방침
청소년보호정책
㈜위시켓
대표이사 : 박우범
서울특별시 강남구 테헤란로 211 3층 ㈜위시켓
사업자등록번호 : 209-81-57303
통신판매업신고 : 제2018-서울강남-02337 호
직업정보제공사업 신고번호 : J1200020180019
제호 : 요즘IT
발행인 : 박우범
편집인 : 노희선
청소년보호책임자 : 박우범
인터넷신문등록번호 : 서울,아54129
등록일 : 2022년 01월 23일
발행일 : 2021년 01월 10일
© 2013 Wishket Corp.
로그인
요즘IT 소개
콘텐츠 제안하기
광고 상품 보기
개발

자바스크립트 Proxy와 Reflect, 객체를 다루는 새로운 방법

효빈
7분
1시간 전
102
에디터가 직접 고른 실무 인사이트 매주 목요일에 만나요.
newsletter_profile0명 뉴스레터 구독 중

자바스크립트에서 객체를 다루다 보면, 프로퍼티에 접근하거나 값을 변경할 때 “자동으로 추가 동작을 실행”하고 싶은 순간이 있습니다. 예를 들어, 객체의 값이 바뀔 때마다 로그를 남기거나, 유효하지 않은 값이 들어오면 막아버리거나, 값이 바뀌면 화면을 자동으로 갱신하고 싶은 경우가 대표적이죠.

 

이전 글에서 살펴본 내부 메서드는 자바스크립트 엔진이 내부적으로 사용하는 동작이라, 개발자가 직접 가로채거나 변경할 수 없었는데요. Proxy를 사용하면 바로 이 내부 동작을 가로채서 원하는 로직을 끼워 넣을 수 있고, Reflect는 그 과정에서 원래의 기본 동작을 안전하게 수행할 수 있도록 도와줍니다. 이번 글에서는 Proxy와 Reflect가 무엇인지, 그리고 실무에서는 어떻게 활용되는지까지 함께 살펴보겠습니다.

 

미리 요점만 콕 집어보면?

  • Proxy는 객체의 기본 동작을 가로채서 커스텀 로직을 추가할 수 있는 도구이고, Reflect는 원래 동작을 안전하게 수행하도록 돕는 짝꿍입니다.
  • get 트랩과 set 트랩을 통해 프로퍼티 읽기와 쓰기를 가로채 유효성 검사나 반환 값 제어가 가능하며, 트랩은 내부 메서드와 상당 부분 1:1로 대응하도록 설계되어 있습니다.
  • 트랩 안에서 Reflect를 통해 원래 동작을 위임하는 것이 가장 안전하고 권장되는 패턴이며, 성능 오버헤드와 일부 내장 객체와의 호환성 문제에 주의해야 합니다.
 

Proxy란?

Proxy는 영어로 “대리인”이라는 뜻을 가지고 있는데요, 자바스크립트에서의 Proxy도 비슷합니다. 어떤 객체에 대한 작업을 직접 처리하는 대신, 중간에 대리인을 두고 그 대리인이 작업을 가로채서 추가적인 동작을 수행할 수 있게 해주는 객체입니다.

 

Proxy 동작 원리
<출처: 작가, Claude로 생성>

 

1) Proxy의 기본 문법

Proxy를 이해하려면 세 가지 핵심 용어를 알아야 합니다. target은 Proxy가 감는 원본 객체, handler는 가로채는 동작을 정의하는 객체, 그리고 trap은 handler 안에 정의하는 메서드로, 말 그대로 특정 동작을 가로채는 “함정”입니다.

 

const user = {
  name: "홍길동",
  age: 27,
};

const handler = {
  // 여기에 트랩(Trap)을 정의합니다
};

const proxy = new Proxy(user, handler);

 

handler에 아무런 트랩도 정의하지 않으면, Proxy는 원본 객체에 대한 동작을 그대로 통과시킵니다.

 

2) 간단한 Proxy 예제

아래 코드는 get 트랩을 사용해서, 프로퍼티를 읽을 때마다 로그를 남기는 예제입니다.

 

const user = {
  name: "홍길동",
  age: 27,
};

const handler = {
  get(target, prop) {
    console.log(`${prop} 프로퍼티에 접근했습니다.`);
    return target[prop];
  },
};

const proxy = new Proxy(user, handler);

console.log(proxy.name);
// "name 프로퍼티에 접근했습니다."
// "홍길동"

 

get 트랩의 첫 번째 매개변수 target은 원본 객체, 두 번째 prop은 접근하려는 프로퍼티의 이름입니다. 이렇게 Proxy를 사용하면, 객체 접근 자체를 가로채서 원하는 로직을 추가할 수 있습니다.

 

 

주요 트랩(Trap) 살펴보기

Proxy에서 사용할 수 있는 트랩은 get 외에도 다양합니다. 프로퍼티를 읽을 때, 쓸 때, 삭제할 때 등 객체에 대한 거의 모든 동작을 가로챌 수 있는데요, 실무에서 자주 사용되는 트랩들을 살펴보겠습니다.

 

1) get 트랩: 프로퍼티 읽기 가로채기

get 트랩은 객체의 프로퍼티를 읽는 동작을 가로채는 트랩입니다. 앞서 간단한 로그 예제를 살펴보았는데요, get 트랩의 핵심은 프로퍼티를 읽을 때 원래 값을 그대로 반환하는 것이 아니라, 개발자가 원하는 방식으로 반환 값을 가공하거나 제어할 수 있다는 점입니다.

 

예를 들어, 존재하지 않는 프로퍼티에 접근했을 때 undefined 대신 의미 있는 메시지를 반환하도록 만들어 볼 수 있습니다.

 

const user = {
  name: "홍길동",
  age: 27,
};

const handler = {
  get(target, prop) {
    if (prop in target) {
      return target[prop];
    }
    return `"${prop}" 프로퍼티는 존재하지 않습니다.`;
  },
};

const proxy = new Proxy(user, handler);

console.log(proxy.name);    // "홍길동"

 

일반 객체였다면 proxy.address는 undefined를 반환했을 텐데요, get 트랩 덕분에 프로퍼티 읽기 동작 자체를 가로채서, 원하는 방식으로 결과를 바꿀 수 있게 된 것입니다.

 

2) set 트랩: 프로퍼티 쓰기 가로채기

set 트랩은 객체의 프로퍼티에 값을 할당하는 동작을 가로채는 트랩입니다. get 트랩이 읽기를 가로챘다면, set 트랩은 쓰기를 가로채는 것이죠. 값이 저장되기 전에 원하는 로직을 먼저 실행할 수 있기 때문에, 대표적으로 유효성 검사에 활용됩니다.

 

const user = {
  name: "홍길동",
  age: 27,
};

const handler = {
  set(target, prop, value) {
    if (prop === "age" && (typeof value !== "number" || value < 0)) {
      throw new Error("나이는 0 이상의 숫자여야 합니다.");
    }
    target[prop] = value;
    return true;
  },
};

const proxy = 

 

set 트랩은 target, prop, value 세 가지 매개변수를 받으며, 반드시 true 또는 false를 반환해야 합니다.

 

3) 트랩과 내부 메서드의 대응 관계

앞서 내부 슬롯과 내부 메서드를 다룰 때, 자바스크립트 엔진이 객체를 처리하기 위해 호출하는 내부 메서드들을 살펴보았는데요, 사실 Proxy의 트랩은 프로퍼티 접근/할당/삭제처럼 객체의 기본 동작을 수행하는 내부 메서드와 상당 부분 1:1로 대응하도록 설계되어 있습니다.

 

트랩과 내부 메서드
<출처: 작가, GPT로 생성>

 

즉, Proxy는 자바스크립트 엔진이 내부적으로 수행하는 동작을 개발자가 가로챌 수 있도록 열어주는 공식적인 방법이라고 할 수 있습니다.

 

 

Reflect란?

트랩 안에서 “원래 동작을 그대로 수행”해야 하는 경우가 자주 있는데요, 이때 사용하는 것이 바로 Reflect입니다. Reflect는 자바스크립트의 내장 객체로, 객체에 대한 기본 동작을 메서드 형태로 제공합니다. 메서드 이름은 Proxy의 트랩 이름과 정확히 동일합니다.

 

target vs Reflect 차이점
<출처 : 작가, Claude로 생성>

 

1) Reflect가 필요한 이유

“트랩 안에서 target[prop]처럼 직접 접근하면 되지 않나?”라고 생각할 수 있는데요, 간단한 경우에는 문제가 없지만, 상속 관계에서 문제가 발생합니다.

 

const parent = {
  _name: "부모",
  get name() {
    return this._name;
  },
};

const handler = {
  get(target, prop, receiver) {
    return target[prop]; // Reflect를 사용하지 않음
  },
};

const proxy = new Proxy(parent, handler);
const child = Object.create(proxy);
child._name = "자식";

console.log(child.name); 

 

target[prop]을 사용하면 getter 안의 this가 parent를 가리키게 되어, child._name이 아닌 parent._name이 반환됩니다.

 

2) Reflect로 해결하기

Reflect.get()의 세 번째 인자 receiver는 getter 안에서 this로 사용될 객체를 지정합니다.

 

const handler = {
  get(target, prop, receiver) {
    return Reflect.get(target, prop, receiver);
  },
};

const proxy = new Proxy(parent, handler);
const child = Object.create(proxy);
child._name = "자식";

console.log(child.name); // "자식" (정상 동작)

 

이처럼 트랩 안에서 원하는 로직을 수행한 후, Reflect를 통해 원래 동작을 위임하는 것이 가장 안전하고 권장되는 패턴입니다.

 

 

실무에서 Proxy는 어떻게 활용될까?

Proxy는 단순히 개념적인 도구가 아니라, 실제 프론트엔드 프레임워크에서도 핵심적으로 사용되고 있습니다. 대표적인 활용 사례를 살펴보겠습니다.

 

1) 데이터 변경 감지와 반응형 시스템

가장 대표적인 활용 사례가 바로 반응형 시스템입니다. Vue 3의 reactive()는 내부적으로 Proxy를 기반으로 구현되어 있는데요. 원리를 간단하게 살펴보면, set 트랩에서 값 변경을 감지하고 화면 갱신 로직을 실행하는 구조입니다.

 

function reactive(target) {
  return new Proxy(target, {
    set(target, prop, value, receiver) {
      const result = Reflect.set(target, prop, value, receiver);
      console.log(`${prop} 값이 변경되었습니다. 화면을 갱신합니다.`);
      return result;
    },
  });
}

const state = reactive({ count: 0 });
state.count = 1;
// "count 값이 변경되었습니다. 화면을 갱신합니다."

 

Vue 3가 이전 버전에서도 사용하던 Object.defineProperty() 대신 Proxy를 선택한 이유도, Proxy가 객체에 대한 대부분의 기본 동작을 가로챌 수 있어 더 유연하고 강력하기 때문입니다.

 

2) Proxy 사용 시 주의할 점

Proxy는 강력한 도구이지만, 몇 가지 주의할 점이 있습니다.

 

  • 첫째, 성능 오버헤드입니다. 트랩이 호출될 때마다 추가 함수 실행이 발생하므로, 대량의 데이터를 반복 처리하는 코드에서는 신중하게 사용해야 합니다.
  • 둘째, 일부 내장 객체와의 호환성 문제입니다. Map, Set, Date처럼 내부 슬롯에 의존하는 객체는 Proxy로 감싸면 정상 작동하지 않을 수 있으므로, 별도의 처리가 필요합니다.

 

 

마치며

지금까지 자바스크립트의 Proxy와 Reflect에 대해 살펴보았습니다. Proxy는 객체의 기본 동작을 가로채서 커스텀 로직을 추가할 수 있는 도구이고, Reflect는 그 과정에서 원래 동작을 안전하게 수행하도록 돕는 짝꿍입니다. 

 

특히 Proxy의 트랩은 자바스크립트 엔진의 내부 메서드와 상당 부분 1:1로 대응되기 때문에, 이전에 살펴본 내부 슬롯과 내부 메서드의 개념을 함께 이해하고 있다면 훨씬 깊이 있게 활용할 수 있습니다. Proxy는 이미 우리가 사용하는 프레임워크 곳곳에서 활용되고 있는데요, 이번 글을 통해 Proxy와 Reflect의 기본 원리를 이해했다면, 다음에는 직접 활용해 보시길 바랍니다.

 

©️요즘IT의 모든 콘텐츠는 저작권법의 보호를 받는 바, 무단 전재와 복사, 배포 등을 금합니다.

console
.log(proxy.address);
// "address" 프로퍼티는 존재하지 않습니다.
new
Proxy
(user, handler);
proxy.age =
30
;
// 정상 동작
proxy.age = -
5
;
// Error: 나이는 0 이상의 숫자여야 합니다.
// "부모" (기대값: "자식")