[디자인 패턴] 프록시 패턴 (Proxy Pattern)

3Beom's 개발 블로그·2023년 4월 4일
0
post-custom-banner

프록시 패턴

프록시, Proxy

  • Proxy는 대리자, 대변인으로 번역될 수 있다. 프록시 패턴에서도 유사한 개념이 적용된다.
  • 특정 객체를 사용할 때, 객체를 직접적으로 사용하는 것이 아닌, 해당 객체를 참조하고 있는 대리자 객체(= 프록시 객체)를 통해 사용하는 패턴이다.
  • 프록시 객체는 다음과 같은 특징을 갖는다.
    • 제 객체 클래스가 implements 하고 있는 인터페이스를 구현한 형태이거나, 실제 객체 클래스를 상속 받아 생성된다.
      • 실제 객체의 메서드를 그대로 활용하기 위해
    • 실제 객체와 동일한 메서드를 갖는다.
    • 실제 객체를 참조하고 있으며, 각 메서드는 실제 객체의 메서드를 그대로 활용한다.
    • 메서드의 입력값(파라미터)과 결과값은 조작되거나 변경되지 않는다.

프록시 패턴 활용 이유1 - 지연 로딩

  • 만약 프록시 패턴이 적용되지 않은 프로젝트일 경우, 특정 객체가 다루는 데이터의 크기가 크면 로드 과정에서 상당 시간이 소요될 수 있다.
  • 이러한 상황에서 프록시 객체로 껍데기만 동일하게 만들어 두면, 실제로 해당 객체가 활용되기 전까지는 시간이 소요되지 않는다. 가장 큰 이유 1!
    • 간단히 예를 들어보면, 웹 혹은 GUI 환경에서 텍스트만 다루는 TextViewer class와 큰 영상을 다루는 VideoViewer class가 있다고 가정해 보자.
    • TextViewerVideoViewer 객체를 만들고, 내부에 정의되어 있는 멤버 메서드와 필드들을 모두 활용하며 코드를 작성할 것이다.
    • 여기서 프록시 패턴을 적용하기 전과 후는 다음과 같은 차이를 만들어 낼 수 있다.
      • 프록시 패턴 적용 전
        • 실행 시 TextViewer 는 빠른 시간 내에 로드될 수 있지만, VideoViewer오랜 시간이 소요될 것이다.
        • 실제로 사용자(Client)는 VideoViewer사용할 수도, 안할 수도 있는데 일단 오랜 시간을 기다려야만 한다.
      • 프록시 패턴 적용 후
        • 각 객체들은 사용자(Client)에 의해 활용되기 전까지는 모두 형태만 동일한 껍데기 객체(=프록시 객체)로 배치되어 있다.
        • 사용자에 의해 TextViewer 객체가 활용되면 그 시점에 실제 TextViewer 객체가 생성되어 역할을 수행하게 되고, VideoViewer 객체 또한 직접적으로 활용되는 시점에 생성된다.
  • 즉, Client와 객체 사이에 껍데기 객체(=프록시 객체)를 둠으로써 객체 생성 시점을 미룰 수 있고(= 지연 로딩), 프록시 객체는 실제 객체와 동일한 메서드를 가지므로 코드 작성, 기능 동작에 문제를 발생시키지 않는다.
  • 실제로 Spring ORM 중 JPA에서 지연 로딩이 프록시 객체로 구현된다.

프록시 패턴 활용 이유2 - 흐름 제어

  • 흐름 제어 : 객체를 사용하는 측(Client)과 객체 사이에 대리인을 두어 흐름을 제어한다. 가장 큰 이유 2!
  • 프록시 패턴을 적용하지 않으면 Client의 요청이 그대로 DB에 전해지게 된다.

  • 소수의 Client의 경우 상관없지만, 트래픽이 증가할 수록 많은 양의 리소스를 처리해야 하기 때문에 DB와 소통하는 쿼리가 느려질 것이다.

  • 본 상황을 해결하려면 Client 요청에 따라 쿼리를 DB로 전달하는 모든 클래스(**Dao)마다 흐름을 제어하기 위한 코드를 추가해야 한다.

  • 또한, Dao class는 DB와의 소통에 대한 역할만 수행해야 하는데, 흐름 제어 역할도 가져버리면 객체 지향 5대 원칙 중 ‘단일 책임의 원칙(SRP)’에 위반된다.

    코드 중복 야기! + 하나의 클래스가 여러 책임을 갖게 됨! + 비효율적!

  • 따라서 중간에 프록시 객체를 넣어두면, 프록시 객체가 흐름을 제어하는 역할을 수행할 수 있다.

프록시 객체 예시

  • 위 사진은 다음과 같이 동작한다.

    • ClientSubject interface 타입의 객체를 활용하려 한다.

      • Subject interface의 구현체로 RealSubject class가 있다.
    • 이때, 일반적으로는 RealSubject class 객체를 생성하여 활용한다.

      • Subject subject = new RealSubject();
    • 하지만 프록시 패턴을 적용할 경우, 다음과 같이 Proxy class를 활용하게 된다.

      • Subject subject = new Proxy();
    • 여기서 Proxy class는 다음과 같이 정의된다.

      public class Proxy implements Subject {
      	Subject realSubject;
      
      	@Override
      	public void doAction() {
      		if (realSubject == null) {
      			realSubject = new RealSubject();
      		}
      		realSubject.doAction();
      	}
      }

프록시 패턴 장단점

  • 장점
    • 사이즈가 큰 객체가 로딩되기 전에도 프록시 객체를 통해 참조할 수 있다.
      • 컴파일 과정에서 문제가 발생하지 않는다.
    • 실제 객체 접근 과정에서 사전 처리를 적용할 수 있다.
      • 로깅, 시간 측정, 캐싱, 흐름 제어 등
  • 단점
    • 객체를 생성할 때 한 단계를 거치게 되므로, 빈번한 객체 생성이 필요한 경우 성능이 저하될 수 있다.
    • 로직이 난해해져 가독성이 떨어질 수 있다.

프록시 패턴 종류

  • 가상 프록시
    • 꼭 필요로 하는 시점(= 실제로 활용되는 시점)까지 객체의 생성을 연기하고, 해당 객체가 생성된 것 처럼 동작하도록 만들고 싶을 때 사용하는 패턴이다. (위 지연 로딩 설명)
    • 프록시 클래스에서는 작은 단위의 작업만을 처리하고, 리소스가 많이 요구되는 작업들이 필요할 경우에만 실제 클래스를 사용하도록 구현한다.
  • 원격 프록시
    • 원격으로 접근할 수 있는 객체를 로컬에 직접 가져오지 않고, 제어만 할 수 있는 프록시 객체를 로컬에 배치한다.
    • 즉, 원격의 객체와 로컬의 객체가 실제로는 서로 다른 주소 공간에 각각 존재하지만, 마치 같은 주소 공간에 있는 것 처럼 동작될 수 있다.
    • 대표적인 예로 Google Docs가 있다.
  • 보호 프록시
    • 실제 클래스에 대한 접근을 제어하기 위한 동작을 프록시 객체에 설정한다.
    • 클래스 자체에 대한 접근 권한을 설정할 수도 있고, 객체마다 서로 다른 접근 권한을 부여할 수도 있다.
  • 이 외에 캐시 프록시, 동기화 프록시, 스마트 참조자 프록시, 방화벽 프록시 등이 있다.

*JPA의 지연 로딩과 프록시 객체 활용

[JPA] 즉시 로딩과 지연 로딩

profile
경험과 기록으로 성장하기
post-custom-banner

0개의 댓글