지난 포스팅에선 객체 내부의 상태가 바뀜에 따라 객체의 행동을 바꿀 수 있는 스테이트 패턴에 대해 알아보았다. 이번엔 객체에 대한 접근을 제어하는 프록시 패턴에 대해 복습한다.

정의

어떤 객체에 대한 접근을 제어하기 위한 용도로 대리인이나 대변인에 해당하는 객체를 제공하는 패턴. 프록시는 다른 객체에 대한 대변자 역할을 한다. 프록시는 자신이 대변하는 객체와 그 객체에 접근하고자 하는 클라이언트 사이에서 여러 가지 방식으로 작업을 처리한다. 보안을 위해 인터넷을 통해 들어오는 메소드 호출을 쫓아내거나 게으른 객체들을 대신해 끈기있게 기다리는 일을 맡기도 한다.

종류

원격 프록시

원격 프록시는 다른 JVM에 들어있는 객체를 대신하는 로컬 객체이다. 프록시의 메소드를 호출하면 그 호출이 네트워크를 통해 전달되면서 원격 객체의 메소드가 호출되고 그 결과는 다시 네트워크를 통해 프록시를 거쳐 클라이언트에게 전달된다. 자바에서는 원격 JVM에 있는 메소드를 호출하기 위해 RMI를 이용한다.

Subject는 Proxy 프록시와 RealSubject 주객체가 모두 구현하는 공통 인터페이스이다. 공통으로 사용하는 인터페이스를 이용해 어떤 클라이언트에서든 프록시를 주 객체하고 똑같이 다룰 수 있다.

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

Proxy에는 작업을 처리하는 주 객체에 대한 래퍼런스가 들어있다. 클라이언트가 특정 작업을 요청했을 때, 주 객체가 필요하다면 그 래퍼런스를 이용해서 요청을 전달한다.

가상 프록시

가상 프록시는 생성하는 데 많은 비용이 드는 객체를 대신하는 역할을 맡는다. 객체가 필요할 때까지 객체의 생성을 미루는 기능도 제공한다. 가상 프록시는 객체를 생성하기 전이나 생성하는 도중에 객체를 대신하기도 하며 객체 생성이 완료되고 나면 그냥 RealSubject에 요청을 직접 전달한다. 가상 프록시를 이용하면 생성하기 힘든 자원에 대한 접근을 제어할 수 있다. 예를 들어 CD 커버를 보여주는 뷰어에서 이미지를 불러오는 동안 무언가 다른 것을 보여주고 싶을 때 가상 프록시를 사용할 수 있다.

보호 프록시 (=동적 프록시)

자바의 reflect 패키지를 이용하면 즉석에서 한 개 이상의 인터페이스를 구현하고 메소드 호출을 지정해 준 클래스에 전달하는 프록시 클래스를 만들 수 있다. 프록시 클래스가 실행중에 생성되기 때문에 이러한 자바의 기술을 동적 프록시라고 부른다.

동적 프록시는 특이하게도 프록시가 두 개의 클래스로 구성된다. Proxy 객체에 대한 모든 메소드 호출을 전달 받는 InvocationHandler가 반드시 필요하다. 이 InvocationHandler에서 RealSubject 객체에 있는 메소드에 대한 접근을 제어한다.

Proxy 클래스는 자바가 만들어주기 때문에 Proxy에게 어떤 작업을 해야 할지 알려줄 방법이 필요하다. 하지만 직접 Proxy를 작성할 수 없기 때문에 InvocationHandler를 정의하여 프록시에 대해서 호출되는 모든 메소드에 대해 응답하도록 작성한다. 프록시가 메소드 호출을 요청받으면 InvocationHandler에게 진짜 작업을 요청한다. 예를 들어 사용자의 권한에 따라 다른 페이지를 보여주고 싶을 때 보호 프록시를 사용할 수 있다.

방화벽 프록시

방화벽 프록시는 일련의 네트워크 자원에 대한 접근을 제어함으로써 객체를 공격자로부터 보호해준다. 방화벽 시스템에서 자주 볼 수 있다. 2017년을 뜨겁게 달군 랜섬웨어의 예방법으로 방화벽 설정이 주목받게 되었다.

스마트 패턴 프록시

스마트 패턴 프록시는 주 객체가 참조될 때마다 추가 행동을 제공한다. 객체에 대한 래퍼런스의 개수를 세는 것처럼 부가적인 작업을 처리할 수 있다.

캐싱 프록시

캐싱 프록시는 비용이 많이 드는 작업의 결과를 임시로 저장해 준다. 여러 클라이언트에서 결과를 공유하게 해 줌으로써 계산 시간 또는 네트워크 지연을 줄여주는 효과가 있다. 웹 서버 프록시, 컨텐츠 관리 및 퍼블리싱 시스템에서 주로 볼 수 있다.

동기화 프록시

동기화 프록시는 여러 스레드에서 주 객체에 접근하는 경우에 안전하게 작업을 처리할 수 있게 해 준다. 분산 환경에서 일련의 객체에 대한 동기화된 접근을 제어해주는 자바스페이스에서 볼 수 있다.

복잡도 숨김 프록시

복잡도 숨김 프록시는 복잡한 클래스들의 집합에 대한 접근을 제어하고, 그 복잡도를 숨겨 준다. 앞서 일련의 복잡한 서브시스템을 단순화한 퍼사드 패턴이 등장했었다. 복잡도를 숨기기 때문에 퍼사드 프록시라고도 부르지만 퍼사드 패턴이 단순히 대체 인터페이스만 제공하는 것에 비해 복잡도 숨김 프록시는 접근을 제어한다.

지연 복사 프록시

지연 복사 프록시는 클라이언트에서 필요로 할 때까지 객체가 복사되는 것을 지연시킴으로써 객체의 복사를 제어한다. 비용이 큰 객체를 대신하는 가상 프록시와 비슷하므로 변형된 가상 프록시라고 할 수 있다. 자바에서 쓰레드에 대한 안정성을 위해 제공되는 CopyOnWriteArrayList에서 등장한다.

프록시 VS 데코레이터

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

  • 프록시 패턴

원치 않는 접근으로부터 보호해주거나 커다란 객체를 로딩하는 동안 프로그램이 멈추는 것을 방지해 주거나 Subject 객체가 원격 시스템에서 돌아가고 있다는 사실을 숨겨주는 등의 기능을 제공할 수 있다.

  • 데코레이터 패턴

코드를 수정하지 않고도 객체가 할 수 있는 행동을 추가해준다. 구성과 위임을 통해서 실행중에 동적으로 새로운 행동을 추가할 수 있으며 구성요소를 감싸는 데코레이터의 개수에는 제한이 없다.

프록시 패턴 예제