[디자인 패턴] 프록시(Proxy) 패턴. 자바스크립트 프록시

❓프록시 패턴?

프록시(proxy)는 대리인, 대리자라는 뜻으로, 다른 객체의 접근 제어를 위한 구조 디자인 패턴입니다. 객체가 전달되는 중간 단계에 대리자를 위치시켜 무언가를 수행할 수 있도록 합니다.

하나의 객체를 두 개로 나눠 재구성하여, 실체 객체를 가로챌 수 있습니다.

여기서 특징은, 두 개로 나뉜 두 객체의 인터페이스는 같다는 것입니다.

❗프록시 패턴의 예시 (JS)

// 실체 객체
class RealObject {
  performAction() {
    console.log("실체 객체 작동");
  }
}

// 프록시 객체
class ProxyObject {
  constructor(realObject) {
    this.realObject = realObject;
  }

  performAction() {
    console.log("실체 객체 작동 이전, 프록시 객체 작동");
    this.realObject.performAction();
    console.log("실체 객체 작동 이후, 프록시 객체 작동");
  }
}

// 클라이언트 코드
const realObject = new RealObject();
const proxyObject = new ProxyObject(realObject);
proxyObject.performAction();
/*
실체 객체 작동 이전, 프록시 객체 작동
실체 객체 작동
실체 객체 작동 이후, 프록시 객체 작동
*/

동적 언어인 JS 특성상 인터페이스를 만들진 않았지만, 실체 객체와 프록시 객체 동일하게 performAction()를 가지고 있다는 것을 알 수 있습니다.

프록시 패턴을 활용하는 방법은 여러가지가 있습니다. 이 중에서 많이 사용되는 활용 용도에는 다음과 같은 것들이 있습니다.

✍️ 동적 프록시 (dynamic proxy)

동적 프록시(dynamic proxy)는 프로그램이 런타임 중에 동적으로 생성되는 프록시 객체를 말합니다. 클라이언트 코드에서는 행동을 처리하는 객체가 프록시인지 실제 객체인지 구별하지 않습니다. 이는 팩토리 패턴을 활용하여 구현할 수 있습니다.

팩토리 패턴이란 객체의 생성을 별도의 클래스로 분리하여 생성을 위임하는 패턴을 말합니다.

// 프록시 팩토리
class ProxyFactory{
	getObject(){
		this.real = new RealObject();
		return new ProxyObject ();
}
//...
const factory = new ProxyFactory();
const proxy = factory.getObject(); // 프록시의 동적 생성

 

 

[디자인 패턴] 팩토리(Factory) 패턴

❓팩토리 패턴 팩토리 패턴은 생성 패턴중에서도 가장 기본이 되는 패턴이라고 합니다. 객체 생성 처리를 위임하여 객체 지향의 문제점을 해결할 수 있다고 하네요. 객체 생성을 담당하는 동작

lurgi.tistory.com

✍️ 원격 프록시 (remote proxy)

원격 프록시(remote proxy)는 가장 많이 응용하는 패턴으로, 주로 데이터 전달을 목적으로 사용합니다. 이는 어댑터 패턴과 비슷하게 보일 수 있습니다.

 

[디자인 패턴] 어댑터(Adapter) 패턴

❓어댑터 패턴? 어댑터 패턴은 구조 패턴으로, 다른 말로 래퍼(Wrapper) 패턴이라고도 합니다. 연관성 없는 2개의 객체를 묶어서 인터페이스를 만들어 주는 패턴을 말하는데요, 이를 통해 서로 연

lurgi.tistory.com

어댑터 패턴과의 차이점이라면 어댑터 패턴은 서로 다른 인터페이스의 호환을 맞춰주는 반면, 원격 프록시는 동일한 인터페이스를 유지한다는 것입니다.

// 실체 객체
class RealObject {
	 action1() {
    console.log("실체 객체 action1 작동");
  }

	action2(){
		console.log("실체 객체 action2 작동");
	}
}

// 프록시 객체
class ProxyObject {
  constructor(realObject) {
    this.realObject = realObject;
  }

  action1() {
    console.log("action1 은 대체되었습니다.");
  }
	
	action2(){
		if(this.realObject.action2()){
			this.realObject.action2()
		}else{
			console.log("실체 객체에 존재하는 메서드가 아닙니다.")
		}
	}

