프록시 패턴 (Proxy Pattern)

2022. 3. 25. 01:40CSE/Design Pattern

1. Proxy Pattern?

프록시 패턴을 알아보기 전에, 프록시를 검색하면 다음과 같은 설명을 볼 수 있다.

 

  • 일반적으로 프록시는 다른 무언가와 이어지는 인터페이스의 역할을 하는 클래스이다. 프록시는 어떠한 것(이를테면 네트워크 연결, 메모리 안의 커다란 객체, 파일, 또 복제할 수 없거나 수요가 많은 리소스)과도 인터페이스의 역할을 수행할 수 있다.

개념만으로는 감이 잘 오지 않으므로 프록시 패턴이 필요한 경우를 보자.

 

2. Proxy Pattern이 필요한 경우 예시

public class Service {
    public String runSomething(){
        return "run Service";
    }
}

 

public class ClientWithNoProxy {
    public static void main(String[] args) {
        Service service = new Service();
        System.out.println(service.runSomething());
    }
}

위와 같은 코드를 실행하는데, 갑자기 Service의 runSomething() 메소드의 실행 시간이 재고 싶어졌다.

그래서 Service의 runSomething() 메소드의 앞부분과 뒷 부분에 시간을 측정하는 코드를 추가했다.

하지만, 이 경우는 결국 runSomething() 메소드의 내용을 변화시키게 된다.

Service 객체의 runSomething() 메소드의 내용을 변화시키지 않고 시간을 측정하는 방법은 없을까?

 

다음과 같이 Proxy Pattern을 적용하면 된다.

public interface IService {
    String runSomething();
}

 

public class Service implements IService {
    public String runSomething(){
        return "run Service";
    }
}

 

public class Proxy implements IService{
    IService service;
    
    public Proxy(){}
    
    public Proxy(IService serivce){
    	this.service = service;
    }

    @Override
    public String runSomething() {
    	if (this.service == null){
        	this.service = new Service();
        } 
        
        //권한 체크를 추가해서 보안 추가할 수 있다.
    
    	//시간 측정 코드 추가
        String result = service.runSomething();
        //시간 측정 코드 추가
        
        return result;
    }
}

 

public class ClientWithProxy {
    public static void main(String[] args) {
        //프록시를 이용한 호출
        IService proxy = new Proxy(new Service());
        System.out.println(proxy.runSomething());
    }
}

이 경우, 시퀀스 다이어그램은 다음과 같다.

ClientWithProxy는 ClientWithoutProxy와 다르게 Service 객체를 직접 사용하지 않고, 대리자 객체인 proxy를 통해 Service의 runSomething() 메서드를 호출하고 반환값을 전달받게 된다.

또한 Proxy의 runSomething() 메서드에 실행 시간을 측정하는 코드를 추가하게 되면서, 원래 객체인 Service의 변화 없이도 실행 시간을 측정할 수 있다.

 

프록시 패턴은 인터페이스를 사용하여 서비스 객체가 들어갈 자리에 대리자 객체를 대신 투입해서 클라이언트쪽이 실제 서비스 객체를 통해 메서드를 호출하고 반환값을 받는지, 대리자 객체를 통해 메서드를 호출하고 반환값을 받는지 전혀 모르게 할 수 있다.

 

 

4. Proxy Pattern

Proxy는 대리자, 대변인이라는 뜻이다.

대변인이 특정 기관의 입장을 대변할 뿐, 그 입장에 본인의 입장을 가감하지 않는 것 처럼 Proxy Pattern은 실제 서비스 메서드의 반환 값을 변화시키지 않고, 오직 제어의 흐름을 변경하거나 다른 로직을 수행하기 위해 사용한다.

즉, 사용하고자 하는 객체를 직접 사용하지 않고, 대리인의 개념인 Proxy를 거쳐서 쓰는 패턴이다.

 

먼저, 프록시 패턴의 기본적인 Class Diagram을 보자.

Subject 인터페이스는 Client가 실제로 사용하고 싶은 RealSubject와 Proxy가 동시에 implement하는 인터페이스이다.

이러한 구조를 통해 Client는 RealSubject 객체를 직접 사용하지 않고, Proxy 객체를 통해 사용하게 된다.

 

즉, 객체에 대한 모든 요청은 Proxy가 가장 먼저 받는다.

 

이러한 구조를 통해 우리는 Proxy Pattern을 다음과 같이 정의할 수 있다.

  • 프록시 패턴은 특정 객체에 대한 접근을 제어하거나 기능을 추가할 수 있는 패턴

 

 

5. Proxy Pattern의 장단점 + 활용

(1) 장점

  • 기존 코드를 변경하지 않고 새로운 기능을 추가할 수 있다.
  • 기존 코드가 해야 하는 일만 유지할 수 있다.
  • 기능 추가 및 초기화 지연 등으로 다양하게 활용할 수 있다.
    • 초기화 지연 : 많은 리소스를 필요로 하는 객체를 미리 만들어 놓았는데, 안 쓰면 손해이므로, 최초 사용을 할 때, 초기화를 하도록 초기화를 지연할 수 있다.
    • 보안 : 특정 권한이 있어야만 해당 객체를 쓸 수 있을 때, 권한을 확인하는 로직을 Proxy에 추가한다.
    • 캐싱 : 특정 객체에 접근하려고 하는데, Proxy에 이미 캐싱되어 있는 객체라면 굳이 타겟 객체까지 가지 않아도 되므로, 성능적으로 이점이 있다.

(2) 단점

  • 코드의 복잡도가 증가한다.
    • 원래는 Client Class, Service Class 두 개로 구현했는데, interface와 proxy class가 추가 되면서 복잡해졌다.