Proxy[Design Pattern]

SnowCat·2023년 3월 15일
0

Design Pattern

목록 보기
13/23
post-thumbnail

의도

프록시: 다른 객체에 대한 대체물을 제공할 수 있는 구조 디자인 패턴

문제

  • 매우 많은 양의 자원을 소비하는 객체가 하나 있다 가정해보자.
  • 항상 필요한 객체가 아니라면, 객체를 지연되게 초기화 시킬 수 있을 것이다.
  • 그렇다면 객체를 사용하는 모든 클라이언트 코드들은 지연된 코드를 실행시켜야 하는데, 이는 많은 코드 중복을 초래하게 된다.
  • 코드를 객체에 집어넣을 수 있으면 좋겠지만, 코드가 타사 라이브러리인 경우와 같이 객체 내부에 집어넣을 수 없는 경우도 발생할 것이다.

해결책

  • 클라이언트에서 요청을 받아 객체와 통신을 대신 해주는 프록시 객체 사용
  • 프록시를 사용해 지연된 초기화 및 결괏값 캐싱을 클라이언트와 실제 데이터베이스 객체가 알지 못하게 처리를 할 수 있음

구조

https://refactoring.guru/images/patterns/diagrams/proxy/structure-2x.png

// 서비스의 인터페이스 부분으로 프록시가 서비스 객체로 위장하기 위해서는 인터페이스를 따라야 함
interface Subject {
    request(): void;
}

// 실제 비즈니스 로직을 작성하는 서비스 부분
class RealSubject implements Subject {
    public request(): void {
        console.log('RealSubject: Handling request.');
    }
}

// 서비스 객체를 레퍼런스로 받는 프록시 객체
// 클라이언트 요청의 처리를 마치고 실제 서비스 로직을 처리해 클라이언트에 결과를 전달함
class Proxy implements Subject {
    private realSubject: RealSubject;

    constructor(realSubject: RealSubject) {
        this.realSubject = 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.');
    }
}

// 이제 클라이언트는 프록시 인터페이스를 통해 실제 코드와 상호작용함
function clientCode(subject: Subject) {
    // ...
    subject.request();
    // ...
}

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

console.log('');

console.log('Client: Executing the same client code with a proxy:');
const proxy = new Proxy(realSubject);
clientCode(proxy);
/*
Proxy: Checking access prior to firing a real request.
RealSubject: Handling request.
Proxy: Logging the time of request.
*/

적용

  • 지연된 초기화가 필요할 때 사용
    앱이 시작될 때 무거운 객체를 생성하는 대신 초기화가 필요한 시점에 지연 생성하도록 함
  • 특정 클라이언트들만 서비스 객체를 사용할 수 있도록 하는 경우에 사용 가능
    프록시에서 자격 증명을 확인하게 할 수 있음
  • 원격 서비스의 로컬 실행이 필요한 경우 사용
    프록시는 이 경우 네트워크를 통해 클라이언트 요청을 전달함
  • 요청을 로깅, 캐싱해야 하는 경우 사용
    프록시가 요청을 받아 기록해두거나, 반복 요청들을 확인해 캐싱해둘 수 있음

구현 방법

  1. 서비스 인터페이스가 없다면 서비스 인터페이스를 생성해 프록시와 서비스 객체 간의 상호 교환을 가능하게 생성
  2. 프록시 클래스를 제작하며, 서비스에 대한 레퍼런스를 저장하는 필드를 가지고 있어야 함
  3. 목적에 따라 프록시 메서드를 구현
  4. 필요하다면 서비스 객체에 대한 지연된 초기화 구현과, 클라이언트가 실제 서비스를 받을지 프록시를 받을지 결정하는 생성 메서드 구현

장단점

  • 클라이언트들이 모르게 서비스 객체를 제어 가능하고, 서비스 객체의 생명 주기를 관리할 수 있음
  • 서비스 객체가 준비되지 않거나 사용 불가능한 경우에도 프록시는 작동함
  • 서비스나 클라이언트 변경 없이 새 프록시를 적용 가능 -> 개방, 폐쇄 원칙 준수
  • 새로운 클래스를 많이 도입해야 하기 때문에 코드가 복잡해질 수 있음
  • 서비스 응답이 느려질 수 있음
profile
냐아아아아아아아아앙

0개의 댓글