ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 프록시 패턴
    스프링/스프링 AOP 2023. 9. 10. 15:56

    프록시를 사용하는 디자인 패턴 중 하나인 프록시 패턴은 접근 제어의 목적을 가지고 있는 패턴이다.

     

    프록시 패턴 도입 전

     

    Subject 인터페이스

    public interface Subject {
        String operation();
    }

     

    Subject 인터페이스의 구현체인 RealSubject

    @Slf4j
    public class RealSubject implements Subject {
        @Override
        public String operation() {
            log.info("실제 객체 호출");
            sleep(1000);
            return "data";
        }
    
        private void sleep(int millis) {
            try {
                Thread.sleep(millis);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    operation()을 호출하면 객체를 반환해주는데, 이 때 데이터를 가져오는 시간이 1초라고 가정을 해서 sleep(1000)을 넣어줬다.

     

     

    ProxyPatternClient 클래스

    public class ProxyPatternClient {
    
        private Subject subject;
    
        public ProxyPatternClient(Subject subject) {
            this.subject = subject;
        }
    
        public void execute() {
            subject.operation();
        }
    }

    클라이언트는 execute()를 통해 subject의 operation() 메서드를 실행한다.

     

     

    Test

    @Test
    void noProxyTest() {
        RealSubject realSubject = new RealSubject();
        ProxyPatternClient client = new ProxyPatternClient(realSubject);
        client.execute();
        client.execute();
        client.execute();
    }

    테스트를 진행했을 때 sleep(1000)이라는 데이터 로딩 기간이 있기 때문에 결과 값들이 1초씩 delay되어서 느리게 나온다. 이미 반환한 적이 있는 데이터를 다시 보내주는 것인데, 굳이 서버에 접근하는 것이 비효율적이다. 그래서 캐싱을 활용해서 이미 반환한 적이 있는 데이터는 실제 객체가 아니라 프록시를 호출하는 것이 프록시 패턴이다.

     

     

     

    프록시 패턴 도입

     

    CacheProxy 클래스

    @Slf4j
    public class CacheProxy implements Subject {
    
        private Subject target; // 프록시도 실제 객체를 호출해야함
        private String cacheValue; // cache 데이터
    
        public CacheProxy(Subject target) {
            this.target = target;
        }
    
        @Override
        public String operation() {
            log.info("프록시 호출");
            if (cacheValue == null) {
                cacheValue = target.operation();
            }
            return cacheValue;
        }
    }

    operation() 메서드가 기존과 다르게 조건문으로 동작한다. operation()이 처음 호출되면 실제 객체(target)의 operation()이 호출된다. 두 번째 호출부터는 cacheValue가 null이 아니기 때문에 기존에 있던 cacheValue를 호출하여 캐시 데이터를 반환해주는 것이다.

     

     

     

    사진 : 인프런 스프링 강의

    client가 요청을 하면 Proxy의 operation()을 호출하게 된다. 첫 번째 접근이면 프록시는 realSubject의 실제 객체를 호출하고, 그 뒤로는 프록시 객체의 캐시 값을 반환해주는 것이다.

     

     

    @Test
    void cacheProxyTest() {
        RealSubject realSubject = new RealSubject();
        CacheProxy cacheProxy = new CacheProxy(realSubject);
        ProxyPatternClient client = new ProxyPatternClient(cacheProxy);
        // client -> proxy -> server 순으로 연관관계
        client.execute();
        client.execute();
        client.execute();
    }

    프록시를 사용한 코드를 적용시켜서 테스트를 해보면 아래 사진과 같은 결과가 나온다.

    사진 : 인프런 스프링 강의

    처음 호출할 때에만 실제 객체를 호출한다. 이 때는 1초가 걸렸는데, 다음부터는 실제 객체가 아닌 프록시만 호출하며 시간도 바로 되는 것을 확인할 수 있다.

     

     

     

    프록시 패턴의 핵심은 RealSubject 코드와 client 코드를 전혀 변경하지 않았다는 것이다. 클라이언트 입장에서는 프록시 객체가 주입되었는지 자체를 모른다. 프록시를 이용해서 접근 제어를 하는 프록시 패턴을 잘 활용한 것이다.

Designed by Tistory.