본문 바로가기

OOP, FP/디자인패턴

헤드퍼스트 디자인 패턴: 13. 프록시 패턴


지난 포스팅에선 객체 내부의 상태가 바뀜에 따라 객체의 행동을 바꿀 수 있는 스테이트 패턴에 대해 알아보았다.


이번엔 객체에 대한 접근을 제어하는 프록시 패턴에 대해 복습한다.


프록시 패턴

어떤 객체에 대한 접근을 제어하기 위한 용도로 대리인이나 대변인에 해당하는 객체를 제공하는 패턴

프록시는 보통 다른 객체에 대한 대변자라고 봐도 무방하다.


프록시는 자신이 대변하는 객체와 그 객체에 접근하고자 하는 클라이언트 사이에서 여러 가지 방식으로 작업을 처리한다.

보안을 위해 인터넷을 통해 들어오는 메소드 호출을 쫓아내거나 게으른 객체들을 대신해 끈기있게 기다리는 일을 맡기도 한다.


프록시의 종류


(1) 원격 프록시


- 원격 프록시는 다른 JVM에 들어있는 객체의 대변인에 해당하는 로컬 객체이다.

  프록시의 메소드를 호출하면 그 호출이 네트워크를 통해 전달되어 결국 원격 객체의

  메소드가 호출되고 그 결과는 다시 네트워크를 통해 프록시를 거쳐 클라이언트에게 전달된다.    



- Subject: Proxy와 RealSubject가 모두 구현해야 하는 인터페이스.

  이를 이용해 어떤 클라이언트에서든 프록시를 주 객체하고 똑같은 식으로 다룰 수 있다.


- RealSubject: 실제 작업을 대부분 처리하는 객체. Proxy는 이 객체에 대한 접근을 제어하는 객체이다.


- Proxy: 실제 작업을 처리하는 객체에 대한 래퍼런스가 들어있다.

  이를 이용해 실제 객체가 필요하면 그 래퍼런스를 이용해서 요청을 전달한다.


* Proxy에서 RealSubject의 인스턴스를 생성하거나, 그 객체의 생성 과정에 관여하는 경우가 있다.

예) 뽑기기계를 원격에서 모니터링 하는 경우


(2) 가상 프록시

- 가상 프록시는 생성하는 데 많은 비용이 드는 객체를 대신하는 역할을 맡는다.

  실제로 진짜 객체가 필요하게 되기 전까지 객체의 생성을 미루게 해 주는 기능도 제공한다.

  객체 생성 전이나 도중에 객체를 대신하기도 하며 객체 생성이 완료되고 나면 그냥 RealSubject에 요청을 직접 전달한다.

  즉, 생성하기 힘든 자원에 대한 접근을 제어할 수 있다.

예) CD 커버를 보여주는 뷰어에서 이미지를 불러오는 동안 무언가 다른 것을 보여주고 싶을 때


(3) 동적 프록시

- 자바의 reflect 패키지를 이용하면 즉석에서 한 개 이상의 인터페이스를 구현하고 메소드 호출을 잊정해 준 클래스에 전달할 수 있는 

  프록시 클래스를 만들 수 있다.

  실제 프록시 클래스가 실행중에 생성되기 때문에 이러한 자바의 기술을 동적 프록시라고 부른다.


- 동적 프록시는 특이하게도 프록시가 두 개의 클래스로 구성된다.

  Proxy 객체에 대한 모든 메소드 호출을 전달 받는 InvocationHandler가 반드시 필요하다.

  이 InvocationHandler에서 RealSubject 객체에 있는 메소드에 대한 접근을 제어한다.


- Proxy 클래스는 자바가 만들어주기 때문에 Proxy 클래스한테 어떤 작업을 처리할 지 알려주기 위해서는

  Invocation에 작성해야 한다.

  InvocationHandler는 프록시에 대해서 호출되는 모든 메소드에 대해 응답하는 역할을 맡는다.

  프록시가 메소드 호출을 요청받으면 InvocationHandler에게 진짜 작업을 요청하게 되는 것이다.

예) 사용자의 권한에 따라 다른 페이지를 보여주고 싶을 때


(4) 방화벽 프록시

- 일련의 네트워크 자원에 대한 접근을 제어함으로써 객체를 공격자로부터 보호해준다.

  우리의 피시나 기업용 방화벽 시스템에서 자주 볼 수 있다.

  오늘 난리난 랜섬웨어 초기 예방법으로 방화벽 설정이 주목받게 되었다.


