본문 바로가기

Design Patterns

프록시, 프록시 패턴, 데코레이터 패턴

프록시 패턴과 데코레이터 패턴

Proxy 란?

클라이언트와 서버 관계는 컴퓨터 세상에서는 흔하게 볼 수 있는 구조다. 네트워크 상의 통신, 하나의 컴퓨터 내부의 프로세스간 통신, 심지어 애플리케이션 내부에서 객체간의 통신에서도 우리는 클라이언트 서버 관계를 쉽게 찾을 수 있다. 여기서는 객체간의 통신에서의 프록시에 대해 알아본다.
클라이언트는 서버에 필요한 것을 요청을 하고 서버는 그에 대한 처리를 하고 응답을 반환한다. 프록시는 이런 클라이언트와 서버 사이에서 요청에 대한 접근 제어, 부가기능 추가 등의 기능을 담당한다. 프록시는 컴퓨터 세상에 다양한 클라이언트 서버 관계들에서 존재할 수 있다. 그리고 클라이언트는 서버로 요청을 보냈지만 이를 처리하는 주체가 프록시인지 실제 서버인지 알 수는 없다.

  • 프록시의 주요 기능은 크게 두 가지로 구분할 수 있다.
    • 접근 제어
      • 권한에 따른 접근 차단
      • 캐시
      • 지연로딩
    • 부가기능 추가
      • 원래 서버가 제공하는 기능에 더해서 부가 기능을 수행
      • 예) 요청, 응답 값 변경, 로그 추가

예제 코드로 알아보는 프록시

아래에 간단한 Client와 Server 코드가 있다. Client는 Server에 request() 요청을 하는 상황이다.

class Client{
    private Server server;

    Client(Server server){
        this.server = server;
    }

    public Response request(){
        return server.request();
    }
}
interface Server{
    public Response request();
}

class RealServer implements Server{
    @Override
    public Response request(){
        // 요청을 처리
        ...
    }
}
  • 위의 관계에 대해 다음과 같이 수동으로 빈 등록을 해줄 수 있다.
class AppConfiguration{
    @Bean
    Client client(){
        return new Client(server());
    }

    @Bean
    Server server(){
        return new RealServer();
    }
}
  • 이와 같은 상황에서 Server와 Client의 코드를 수정하지 않고 Server의 응답을 캐싱하거나 로그를 남기는 등의 부가 기능을 추가하려면 어떻게 해야할까? 이럴 때 Proxy를 사용하면 간단하게 구현할 수 있다.
class ProxyServer implements Server{
    private Server server;

    public ProxyServer(Server server){
        this.server = server;
    }

    @Override
    public Response request(){
        // 캐싱 or 부가기능 처리
        server.request(); // 필요시 서버에 요청
        // 캐싱 or 부가기능 처리
        ...
    }
}
class AppConfiguration{
    @Bean
    Client client(){
        return new Client(server());
    }

    @Bean
    Server server(){
        Server realServer = new RealServer();
        return new ProxyServer(realServer);
    }
}
  • 위와 같이 Server를 상속한 ProxyServer 클래스를 구현하고 ProxyServer는 RealServer를 참조한다. RealServer에 실제 요청이 필요할때는 요청을 하고 요청의 전/후에 접근 제어, 부가기능 등의 역할을 수행할 수 있게 된다.
    • 위의 예제 코드에서는 Server라는 Interface를 두었지만 Interface가 없는 구체 클래스도 상속을 이용해서 Proxy를 만들 수 있다.

프록시 패턴과 데코레이터 패턴

  • GoF 디자인 패턴으로 소개되는 대표적인 패턴이다. 이 둘은 클라이언트 서버 사이에 프록시를 사용하여 구현한다는 점에서 겉에서 봤을 때에는 구조적으로 거의 차이가 없다. 다만 두 패턴은 구현하는 의도가 다르다.
    • 프록시 패턴 : 접근제어 (캐싱, 권한에 따른 접근제어, 지연로딩 등)
    • 데코레이터 패턴 : 부가기능 추가 (로그추가, 요청/응답 값 수정)

Reference

  • 스프링 핵심 원리 - 고급편, 김영한