프로그래밍

프록시 패턴

수박수박좋다 2024. 7. 19. 10:14
반응형

프록시 패턴

원본 객체에 접근해 사용하는 것이 아닌 프록시를 통해 원본 객체를 사용하는 방법

 

장점

  • 접근 제어: 클라이언트가 원본 객체에 직접 접근하지 않도록 제어
  • 지연 초기화: 무거운 클래스를 바로 인스턴스화하지 않고 지연시켜 성능 최적화 가능
  • 캐싱: 자주 사용하는 데이터를 캐싱하여 성능 향상
  • 원격 서비스 로컬 실행: 원격 서비스를 로컬에서 실행하여 네트워크 비용 절감
  • 유효성 검사: 데이터 유효성 검사를 통해 원본 객체 보호
  • 로깅: 요청과 응답에 대한 로깅 기능 제공

 

단점

  • 응답 지연: 프록시를 통한 추가 작업으로 인해 응답이 늦어질 수 있음
  • 복잡성 증가: 클래스를 새로 도입해야 하므로 코드가 복잡해질 수 있음

예제

/**
 * Subject 인터페이스는 RealSubject와 Proxy 모두를 위한 공통 작업을 선언합니다. 
 * 클라이언트가 이 인터페이스를 사용하여 RealSubject와 작업하는 한, 
 * 실제 주체 대신 프록시를 전달할 수 있습니다.
 */
interface Subject {
    request(): void;
}

/**
 * RealSubject는 일부 핵심 비즈니스 로직을 포함합니다. 일반적으로 RealSubjects는 
 * 입력 데이터를 수정하는 등 유용한 작업을 수행할 수 있지만 매우 느리거나 민감할 수 있습니다. 
 * Proxy는 RealSubject 코드의 변경 없이 이러한 문제를 해결할 수 있습니다.
 */
class RealSubject implements Subject {
    public request(): void {
        console.log('RealSubject: Handling request.');
    }
}

/**
 * Proxy는 RealSubject와 동일한 인터페이스를 가집니다.
 */
class Proxy implements Subject {
    private realSubject: RealSubject;

    /**
     * Proxy는 RealSubject 클래스의 객체에 대한 참조를 유지합니다. 
     * 이는 지연 로드되거나 클라이언트에 의해 Proxy로 전달될 수 있습니다.
     */
    constructor(realSubject: RealSubject) {
        this.realSubject = realSubject;
    }

    /**
     * Proxy 패턴의 가장 일반적인 응용 프로그램은 지연 로딩, 캐싱, 
     * 액세스 제어, 로깅 등입니다. Proxy는 이러한 작업 중 하나를 수행한 다음, 
     * 결과에 따라 연결된 RealSubject 객체에서 동일한 메서드의 실행을 전달할 수 있습니다.
     */
    public request(): void {
        if (this.checkAccess()) {
            this.realSubject.request();
            this.logAccess();
        }
    }

    private checkAccess(): boolean {
        // 여기에 실제 검사 로직이 들어가야 합니다.
        console.log('Proxy: Checking access prior to firing a real request.');

        return true;
    }

    private logAccess(): void {
        console.log('Proxy: Logging the time of request.');
    }
}

/**
 * 클라이언트 코드는 모든 객체(주제 및 프록시)를 Subject 인터페이스를 통해 
 * 작업해야 실제 주제와 프록시 모두를 지원할 수 있습니다. 그러나 실제로는 
 * 대부분의 클라이언트가 실제 주체와 직접 작업합니다. 이 경우 패턴을 더 쉽게 
 * 구현하기 위해 프록시를 실제 주체 클래스에서 확장할 수 있습니다.
 */
function clientCode(subject: Subject) {
    // ...

    subject.request();

    // ...
}

console.log('Client: Executing the client code with a real subject:');
const realSubject = new RealSubject();
clientCode(realSubject);

console.log('');