	action3(){
		if(this.realObject.action3()){
			this.realObject.action3()
		}else{
			console.log("실체 객체에 존재하는 메서드가 아닙니다.")
		}
	}
}

// 클라이언트 코드
const realObject = new RealObject();
const proxyObject = new ProxyObject(realObject);
proxyObject.action1(); // action1 은 대체되었습니다.
proxyObject.action2(); // 실체 객체 action2 작동
proxyObject.action3(); // 실체 객체에 존재하는 메서드가 아닙니다.
/*

*/

예제와 같이 투과적 특성을 이용하여 객체의 행동을 대체 처리 할 수 있습니다.

✍️ 가상 프록시 (virtual proxy)

가상 프록시(virtual proxy) 는 프로그램의 실행 속도를 개선하기 위한 패턴입니다. 프록시 패턴을 이용하여 무거운 객체 생성을 잠시 유보합니다.

게으른 초기화(lazy initialization)를 이용하여 지연 시간을 줄일 수 있습니다. 게으른 초기화란, 객체의 생성에 많은 시간이 소요되는 경우, 해당 객체를 직접 요청하기 이전까지 객체의 생성을 지연시켜 불필요한 리소스 사용을 방지하는 것을 말합니다.

//... 프록시 클래스 내부
	action2(){
		// 게으른 객체 생성
		if(!this.realObject){
			this.real()
		}
		this.realObject.action2();
	}
	
	//무거운 객체를 생성합니다.
	real(){
		this.realObject = new RealObject();
	}
//...

✍️ 보호용 프록시

보호용 프록시는 객체의 접근을 제어하기 위한 객체의 대리자를 제공합니다. 특정 조건을 이용하여 보안이나 안정성을 강화하는 패턴입니다.

// 프록시 클래스
class FileSystemProxy {
    constructor(realFileSystem, userRole) {
        this.realFileSystem = realFileSystem;
        this.userRole = userRole;
    }

    readFile(fileName) {
        if (this.userRole === "admin") {
            this.realFileSystem.readFile(fileName);
        } else {
            console.log("접근이 거부되었습니다.");
        }
    }
}

// 클라이언트 코드
const realFileSystem = new FileSystem();

const adminProxy = new FileSystemProxy(realFileSystem, "admin");
adminProxy.readFile("importantDocument.txt");  // 허용

const userProxy = new FileSystemProxy(realFileSystem, "user");
userProxy.readFile("confidentialInfo.txt");    // 거부

✍️ 스마트 참조자

이것은 장식자 패턴과 유사하게 객체를 동적으로 확장 응용하는 기법입니다.

//... 프록시 클래스 내부
	action2(){
		console.log("추가 동작 콘솔 입니다.") // 동적 기능을 확장
		this.realObject.action2();
	}
//...

위와 같이 실제 코드를 직접 변경하지 않고, 추가 작업을 부여할 수 있다는 장점이 있습니다.

 

[디자인 패턴] 장식자(Decorator) 패턴

❓장식자 패턴? 동적 기능을 추가하기 위해 구조를 개선하는 패턴입니다. 장식자 패턴은 어댑터 패턴과도 비슷한 개념입니다. 래퍼(Wrapper) 구조를 사용한다는 점에서 비슷한데요, 두 패턴의 목

lurgi.tistory.com

❗JS에서의 프록시

JS에서는 ES6에서 Proxy 객체가 추가되었습니다. 따라서 위의 코드처럼 직접 Proxy를 구현할 필요 없이 Proxy 객체를 사용하여 기본 객체를 가로채고 재정의할 수 있습니다. 자세한 사용법은 MDN을 참고하세요!

const target = {
  message1: "hello",
  message2: "everyone",
};

const handler1 = {};

const proxy1 = new Proxy(target, handler1);

 

 

Proxy - JavaScript | MDN

Proxy 객체를 사용하면 한 객체에 대한 기본 작업을 가로채고 재정의하는 프록시를 만들 수 있습니다.

developer.mozilla.org

❗결론!

프록시 패턴은 결국 “대리자”를 만들어 실체 객체의 변경을 하지 않고, 간접 적으로 제어할 수 있는 것이라는 것을 알았습니다! 객체 지향에서 중요한 ‘느슨한 결합’을 도와주는 아주 유용한 패턴이라는 것을 깨달았구요. 정말 중요한 내용 패턴이라고 생각되네요!