프록시(Proxy)를 번역하면 대리자, 대변인의 의미를 갖고 있다. 대리자, 대변인은 누군가를 대신해서 그 역할을 수행하는 존재이다. 이는 프로그램에도 똑같이 적용된다. 즉, 프록시에게 어떤 일을 대신 시키는 것이다.
어떤 객체를 사용하고자 할때, 객체를 직접적으로 참조하는 것이 아닌 해당 객체를 대항하는 객체를 통해 대상 객체에 접근하는 방식을 사용하면 해당 객체가 메모리에 존재하지 않아도 기본적인 정보를 참조하거나 설정할 수 있고, 실제 객체의 기능이 필요한 시점까지 객체의 생성을 미룰 수 있다.
Client
Subject
Proxy
Real Subject
프록시는 실제 서비스와 같은 이름의 메서드를 구현한다. 이때 인터페이스를 사용한다.
프록시는 실제 서비스에 대한 참조 변수를 갖는다(합성)
프록시는 실제 서비스의 같은 이름을 가진 메서드를 호출하고 그 값을 클라이언트에게 돌려준다.
프록시는 실제 서비스의 메서드 호출 전후에도 별도의 로직을 수행할 수도 있다.
// Proxy 패턴
// Subject 인터페이스
interface Payment {
request(amount: number):void
}
// Real Subject 실제객체
class Cash implements Payment {
request(amount:number){
console.log(`결제요청 완료.. 금액: ${amount}`)
}
}
const targetObject = new Cash();
// Proxy
const paymentProxy = new Proxy(targetObject, {
get: (object, prop) => {
if(prop === 'request'){
return object[prop];
}
throw new Error("operation not implemented");
}
});
paymentProxy.request(100);
//결제요청 완료.. 금액: 100
paymentProxy.add(100);
//"operation not implemented"
const target = {
message1: 'hello',
message2: 'everyone',
}
const handler = {
get(target,prop) {
retrun 'world'
}
}
const proxy = new Proxy(target,handler)
console.log(proxy.message1) // world (가로챔)
const handler2 = {
get(target,prop) {
if(prop === 'message2') {
retrun 'world'
}
retrun target[prop]
//retrun Reflect.get(...arg)
}
}
const proxy2 = new Proxy(target,handler2)
console.log(proxy2.message1) // hellod
console.log(proxy2.message2) // world (가로챔)
const validator = {
set(obj,prop,value) {
if(prop === 'age') {
if(!Number.isInteger(value)) {
throw new TypeError ('the age is not an integer')
}
if(value > 200) {
throw new RangeError ('the age seems invalid')
}
}
//값을 저장하는 기본 동작
obj[prop] = value;
// 성공 표시
return true
}
}
const person = new Proxy({},validator);
person.age = 100;
console.log(person.age) // 100
person.age = 'young'; // 'the age is not an integer'
person.age = 300; // 'the age seems invalid'
let fullName = ''
const updater = {
set(obj,prop,value) {
if(prop === 'firstName') {
fullName = `${value} ${obj.lastName}`
} else if (prop === 'lastName') {
fullName = ` ${obj.firstName} ${value}`
}
obj[prop] = value;
}
}
const name = new Proxy({firstName:'', lastName:''})
const AccessibleProps = ['age'];
const handleAccess = {
get(obj,prop,value) {
if(!AccessibleProps.includes(prop)) {
retrun 'ACCESS_DENIED'
}
return obj[prop]
}
}
const person = new Proxy({
birthYear: 2000,
age:24
}, handleAccess)
console.log(person.birthYear) // 'ACCESS_DENIED'
console.log(person.age) // 24