공부공부/Java

[Java] 프록시패턴 (Proxy Pattern)

이숭간 2021. 8. 7. 20:41
728x90

프록시 패턴이란?

디자인패턴중 하나로, 다른 무언가와 이어지는 인터페이스 역할을 하는 클래스를 의미한다.

먼저 프록시라는 용어를 이해하고 넘어가보자.

0.  프록시 라는 용어의 의미

소프트웨어 기술에서 종종 등장하는 '프록시'의 용어를 정리하고 넘어가자.

프록시는 어디서 많이 주워들었다. 리버스프록시,프록시서버, lazy전략에서 사용되는 프록시객체, 프록시패턴 등등

프록시란 '대리'라는 의미로 프록시에게 어떤 일을 대신 시키는것이다. 

예를 들어 보안분야에서, 보안상의 이유로 서버를 외부에 노출시키지 않기 위해 서버와 클라이언트단 중간에서 접점을 담당하는 서버를 보고 프록시서버라고 부른다. 

프록시서버가 궁금하다면 다음글을 읽어보자. (https://brownbears.tistory.com/191)

 

1.  프록시 패턴

인터페이스를 사용하고 실행시킬 클래스에 대해 객체가 들어갈 자리에 대리자 객체를 대신 투입하여,

클라이언트는 실제 실행시킬 클래스에 대한 메소드를 호출하여 반환값을 받는지

대리 객체의 메소드를 호출해서 반환값을 받는지 모르게 하는것을 말한다.

프록시 패턴

위 그림에서 보듯, 클라이언트가 Subject인터페이스의 request()를 호출하면 구현클래스인 RealSubject의 request()가 호출된다. 

이때 프록시가 대신 RealSubject의 request()메서드를 호출하고 그 반환값을 클라이언트에게 전달하는것이다.

 

위 그림을 코드로 구현해보자.

package com.programmers.java.proxyPattern;

public interface Subject {
    String request();
}
package com.programmers.java.proxyPattern;

public class RealSubject implements Subject {

    @Override
    public String request() {
        return "HelloWorld";
    }
}
package com.programmers.java.proxyPattern;

public class Proxy implements Subject {

    private final RealSubject realSubject = new RealSubject();

    @Override
    public String request() {
        return realSubject.request();  //프록시가 실제의 메소드를 호출한다.
    }

}
package com.programmers.java.proxyPattern;

public class Main {

    public static void main(String[] args) {
        // Subject클래스의 메소드를 호출하는것이아닌 프록시클래스의 메소드를 호출한다.
        Subject subject = new Proxy();
        System.out.println(subject.request()); // 내부적으로 Subject의 메소드를 호출한다.

    }
}

이렇게 코드를 작성할경우

1. 인터페이스를 두기때문에 개발코드에서 구현체에 영향을 받지 않는다.

2. 구현클래스에 직접 접근하지않고 Proxy를 통해 한번 우회하여 접근하도록 되어있다. ( 흐름제어 )

 

 

 

 

 

 

 

주의할점은 흐름을 제어하는것만 담당하고 결과를 조작하거나 변경할순 없다.

 

2. 프록시 패턴은 왜 사용하는가

0. 흐름을 제어할수 있다. 그렇다면 흐름제어는 왜 필요한가??

흐름을 제어하가 위한것이 프록시패턴을 이용하는 가장 큰 이유인데 

간단히 말하면 동기적인 처리를 최대한 비동기적으로 처리하기 위함이라고 생각한다.

 

프록시 객체를 사용하지않는 아래 사진과같은 상황을 생각해보자.

많은 양의 리소스를 필요로하는 상황에서 디비쿼리가 엄청나게 느려질 수 있다. 

이럴때 지연초기화를 위한 코드작성을 해야하는데 이를 모든 클래스마다 직접 넣어버리면 엄청나게 많은 코드중복이 발생할것이다. (여기서도 AOP가 떠올랐다.)

 

출처 : https://refactoring.guru/design-patterns/proxy

따라서 아래사진과같이 프록시객체를 이용하면 요청을 프록시객체가 먼저 받은뒤에 흐름을 제어하여 디비에 쿼리를 날릴 수 있게된다.

출처 : 출처 : https://refactoring.guru/design-patterns/proxy

 

1. 실제 메소드가 호출되기 이전에 필요한 기능(전처리등의)을 구현객체 변경없이 추가할 수 있다.

 (코드변경의 최소화)

 

2. 캐시를 사용할 수 있다.

프록시가 내부캐시를 통해 데이터가 캐시에 존재하지 않는 경우에만

주체클래스에서 작업이 실행되도록 할 수 있다. 

 

 

 

참고 :

https://refactoring.guru/design-patterns/proxy

https://limkydev.tistory.com/79