자바스크립트의 Proxy
와 Reflect
는 객체의 동작을 가로채고 제어하는 강력한 도구입니다. 이 두 기능을 함께 사용하면 객체의 기본 동작을 커스터마이즈하고, 필요한 경우 원래의 동작을 유지할 수 있습니다.
Proxy
Proxy
객체는 다른 객체의 기본 동작(속성 접근, 할당, 순회, 함수 호출 등)을 가로채고 재정의할 수 있도록 합니다. 이를 통해 객체의 동작을 제어하고 수정할 수 있습니다.
Proxy 생성
Proxy
는 두 개의 매개변수를 받습니다:
target
: 프록시할 기본 객체handler
: 기본 동작을 가로채기 위해 정의된 트랩(traps)들의 집합
const proxy = new Proxy(target, handler);
예제
let target = { message: "Hello, world!" };
let handler = {
get: function(target, property) {
if (property === 'message') {
return target[property].toUpperCase();
}
return target[property];
}
};
let proxy = new Proxy(target, handler);
console.log(proxy.message); // "HELLO, WORLD!"
주요 트랩
get
: 속성 접근을 가로챕니다.
get(target, property, receiver) {
// target: 원본 객체
// property: 접근하려는 속성
// receiver: 프록시 또는 상속 객체
}
set
: 속성 할당을 가로챕니다.
set(target, property, value, receiver) {
// target: 원본 객체
// property: 설정하려는 속성
// value: 설정하려는 값
// receiver: 프록시 또는 상속 객체
}
has
:in
연산자를 가로챕니다.
has(target, property) {
// target: 원본 객체
// property: 검사하려는 속성
}
deleteProperty
:delete
연산자를 가로챕니다.
deleteProperty(target, property) {
// target: 원본 객체
// property: 삭제하려는 속성
}
apply
: 함수 호출을 가로챕니다.
apply(target, thisArg, argumentsList) {
// target: 원본 함수 객체
// thisArg: 호출 시 this 값
// argumentsList: 호출 시 전달된 인수 목록
}
construct
:new
연산자를 가로챕니다.
construct(target, argumentsList, newTarget) {
// target: 원본 생성자 함수
// argumentsList: 생성자 함수에 전달된 인수 목록
// newTarget: 새로운 인스턴스의 prototype 객체
}
Reflect
Reflect
객체는 자바스크립트의 메타프로그래밍 API를 단순화하고 일관성 있게 제공하는 표준 빌트인 객체입니다. Reflect
는 객체의 기본 동작을 수행하는 정적 메서드들을 제공합니다. Reflect
의 메서드는 Proxy
트랩과 동일한 이름을 가지고 있으며, 동일한 작업을 수행합니다.
Reflect의 주요 메서드
Reflect.get
: 속성 접근
Reflect.get(target, property, receiver);
Reflect.set
: 속성 할당
Reflect.set(target, property, value, receiver);
Reflect.has
: 속성 존재 여부 확인
Reflect.has(target, property);
Reflect.deleteProperty
: 속성 삭제
Reflect.deleteProperty(target, property);
Reflect.apply
: 함수 호출
Reflect.apply(target, thisArg, argumentsList);
Reflect.construct
: 생성자 호출
Reflect.construct(target, argumentsList, newTarget);
예제
Proxy
와 Reflect
를 함께 사용하는 예제입니다.
let target = { message1: "hello", message2: "everyone" };
let handler = {
get: function(target, property) {
console.log(`Property '${property}' has been accessed`);
return Reflect.get(target, property);
},
set: function(target, property, value) {
console.log(`Property '${property}' has been set to '${value}'`);
return Reflect.set(target, property, value);
}
};
let proxy = new Proxy(target, handler);
console.log(proxy.message1); // "hello"
// 콘솔: Property 'message1' has been accessed
proxy.message2 = "world";
// 콘솔: Property 'message2' has been set to 'world'
console.log(proxy.message2); // "world"
// 콘솔: Property 'message2' has been accessed
실용적인 사용 사례
데이터 검증
Proxy
를 사용하여 객체에 할당되는 데이터의 유효성을 검사할 수 있습니다.
let person = { name: "", age: 0 };
let handler = {
set: function(target, property, value) {
if (property === "age") {
if (typeof value !== "number" || value <= 0) {
throw new TypeError("Age must be a positive number");
}
}
return Reflect.set(target, property, value);
}
};
let proxyPerson = new Proxy(person, handler);
proxyPerson.age = 25; // 정상
console.log(proxyPerson.age); // 25
try {
proxyPerson.age = -5; // 오류 발생
} catch (e) {
console.error(e.message); // "Age must be a positive number"
}
속성 접근 로깅
Proxy
를 사용하여 객체의 속성 접근을 로깅할 수 있습니다.
let user = { name: "Alice", email: "alice@example.com" };
let handler = {
get: function(target, property) {
console.log(`Accessing property '${property}'`);
return Reflect.get(target, property);
}
};
let proxyUser = new Proxy(user, handler);
console.log(proxyUser.name); // "Alice"
// 콘솔: Accessing property 'name'
console.log(proxyUser.email); // "alice@example.com"
// 콘솔: Accessing property 'email'
요약
Proxy
와 Reflect
는 객체의 동작을 가로채고 제어하는 강력한 도구입니다. Proxy
를 사용하여 객체의 기본 동작을 재정의하고, Reflect
를 사용하여 원본 객체의 기본 동작을 호출할 수 있습니다. 이를 통해 객체의 동작을 맞춤 설정하거나 새로운 기능을 추가하는 등 다양한 활용이 가능합니다.