Proxy
: 특정 객체를 감싸 프로퍼티 읽기, 쓰기와 같은 객체에 가해지는 작업을 중간에서 가로채는 객체Proxy
자체에서 처리되기도 하고, 원래 객체가 처리하도록 그대로 전달되기도함⇒ 문법
let proxy = new Proxy(target, handler)
target
– 감싸게 될 객체로, 함수를 포함한 모든 객체가 가능합니다.handler
– 동작을 가로채는 메서드인 '트랩(trap)'이 담긴 객체로, 여기서 프락시를 설정합니다(예시: get
트랩은 target
의 프로퍼티를 읽을 때, set
트랩은 target
의 프로퍼티를 쓸 때 활성화됨).proxy
에 작업이 가해지고, handler
에 작업과 상응하는 트랩이 있으면 트랩이 실행되어 프락시가 이 작업을 처리할 기회를 얻게 됨target
에 작업이 직접 수행됨⇒ 트랩이 없는 프락시를 사용한 예시
let target = {};
let proxy = new Proxy(target, {}); // 빈 핸들러
proxy.test = 5; // 프락시에 값을 씁니다. -- (1)
alert(target.test); // 5, target에 새로운 프로퍼티가 생겼네요!
alert(proxy.test); // 5, 프락시를 사용해 값을 읽을 수도 있습니다. -- (2)
for(let key in proxy) alert(key); // test, 반복도 잘 동작합니다. -- (3)
위 예시의 프락시엔 트랩이 없기 때문에 proxy
에 가해지는 모든 작업은 target
에 전달됨
proxy.test=
를 이용해 값을 쓰면 target
에 새로운 값이 설정됨proxy.test
를 이용해 값을 읽으면 target
에서 값을 읽어옴proxy
를 대상으로 반복 작업을 하면 target
에 저장된 값이 반환됨proxy
는 target
을 둘러싸는 투명한 래퍼가 됨Proxy
는 일반 객체와는 다른 행동 양상을 보이는 '특수 객체(exotic object)'임handler
가 비어있으면 Proxy
에 가해지는 작업은 target
에 곧바로 전달됨⇒ 트랩을 사용해 가로챌 수 있는 작업
⇒ get 트랩으로 프로퍼티 기본값 설정하기
프로퍼티 읽기를 가로채려면 handler에 get(target, property, receiver) 메서드가 있어야 합니다.
get메서드는 프로퍼티를 읽으려고 할 때 작동합니다. 인수는 다음과 같습니다.
let numbers = [1, 3, 5];
numbers = new Proxy(numbers, {
get(target, prop) {
if (prop in target) {
return target[prop];
} else {
return 0; // 기본값
}
}
});
console.log(numbers) // [ 1, 3, 5 ]
console.log( numbers[1] ); // 3
console.log( numbers[123] ); // 0 (해당하는 요소가 배열에 없으므로 0이 반환됨)
// 변수에 키가 있는 것은 값을 반환, 없는 것은 키를 반환
let words = {
'company' : 'ABC',
'name' : 'PCY',
'age' : 25
};
words = new Proxy(words, {
get(target, prop){
if (prop in target) return target[prop];
else return prop;
}
});
console.log(words); // { company: 'ABC', name: 'PCY', age: 25 }
console.log(words['name']); // PCY >>> proxy에 있으므로 값을 반환
console.log(words['live']); // live >>> proxy에 없으므로 키워드를 반환
⇒ set 트랩으로 프로퍼티 값 검증하기
set(target, property, value, receiver):
구현해야 할 set 트랩은 숫자형 값을 설정하려 할 때만 true를,
그렇지 않은 경우엔(TypeError가 트리거되고) false를 반환
set 트랩을 사용해 배열에 추가하려는 값이 숫자형인지 검증
let nums = [];
nums = new Proxy(nums, {
set(target, prop, val){ // 프로퍼티에 값을 쓰는 동작을 가로챕니다.
if (typeof val == 'number'){
target[prop] = val;
return true; // 반드시 set트랩 사용시 true를 반환해야 한다!
}
else {
return false;
}
}
});
nums.push(3);
nums.push(-5);
nums.push(1);
nums.push(7);
nums.push(-8);
console.log("Length is: " + nums.length); // Length is: 5
nums.push('hello'); // TypeError: 'set' on proxy: trap returned falsish for property '5'
// nums와 numsMig는 서로 push한 값을 공유함...
let numsMig = nums;
console.log(numsMig);
nums.push(100);
console.log("nums = " + nums);
console.log("numsMig = " + numsMig);
numsMig.push(200);
console.log("nums = " + nums);
console.log("numsMig = " + numsMig);
⇒ 1번
/* 과제 1번 : 존재하지 않는 프로퍼티를 읽으려고 할 때 에러 던지기
존재하지 않는 프로퍼티 값을 읽으려고 하면 보통은 undefined가 반환됩니다.
undefined 대신 에러를 던지는 프락시를 만들어봅시다.
이렇게 해 놓으면 프로그래밍 중에 저지르는 실수를 미연에 방지할 수 있을 겁니다.
객체 target을 받는 함수 wrap(target)를 만들고 위에서 언급한 기능을 구현하여
함수 wrap(target)이 프락시를 반환하도록 해보세요.
함수는 아래와 같이 동작해야 합니다.
let user = {
name: "John"
};
function wrap(target) {
return new Proxy(target, {
// 여기에 코드를 작성하세요.
});
}
user = wrap(user);
console.log(user.name); // John
console.log(user.age); // ReferenceError: Property doesn't exist "age"
*/
/* 내 해답 >>> 하드 코딩적인 느낌 */
let user = {
name: "John"
};
function wrap(target) {
return new Proxy(target, {
get(tar, prop){
if (prop in tar){
return tar[prop];
}
else return `ReferenceError: Property doesn't exist "${prop}"`
}
});
}
user = wrap(user);
console.log(user.name); // John
console.log(user.check); // ReferenceError: Property doesn't exist "check"
/* 정답 > throw 활용 */
let answer = {
name: "PCY"
};
function wrap(target) {
return new Proxy(target, {
get(target, prop, receiver) {
if (prop in target) {
return Reflect.get(target, prop, receiver);
} else {
throw new ReferenceError(`Property doesn't exist: "${prop}"`);
}
}
});
}
answer = wrap(answer);
console.log(answer.name); // John
console.log(answer.age); // ReferenceError: Property doesn't exist "age"
⇒ 2번
/* 음수 인덱스를 사용해 배열 요소에 접근하기
몇몇 프로그래밍 언어는 음수 인덱스를 사용해 배열 끝을 기준으로 요소에 접근할 수 있게 해줍니다.
아래와 같이 말이죠.
let array = [1, 2, 3];
array[-1]; // 3, 마지막 요소
array[-2]; // 2, 뒤에서 두 번째 요소
array[-3]; // 1, 뒤에서 세 번째 요소
위 예시에서 array[-N]는 array[array.length - N]와 동일합니다.
이렇게 음수 인덱스를 사용해 배열 요소에 접근할 수 있도록 해주는 프락시를 만들어봅시다.
최종 결과는 아래 조건을 만족해야 합니다.
let array = [1, 2, 3];
array = new Proxy(array, {
// 여기에 코드를 작성하세요.
});
alert( array[-1] ); // 3
alert( array[-2] ); // 2
// 배열 기능은 "변함없이 그대로" 동작해야 합니다.
*/
let array = [1, 2, 3];
array = new Proxy(array, {
get(target, prop, receiver) {
if (prop < 0) {
// arr[1] 같은 형태로 배열 요소에 접근하는 경우에도
// prop은 문자열이기 때문에 숫자로 바꿔줘야 합니다.
prop = +prop + target.length;
}
return Reflect.get(target, prop, receiver);
}
});
console.log(array[-1]); // 3
console.log(array[-2]); // 2
⇒ Chat GPT 답변
요약하면 프록시는 객체 작업 동작을 가로채고 사용자 정의하기 위한 강력하고 유연한 메커니즘을 제공합니다. 보안 및 검증 강화부터 애플리케이션의 반응성 및 관찰 가능성과 같은 고급 프로그래밍 패턴 활성화에 이르기까지 다양한 용도로 사용됩니다.
상당히 유용한 기능이라고 생각한다. Chat GPT를 통해서 언제 그리고 무엇을 위해 Proxy를 사용하는 것이 좋냐고 물어봤을 때 여러가지 답변이 나왔는데, 그중에서도 API 가로채기 및 수정과 가상화에 좋다고 나왔다.
분명, 실무에서도 자주 사용될 것이라고 생각한다. 내 개인 프로젝트에서도 사용해보면 좋을 것 같다.