디자인 패턴 7. Proxy Pattern, 프록시 패턴

Eddy·2023년 4월 14일
0

Javascript

목록 보기
20/27

디자인패턴

프록시패턴(Proxy Pattern) 이란?

프록시(Proxy)를 번역하면 대리자, 대변인의 의미를 갖고 있다. 대리자, 대변인은 누군가를 대신해서 그 역할을 수행하는 존재이다. 이는 프로그램에도 똑같이 적용된다. 즉, 프록시에게 어떤 일을 대신 시키는 것이다.

어떤 객체를 사용하고자 할때, 객체를 직접적으로 참조하는 것이 아닌 해당 객체를 대항하는 객체를 통해 대상 객체에 접근하는 방식을 사용하면 해당 객체가 메모리에 존재하지 않아도 기본적인 정보를 참조하거나 설정할 수 있고, 실제 객체의 기능이 필요한 시점까지 객체의 생성을 미룰 수 있다.


구성 요소

Client
Subject
Proxy
Real Subject


프록시 패턴의 특징

프록시는 실제 서비스와 같은 이름의 메서드를 구현한다. 이때 인터페이스를 사용한다.
프록시는 실제 서비스에 대한 참조 변수를 갖는다(합성)
프록시는 실제 서비스의 같은 이름을 가진 메서드를 호출하고 그 값을 클라이언트에게 돌려준다.
프록시는 실제 서비스의 메서드 호출 전후에도 별도의 로직을 수행할 수도 있다.


프록시 패턴의 장단점

프록시패턴 장점

  • 기존 코드를 변경하지 않고 새로운 기능을 추가할 수 있으며 기존 코드가 해야 하는 일만 유지할 수 있다. 즉유연한 코드를 만들 수 있다.
  • (객체)초기화 지연 및 기능 추가(로깅, 시간 측정, 캐싱), 흐름제어 등으로 다양하게 활용할 수 있다.
  • 사이즈가 큰 객체가 로딩되기 전에도 프록시를 통해 참조를 할 수 있다.
  • 실제 객체의 public, protected 메소드를 숨기고 인터페이스를 통해 노출시킬 수 있다.
  • 로컬에 있지 않고 떨어져있는 객체를 사용할 수 있다.
  • 원래 객체에 접근에 대해 사전처리를 할 수 있다.

프록시패턴 단점

  • 객체를 생성할 때 한 단계를 거치게 되므로, 빈번한 객체 생성이 필요한 경우 성능이 저하될 수 있다.
  • 프록시 내부에서 객체 생성을 위해 스레드가 생성, 동기화가 구현되어야 하는 경우 성능이 저하될 수 있다.
  • 로직이 난해해져 가독성이 떨어질 수 있다.

예제

예제1 TypeScript

// 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"

예제2 simple code

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 (가로챔)

예제 3 유효성 검사

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'

예제 4 데이터 싱크

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:''})

예제 5 접근 제어

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

0개의 댓글