console.log('Client: Executing the same client code with a proxy:');
const proxy = new Proxy(realSubject);
clientCode(proxy);
  • 프록시 패턴은 원본 객체의 호출을 가로채 부가 작업을 추가하는데 유용
  • 클라이언트는 원본과 동일한 인터페이스로 구현된 객체를 호출하기에 큰 차이를 알지 못함
  • JavaScript에는 Proxy라는 내장 객체가 있어 동일 기능을 짧게 구현 가능

 

 

내장 객체 Proxy 예제

class Subject {
    request() {
        throw new Error("This method should be overridden.");
    }
}

class RealSubject extends Subject {
    request() {
        console.log('RealSubject: Handling request.');
    }
}

/**
 * JavaScript Proxy 객체를 사용하여 동일한 인터페이스를 가진 Proxy 클래스를 생성
 */
const handler = {
    get: function(target, prop, receiver) {
        if (prop === 'request') {
            return function() {
                if (proxy.checkAccess()) {
                    target[prop]();
                    proxy.logAccess();
                }
            };
        }
        return Reflect.get(...arguments);
    }
};

const realSubject = new RealSubject();
const proxy = new Proxy(realSubject, handler);

// Proxy 객체의 메서드들을 추가로 정의
proxy.checkAccess = function() {
    console.log('Proxy: Checking access prior to firing a real request.');
    return true;
};

proxy.logAccess = function() {
    console.log('Proxy: Logging the time of request.');
};

/**
 * 클라이언트 코드는 모든 객체(주제 및 프록시)를 Subject 인터페이스를 통해 
 * 작업해야 실제 주제와 프록시 모두를 지원할 수 있습니다. 그러나 실제로는 
 * 대부분의 클라이언트가 실제 주체와 직접 작업합니다. 이 경우 패턴을 더 쉽게 
 * 구현하기 위해 프록시를 실제 주체 클래스에서 확장할 수 있습니다.
 */
function clientCode(subject) {
    // ...

    subject.request();

    // ...
}

console.log('Client: Executing the client code with a real subject:');
clientCode(realSubject);

console.log('');

console.log('Client: Executing the same client code with a proxy:');
clientCode(proxy);
  • 위 예제는 JavaScript의 Proxy 객체를 사용하여 프록시 패턴을 구현
  • handler 객체의 get 트랩을 사용하여 request 메서드를 가로채고 접근 제어와 로깅 기능 추가
  • Proxy 객체는 checkAccesslogAccess 메서드를 정의하여 이러한 기능 구현

 

 

JavaScript Proxy 객체

  • Proxy 객체: JavaScript에서 제공하는 내장 객체로, 기본 작업(속성 접근, 할당, 함수 호출 등)을 가로채고 재정의할 수 있음
  • 핸들러(handler): 트랩(trap)이라는 함수를 정의하는 객체로, 특정 작업을 가로챌 때 실행되는 메서드들로 구성
  • 타겟(target): Proxy가 감싸는 객체로, 기본 작업이 수행되는 실제 객체

Proxy 객체 예제

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

const handler = {
    get: function(target, prop, receiver) {
        if (prop === 'message2') {
            return 'world';
        }
        return Reflect.get(...arguments);
    }
};

const proxy = new Proxy(target, handler);

console.log(proxy.message1); // hello
console.log(proxy.message2); // world
  • target 객체는 원본 객체
  • handler 객체는 get 트랩을 정의하여 속성 접근을 가로채고 재정의
  • Proxy 객체는 targethandler를 결합하여 새로운 프록시 객체 생성
  • proxy.message2 접근 시 handlerget 트랩이 실행되어 'world'를 반환
  • JavaScript Proxy 객체는 프록시 패턴을 간결하게 구현할 수 있게 해줌
  • 다양한 트랩을 통해 속성 정의, 삭제, 함수 호출 등 여러 작업을 가로채고 재정의 가능
  • 성능 최적화, 접근 제어, 로깅 등 다양한 활용 사례 존재
반응형