카테고리 없음

Proxy (Object)

Sila 2022. 10. 30. 23:46

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 // 존재하지 않는 속성입니다.