ES6에서 새로 등장한 Proxy는 요즘 많은 프레임워크에서 주목하는 키워드이다
요즘이라고 하기엔 좀 오래되긴했지만 ..
예시로 Vue2.x에서는 객체의 수정 및 삭제를 Object.defineProperty의 getter와 setter를 사용하여 구현했지만 속성의 프로퍼티 변경시 추적이 어렵다는 이유로 Vue3부터는 속성에 proxy개념을 도입했다
Proxy의 사전적 뜻은 '대리인', '대리'라는 뜻이다
JS에서도 말그대로 대리인의 역할을 해주는데, 쉽게 말하면 객체의 여러 기본 동작을 가로채(트랩) 특별한 다른 동작을 할 수 있게 한다는 것이다.
객체는 Object, Array, Dictionary등 js의 모든 자료형이 대상이 될 수 있다
//아래와 같이 정의
const proxy = new Proxy(target, handler)
target
감싸질 객체. Proxy의 대상이 되는 객체
handler
target의 여러 트랩을 정의
handler로 가로챌 수 있는 동작은 다음과 같다
핸들러 메서드 | 작동 시점 |
---|---|
get | 프로퍼티를 읽을 때 |
set | 프로터티에 값을 쓸 때 |
has | in 연산자가 작동할 때 |
deleteProperty | delete 연산자가 작동할 때 |
apply | 함수를 호출할 때 |
constructor | new 연산자가 작동할 때 |
getPrototypeOf | Object.getPrototypeOf |
setPrototypeOf | Object.setPrototypeOf |
isExtensible | Object.isExtensible |
preventExtensions | Object.preventExtensions |
getOwnPropertyDescriptor | Object.getOwnPropertyDescriptor |
ownKeys | Object.getOwnPropertyNames Object.getOwnPropertySymbols |
사실 프록시를 처음 접할 때 이렇게 보면 100번을 읽어도 뭘하는 애인지 파악하기 힘들다
바로 예제 코드를 보고 익혀보자
우선 handler가 없는 proxy 객체를 사용해보자
const target= {};
const proxy = new Proxy(target, {}) //핸들러가 없는 proxy 객체 정의
proxy.test = 5; // 1. proxy에 새로운 프로퍼티 'test' 추가
console.log(proxy.test) // 2. proxy의 test 프로퍼티를 읽을 수 있다. 당연히 5가 출력
console.log(target.test) // 3. 원래 객체인 target의 test 프로퍼티에도 5가 저장된다
handler가 없는 proxy는 위의 그림과 같이 그냥 target객체를 감싸는 투명한 래퍼가 된다고 생각하면 된다
중간에 가로채는 트랩이 없으니 그냥 target의 기본 메서드를 사용하는것!
위의 예시코드에서 했던 1, 2번 동작은 각각 handler set, get메소드가 되겠다
이번에는 proxy에 핸들러를 추가해서 가장 기본적인 동작인 get부터 사용해보자
get은 객체의 값을 읽을 때 호출된다
// get 사용방법
const target= {};
const proxy = new Proxy(target, {
get(target, prop, receiver){
console.log(target,prop, receiver)
}
})
proxy.test = 5;
console.log(proxy.test) //출력: { test: 5 } test { test: 5 }
get메서드는 기본적으로 다음 세가지 인자를 받는다
target
new Proxy의 첫번째 인자로 주었던 target 객체
prop
조회한 property
receiver
getter가 호출될 때 this. 기본적으로는 객체 자신을 호출하지만 상속받은 객체일 경우에는 상속한 객체가 this가 된다. 예제코드에서는 사용하지 않을거니 신경안써도됨
파라미터로 받은 인자들로 새로운 동작을 정의하면 된다
이번에는 새로운 동작을 정의해보자
// get 사용 예제
const target= {};
const proxy = new Proxy(target, {
get(target, prop){
return `${prop}을 조회했더니 ${target[prop]}가 나왔당~!`
}
})
proxy.test = 5;
console.log(proxy.test) // test을 조회했더니 5가 나왔당~!
handler가 추가됐기 때문에 get 동작을 했을 때 원래 값인 5이 출력되는 것이 아니라
get handler가 가로채서 'test을 조회했더니 5가 나왔당~!'이 출력된 것이다
이번에는 객체에 프로퍼티를 쓸 때 호출되는 set을 사용해보자
// set 사용방법
const target= {};
const proxy = new Proxy(target, {
set(target, prop, value, receiver){
console.log(target,prop, value, receiver)
}
})
proxy.test = 5; // 출력: {} test 5 {}
set메서드는 기본적으로 다음 네가지 인자를 받는다
target
new Proxy의 첫번째 인자로 주었던 target 객체
prop
작성한 property 이름
val
작성한 property 값
receiver
get의 receiver와 유사하게 동작하는 객체
target의 test는 4자 이상의 문자열만 받아야한다고 가정해보자
그러면 set 핸들러로 validation check를 하듯이 사용할 수 있다
const target= {};
const proxy = new Proxy(target, {
set(target, prop, value, receiver){
if (typeof value !== 'string') {
console.log('ERROR:문자열을 입력해주세요');
return false;
}
if (value.length<4) {
console.log('ERROR:4자 이상의 문자열을 입력해주세요');
return false;
}
console.log(`${prop}에 '${value}' 저장`)
return true;
}
})
proxy.test = 5; // ERROR:문자열을 입력해주세요
proxy.test = 'abc'; //ERROR:4자 이상의 문자열을 입력해주세요
proxy.test = '안녕하세요'; //test에 '안녕하세요' 저장
이렇게 proxy의 기본적인 원리와 동작을 살펴보았다 !
이외에 순환을 돌때, 프로퍼티를 삭제할 때 등에도 여러 핸들러를 추가할 수 있다
더 심화적인 내용은 2편에 ..!