(5) 스마트 패턴 프록시

- 주 객체가 참조될 때마다 추가 행동을 제공한다.

  객체에 대한 래퍼런스의 개수를 센다든가 하는 식으로 부가적인 작업을 처리할 수 있다.


* HandlerInterceptorAdaper, HandlerMethodArgumentResolver가 이러한 스마트 패턴 프록시인가 ..?


(6) 캐싱 프록시

- 비용이 많이 드는 작업의 결과를 임시로 저장해 준다.

  여러 클라이언트에서 결과를 공유하게 해 줌으로써 계산 시간 또는 네트워크 지연을 줄여주는 효과가 있다.

  웹 서버 프록시, 컨텐츠 관리 및 퍼블리싱 시스템에서 주로 볼 수 있다.


* Guava캐시, Eh캐시와 같은 라이브러리가 캐싱 프록시 패턴을 이용한 것 같다.


(7) 동기화 프록시

- 여러 스레드에서 주 객체에 접근하는 경우에 안전하게 작업을 처리할 수 있게 해 준다.

  분산 환경에서 일련의 객체에 대한 동기화된 접근을 제어해주는 자바스페이스에서 볼 수 있다.


* 아직 쓰레드와 동기화가 필요한 코드를 접해보지 못했다. 언젠가 만나게 되길


(8) 복잡도 숨김 프록시

- 복잡한 클래스들의 집합에 대한 접근을 제어하고, 그 복잡도를 숨겨 준다.

  앞서 일련의 복잡한 서브시스템을 단순화한 퍼사드 패턴이 등장했었다.

  복잡도를 숨기기 때문에 퍼사드 프록시라고도 부르지만

  퍼사드 패턴이 단순히 대체 인터페이스만 제공하는 것에 비해 복잡도 숨김 프록시는 접근을 제어한다.


(9) 지연 복사 프록시

- 클라이언트에서 필요로 할 때까지 객체가 복사되는 것을 지연시킴으로써 객체의 복사를 제어한다.

  비용이 큰 객체를 대신하는 가상 프록시와 비슷하므로 변형된 가상 프록시라고 할 수 있다.

  자바에서 쓰레드에 대한 안정성을 위해 제공되는 CopyOnWriteArrayList에서 등장한다.


프록시 VS 데코레이터

(1) 프록시 패턴은 객체에 대한 접근을 제어하고, 데코레이터 패턴은 객체를 치장하여 기능을 확장한다.


(2) 프록시 패턴

- 원치 않는 접근으로부터 보호해주거나 커다란 객체를 로딩하는 동안

  프로그램이 멈추는 것을 방지해 주거나 Subject 객체가 원격 시스템에서 돌아가고 있다는 사실을

  숨겨주는 등의 기능을 제공할 수 있다.


(3) 데코레이터 패턴

- 코드를 수정하지 않고도 객체가 할 수 있는 행동을 추가해준다. 

  구성과 위임을 통해서 실행중에 동적으로 새로운 행동을 추가할 수 있으며

  구성요소를 감싸는 데코레이터의 개수에는 제한이 없다. 


프록시 패턴처럼 요청을 중간에 가로챈다거나 메소드 호출을 제어한다거나 이런 기능을 스프링에서 많이 본 것 같다.

컨트롤러 실행 전후에 공통적인 작업을 처리하는 HandlerInterceptorAdapter

컨트롤러의 메서드 인자에 빈을 넘겨주기 위한 HandlerMethodArgumentResolver

간편한 설정으로 강력한 보안을 제공하는 SpringSecurity

등등.. 


학교에서 스프링을 접한지 6개월, 직장 생활 3개월 총 9개월 동안 알게 모르게 디자인 패턴이 적용된 기능들을 사용하고 있었다.

프록시 패턴은 예제의 코드량이 너무 많아서 차마 포스트로 옮기지 못하겠다;;


객체에 대한 접근을 제어하는 프록시 패턴에 대한 복습을 마치겠다.


1. 스트래티지 패턴

2. 옵저버 패턴

3. 데코레이터 패턴

4-1. 팩토리 메서드 패턴

4-2. 추상 팩토리 패턴

5. 싱글턴 패턴

6. 커맨드 패턴

7. 어댑터 패턴

8. 퍼사드 패턴

9. 템플릿 메소드 패턴

10. 이터레이터 패턴

11. 컴포지트 패턴

12. 스테이트 패턴

13. 프록시 패턴