1. intro
proxy 기능을 사용하면 한 객체에 대한 기본 작업을 가로채고 재정의하는 프록시 객체를 만들 수 있다.
(비슷한 기능을 함수에선 hooking이라고 함)
원래 obj대신 사용할 있는 객체를 만들지만 이 객체의 속성 가져오기, 설정, 정의등의 기본적인 작업을 재정의한다.
(사용자가 객체안의 속성값을 읽는 코드를 입력 후 실행해도, 객체 속성을 바꾸는 등의 작업으로 바꿀 수 있음)
일반적으로 속성 엑세스 기록, 입력의 유효성 검사, 형식 지정, 삭제에 사용한다.
보통 매개변수 2개를 이용해 Proxy 객체를 생성한다.
target : 프록시 할 원본 객체
handler : 가로채는 작업, 가로채는 작업을 재정의하는 방법을 정의하는 객체
const target = {
msg1 : 'hello',
msg2 : 'world'
}
const handler = {}
const proxy1 = new Proxy(target, handler)
console.log(proxy1.msg1) // hello
console.log(proxy1.msg2) // world
handler가 빈 객체일 경우, 특별히 달라지는 것 없이 원래 사용하던대로 객체를 사용할 수 있다.
2. get()
get() 처리기는 대상 객체의 속성 액세스에 개입한다.
즉, 우리가 객체의 속성에 접근하는 코드를 실행할 때, 이를 막고 다른 기능을 수행하게 한다.
const target = {
msg1 : 'hello',
msg2 : 'world'
}
const handler2 = {
get(target, prop, receiver) {
// target : 원본 객체
// prop : 객체의 속성
// receiver : assign이 지시된 원래 객체 (일반적으로 Proxy 자체)
return 'intercept!'
}
}
const proxy2 = new Proxy(target, handler2)
console.log(proxy2.msg1) // intercept!!
console.log(proxy2.msg2) // intercept!!
Reflect class를 이용해 재정의할 접근자를 객체의 일부 속성만으로 한정할 수 있다.
const target = {
msg1 : 'hello',
msg2 : 'world'
}
const handler3 = {
get(target, prop, receiver) {
if(prop === 'message2') return 'intercept';
}
return Reflect.get(...arguments)
// 조건문을 만족하지 않을 경우, 원래 속성값을 리턴
}
const proxy3 = new Proxy(target, handler3)
console.log(proxy3.msg1) // hello
console.log(proxy3.msg2) // intercept
이걸 응용해 객체에 없는 속성을 보려고 하면 미리 정해둔 값을 돌려주는 기능 등을 추가할 수 있다.
const handler = {
get(obj, prop) {
return prop in obj ?
obj[prop] :
37;
}
}
const p = new Proxy({}, handler)
p.a = 1;
p.b = 'asd';
console.log(p.b) // 'asd'
console.log('c' in p, p.c) // false, 37
p.b는 p안의 b 값을, 존재하지 않는 p.c 값은 get함수에 따라 37을 리턴한다.
const steamDollar = {
class : 'windBreaker',
level : 232
}
const handler = {
get : function(target, prop, receiver) {
if(prop === 'class') {
return `archer`
}
return Reflect.get(...arguments)
}
}
const proxy = new Proxy(steamDollar, handler)
console.log(proxy) // { class: 'windBreaker', level: 232 }
console.log(proxy.level) // 232
console.log(proxy.class) // archer
Proxy 객체 class 속성을 불러오면 get 함수에 따라 원 객체 class 속성의 value인 windbreaker가 아닌 'archer'가 출력된다.
Proxy 객체 자체나 다른 속성인 level을 불러오면 원 객체와 동일한 값이 리턴된다.
3. set()
set 함수는 우리가 객체의 속성값을 세팅할 때의 과정에 개입한다.
const Laminaria = { class : 'mercedes', level : 190 }
const handler = {
set(obj, prop, value, receiver) {
// obj : 대상 객체
// prop : 속성
// value : prop의 value
// receiver : assign이 지시된 원래 객체 (일반적으로 프록시 자체)
if((prop === 'level') && ((value >= 200))) {
console.log('입장 가능')
}
else {
return Reflect.set(...arguments)
}
}
}
const proxy = new Proxy(Laminaria, handler)
proxy.level = 200; // 입장 가능
// level을 200으로 바꾸려 시도
// > 조건을 만족해 handler의 set 함수가 발동
// > proxy.level을 200으로 바꾸는 대신 콘솔로그 출력
console.log(proxy.level) // 190
proxy.level = 180;
// 조건을 만족하지 못할 경우, level이 변경되고 종료
console.log(proxy)
// { class: 'mercedes', level: 180 }
proxy.level = 210; // 입장 가능
console.log(proxy.level) // 180
proxy.level을 200으로 set하려 하면, Proxy 객체가 이를 막고, 조건문에 따라 함수를 실행해 '입장 가능'을 출력한다.
값을 set하는 것을 막았으므로 다시 proxy.level의 값을 확인하면
바뀌지 않고 그대로 원 객체의 그 값을 유지하고 있음을 확인할 수 있다.
4. deleteProperty()
delete 함수는 원본 객체의 속성을 삭제하는 과정에 개입한다.
const chaosHead = {
class : 'luminous',
level : 70
}
const handler = {
deleteProperty(target, prop) {
if (prop in target) {
if(prop == 'level') {
delete target[prop]
console.log(`property deleted: ${prop}`)
}
else {
console.log('삭제할 수 없는 속성입니다.')
return
}
}
else {
console.log('존재하지 않는 속성입니다.')
}
}
}
console.log(chaosHead.class) // luminous
const proxy = new Proxy(chaosHead, handler)
delete proxy.level // property deleted: level
console.log(chaosHead.level) // undefined
delete proxy.class // 삭제할 수 없는 속성입니다.
delete proxy.gender // 존재하지 않는 속성입니다.