1 분 소요

핵심기능을 하는 코드와 부가기능을 하는 코드가 있을 때 보통 핵심기능이 부가기능을 사용하도록 하고 부가기능의 구체적인 로직은 밖으로 빼는 구조로 사용합니다. 만약 부가기능이 많아지고 여러 핵심기능에 적용해야 한다면 전체적인 코드는 지저분해지고 핵심기능은 여러 부가기능을 적용해야 한다는 사실을 알고 있어야 합니다.

핵심to부가1

위의 문제점은 Proxy와 데코레이터 패턴, 프록시 패턴을 사용해서 해결할 수 있습니다.

프록시란

클라이언트가 사용하려고 하는 실제 대상인 것처럼 위장해 클라이언트의 요청을 받아주는 대리자 (토비의 스프링 p.435)

부가to핵심2

핵심기능이 부가기능의 존재를 모르게 하려면 반대로 부가기능이 핵심기능을 사용하도록 하면 됩니다. 클라이언트가 핵심기능을 바로 사용하지 못하도록 부가기능을 하는 프록시가 핵심기능 인터페이스를 구현해 클라이언트는 핵심기능 인터페이스에 의존하도록 프록시는 핵심기능 사이에 끼어 핵심기능을 호출하도록 하면 됩니다.

프록시 패턴 vs 데코레이터 패턴

프록시 패턴과 데코레이터 패턴을 구분하기 위해서는 패턴이 가지는 의도(intent)를 파악하면 됩니다. 프록시 패턴의 의도는 접근을 제어하는 것이고 데코레이터 패턴은 부가적인 기능을 수행하는 것 입니다. 두 패턴의 구조는 위 사진과 동일하기 때문에 두 의도에 대해서 잘 알고 있어야 합니다.

프록시 패턴

프록시 패턴은 접근을 제어하기 위한 의도를 가지고 있는데 대표적으로 캐시를 사용하기 위해서 사용할 수 있습니다. 데이터베이스에 있는 데이터를 가져오는데 1초가 걸린다고 가정한다면 프록시 패턴을 사용하지 않으면 총 3초가 걸리게 됩니다. 하지만 프록시 패턴을 사용해 접근을 제어하게 된다면 총 1초로 시간을 절약할 수 있습니다.

@Slf4j
public class CacheProxy implements Subject { // 접근 제어를 위한 프록시
    private Subject target; // 핵심 기능을 하는 클래스
    private String cacheValue;
    public CacheProxy(Subject target) {
        this.target = target;
    }   

    @Override
    public String operation() {
        log.info("프록시 호출");
        if (cacheValue == null) {
            cacheValue = target.operation();
        }
        return cacheValue;
    }
}
@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();
        }
    }
}
@Test
    void cacheProxyTest() {
        RealSubject realSubject = new RealSubject();
        CacheProxy cacheProxy = new CacheProxy(realSubject);
        ProxyPatternClient client = new ProxyPatternClient(cacheProxy);

        client.execute();
        client.execute();
        client.execute();
    }

데코레이터 패턴

데코레이터 패턴은 부가기능을 부여하기 위한 의도를 가지고 있고 대표적으로 KEEPER R2의 API 성능 측정기가 있습니다.3

데코레이터패턴

프록시에서 핵심기능API를 호출하고 시간을 측정한 다음 일정한 포맷팅을 거쳐 출력해주는 것을 확인할 수 있습니다.

마치며

패턴단점4

위의 사진처럼 프록시가 많아지고 여러 핵심기능에 적용해야 한다면 인터페이스를 구현하고 위임하는 코드 작성 양이 늘어난다는 점을 예상할 수 있습니다. 이런 문제점은 다이내믹 프록시를 사용해서 해결할 수 있습니다. 다음 글은 다이내믹 프록시에 대한 글을 작성하겠습니다.

참고문헌

[1] (참고: 토비의 스프링 p.429)

[2] (참고: 토비의 스프링 p.430)

[3] (참고: https://02ggang9.github.io/jpa/KEEPER-%ED%94%84%EB%A1%9C%ED%95%84-%EC%A1%B0%ED%9A%8C-%EC%B5%9C%EC%A0%81%ED%99%94/)

[4] (참고: 김영한 - 스프링 핵심원리 고급편 - 프록시 패턴 p.33)

카테고리:

업데이트: