[Design Pattern] Proxy

[디자인 패턴][구조 패턴] 프록시

Posted by ChaelinJ on April 24, 2021

Proxy

실제 기능을 수행하는 객체 대신 가상의 객체를 사용해 로직의 흐름을 제어하는 디자인 패턴

  • 프록시 클래스는 Wrapper로서의 역할 수행
  • 클라이언트 쪽에서 실제 실행시킬 클래스의 객체를 통해 결과값을 받는지, 대리자 객체를 통해 결과값을 받는지 전혀 모르게 처리합니다.
  • 프록시 클래스는 흐름제어만 할 뿐 결과값을 조작하거나 변경시키면 안 됩니다.

다이어그램

  • 프록시 클래스(대리자)는 RealSubject(실제 서비스)와 같은 이름의 메서드를 구현합니다.
    • 인터페이스를 이용
  • 프록시 클래스는 RealSubject에 대한 참조 변수를 갖습니다.
    • 조합(Composition)을 이용
  • 프록시 클래스는 RealSubject와 같은 이름을 가진 메서드를 호출하고 그 값을 클라이언트에게 돌려줍니다.
    • 클라이언트는 프록시를 중간에 두고 프록시를 통해서 RealSubject와 데이터를 주고 받습니다.
  • 프록시 클래스는 RealSubject의 메서드 호출 전후에도 별도의 로직을 수행할 수 있습니다.

프록시가 사용되는 세 가지 방법

1. Virtual Proxy (가상 프록시)

실제 클래스가 리소스 집약적인 경우에 사용됩니다.

예를들어, 실제 클래스가 해상도가 높은 이미지를 처리해야하는 경우 인스턴스화 할 때 많은 메모리를 사용하게 되는데, 이런 이미지들에 동시에 많은 접근이 이루어진다면 시스템에 부하가 많이 가게 됩니다.

프록시 클래스에서 자잘한 작업들을 처리하고 리소스가 많이 요구되는 작업들이 필요할 때에만 실제 클래스를 사용하도록 구현할 수 있습니다.

리소스 집약적인 객체가 실제로 필요해질 때까지 라이트한 버전의 프록시 인스턴스로 전처리 등 필요한 작업을 수행할 수 있습니다.

2. Protection Proxy (보호 프록시)

실제 클래스에 대한 접근을 제어하기 위한 경우에 사용됩니다.

프록시 클래스에서 클라이언트가 실제 클래스에 대한 접근을 허용할지 말지 결정하도록 할 수 있습니다. 어떤 접근 권한을 가지고 있는지에 따라 그에 맞는 주체 클래스의 메소드를 호출하도록 구현할 수 있습니다.

즉, 어떤 클랑이언트인지에 따라 서로 다른 방식으로 요청을 처리할 수 있습니다.

3. Remote Proxy (원격 프록시)

프록시 클래스는 로컬에 두고, 실제 클래스는 Remote로 존재하는 경우입니다.

즉 동일한 물리적, 또는 가상 공간에 있지 않은 시스템을 로컬 프록시를 통해 로컬에 있는 것처럼 표현하는 것입니다.

Google Docs가 대표적인 예시입니다. 브라우저는 브라우저대로 필요한 자원을 로컬에 가지고 있고, 또 다른 일부 자원은 Google 서버에 있는 형태입니다.


예제와 이해 - 자원관리 프록시

이미지를 load하고, display하는 과정을 Image 인터페이스를 통해 표현했습니다.

처음 load엔 시간이 걸린다고 생각하고 가상의 지연을 주었습니다. 코드로 확인할 수 있습니다.

Image - interface

loadFromDisk를 통해 객체의 fileName 필드에 fileName을 저장하고, display를 통해 load된 파일을 출력합니다.

RealImage

실제 클래스에선 load할 때마다 지연이 발생하고, display 시 기존에 load된 파일이 있는지 확인하는 부분이 없습니다.

ProxyImage

실제 클래스의 동작을 수행하기 전, 이미 load 되어있는 파일의 경우 다시 load하며 발생하는 시간 낭비를 방지할 수 있습니다.

추가적으로 display 하기 전 load된 파일이 있는지 확인을 통해 발생될 문제를 방지할 수 있습니다.

Main

다양하게 수행을 거쳤습니다.

결과는 다음과 같습니다.

No files loaded
---------------
Load: image01.jpg
Display: image01.jpg
---------------
Display: image01.jpg
---------------
Load: image02.jpg
Display: image02.jpg

이미 load된 image01.jpg를 다시 load했으나 출력을 보면 load에 대한 수행은 중복되지 않았고, display만 수행된 것을 확인할 수 있습니다.

이처럼 자원관리 프록시를 이용하면 실제 객체가 비용이 많이 드는 자원을 생성할 때, 중복 생성을 방지해 자원을 효율적으로 이용할 수 있습니다.

다른 예제

위 예제는 다음 링크를 참조하였습니다. 추가적으로 다른 프록시에 대해 이해할 수 있는 예시 코드들이 있으니 참고하시면 좋을 것 같습니다.

https://effectiveprogramming.tistory.com/entry/Proxy-%ED%8C%A8%ED%84%B4%EA%B3%BC-%EA%B7%B8-%ED%99%9C%EC%9A%A9

장점

  • 기본 객체의 리소스가 무거운 경우, 프록시 객체에서 간단한 처리를 맡아 함으로써 부하를 줄일 수 있습니다.
  • 기본 객체에 대한 수정 없이 프록시 객체를 통해 클라이언트에서의 사용과 기본 객체 사이에 일련의 로직을 추가할 수 있습니다.
  • 프록시는 기본 객체와 클라이언트 요청 사이에 있기 때문에 일종의 보안 역할이기도 합니다.
  • 구조나 코드 구현이 간단합니다.

단점

  • 프록시 객체가 중간에 껴있기 때문에, 간혹 응답이 느려질 수 있습니다. (캐싱 이전)

참조

감사합니다.

Text by Chaelin. Photographs by Chaelin, Unsplash.