디자인 패턴(구)/구조 패턴

프록시(Proxy) 패턴이란?

공대키메라 2022. 4. 16. 18:55

1. 프록시 패턴이란?

의도

다른 객체에 대한 접근을 제어하기 위핸 대리자 또는 자리채움자 역할을 하는 객체를 다룬다. 

 

즉, 특정 객체에 대한 접근을 제어하거나 기능을 추가할 수 있는 패턴이다

 

proxy를 통해 할 수 있는 일은 두가지로 구분할 수 있다.

 

접근제어

  • 권한에 따른 접근 차단
  • 캐싱
  • 지연로딩

부가 기능 추가

  • 원래 서버가 제공하는 기능에 더해 부가 기능 수행( 중간에서 변형! 혹은 로그 작동도 가능 )

 

즉, 프록시 객체가 중간에 있으면 크게 접근 제어와 부가 기능 추가를 수행할 수 있다.

사용 시기

프록시 패턴은 단순한 포인터보다 조금 더 다방면에서 활용할 수 있거나 정교한 객체 참조자가 필요한 때 적용할 수 있다.

  • 원격지 프록시 : 서로 다른 주소 공간에 존재하는 객체를 가리키는 대표 객체
  • 가상 프록시 : 요청이 있을 때만 필요한 고비용 객체를 생성(스프링에서 많이 사용)
  • 보호용 프록시 : 원래 객체에 대한 실제 접근을 제어
  • 스마트 참조자 : 원시 포인터의 대체용 객체

구조

  • Proxy : 실제 참조할 대상에 대한 참조자를 관리. RealSubject와 Subject인퍼테이스가 동일하면 프록시는 Subject에 대한 참조자를 찾는다. 또한, Subject와 동일한 인터페이스를 제공해 실제 대상을 대체 가능해야 하며 실제 대상에 대한 접근제어, 생성, 삭제를 책임인다.
  • Subject : Proxy에 공통적인 인터페이스를 정의, RealSubject가 요청되는 곳에 Proxy를 사용할 수 있게 한다. 
  • RealSubject : 프록시가 대표하는 실제 객체 

2. 구현

필자가 이번에는 구조와 동일하게 직접 uml 다이어그램을 뭔가 좀 삐뚤삐뚤 하지만...

 

잘 구성하려고 했다.

 

 

음... 한번 코드를 확인해보자

Subject.java

public interface Subject {
    String doSubject();
}

 

Subject 인터페이스에는 우리가 공통적으로 사용할 작업을 정의한다. 

 

간단하게 Subject를 실행시킨다는 의미의 doSubject()를 선언했다.

RealSubject.java

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class RealSubject implements Subject{

    @Override
    public String doSubject() {
        log.info("do subject from Subject in RealSubject");
        sleep(1000);
        return "do subject";

    }

    private void sleep(int millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

 

RealSubject.java는 프록시가 대표하는 실제 객체이다. 

FirstProxyCache.java

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class FirstProxyCache implements Subject{

    private Subject target;
    private String info;

    public FirstProxyCache(Subject target) {
        this.target = target;
    }

    @Override
    public String doSubject() {
        log.info("First proxy called");
        if(info == null) {
            info = target.doSubject();
        }
        return info;
    }
}

 

FirstProxyCache class는 내가 사용할 Proxy이다. Subject 인터페이스를 구현하고, RealSubject에서 무언가를 한번 거치고 사용하도록 할 예정이다.

ProxyClient.java

public class ProxyClient {

    private Subject subject;

    public ProxyClient(Subject subject) {
        this.subject = subject;
    }

    public void execute() {
        subject.doSubject();
    }

}

Client.java

public class Client {

    public static void main(String[] args) {
        Subject subject = new RealSubject();
        FirstProxyCache proxyCache = new FirstProxyCache(subject);
        ProxyClient client = new ProxyClient(proxyCache);
        client.execute();
        client.execute();
        client.execute();
    }

}

 

여기서 착각하지 말아야 할 것이 Client.java가 구조에서 본 Client가 아니라는 사실이다. 이미 Client에 해당되는 class는 ProxyClient로 Subject를 참조하고 있다.(association)

결과

 

이렇게 간단하게 Proxy패턴을 이용해서 Cache 접근을 제어했다.

 

장점
• 기존 코드를 변경하지 않고 새로운 기능을 추가할 수 있다. 
• 기존 코드가 해야 하는 일만 유지할 수 있다. 
• 기능 추가 및 초기화 지연 등으로 다양하게 활용할 수 있다. 

 

 단점
• 코드의 복잡도가 증가한다.

3. 프록시 패턴 vs 데코레이터 패턴

비슷하지만 프록시 패턴은 접근을 제어하기 위해 대리자를 제공하고, 데코레이터 패턴은 객체에 추가 책임을 동적으로 추가하고, 기능을 확장하기 위한 유연한 대안을 제공한다. 

 

그래서, 프록시를 사용하고 해당 프록시가 접근 제어가 목적이라면 프록시 패턴을, 새로운 기능을 추가하는 것이 목적이라면 데코레이터 패턴을 사용하는게 좋다. 


이번 글에서는 프록시 패턴이 무엇인가에 대해 알아봤다.

 

사실 여러 강의를 통해 예제를 접햇고, 그것을 심지어 따라 해보고 공부를 해봤음에도

 

무언가 금방 잊어먹고 아직도 내 것이 되질 않았다.

 

후에 수정을 통해 좀 더 좋은 예시와 활용법을 내 코드로 변경해서 update하고싶다.