스프링 핵심 원리 - 고급편 - 섹션4. 프록시 패턴과 데코레이터 패턴
CS/김영한 스프링 강의

스프링 핵심 원리 - 고급편 - 섹션4. 프록시 패턴과 데코레이터 패턴

 

 

일단 v1, v2, v3를 만든다.

 

 

 

v2

수동으로 빈에 등록하려고 여기선 @Controller를 사용하지 않음

 

v3

얘내들은 컴포넌트 스캔에 걸리도록 정직하게 어노테이션을 걸어준다.

 

 

새로운 요구사항으로 원본 코드는 건들지 말고 추적 기능을 추가해야 한다. 근데 앞뒤로 있어야 넣을 수 있는 추적 기능을 어떻게 수정 없이 넣을 수 있는가? 프록시를 쓰면 된다.

 

프록시는 대신 실행해주는거라고 보면 된다. 여기선 원본 코드와 똑같은데 기능이 조금 더 추가된 프록시를 실행하는 거다.

 

접근 제어가 목적이면 프록시 패턴으로, 새로운 기능 추가가 목적이면 데코레이터 패턴이라고 부르기로 했다.

 

 

이해를 위한 예제 코드를 작성하자.

 

그냥 프록시가 뭔지를 보여주기 위한 빈 프록시다.

만약 여러번 실행했을 때, 처음에 실행할 때 캐시에 저장했다가 이후 불러올 때 캐시에 저장된 걸 꺼내오는 것도 접근제어의 일부다. 이걸 해보자.

 

 

프록시들끼리 같은 인터페이스를 공유하고 있고, 클라이언트는 어떤거든 같은 인터페이스를 받기 때문에 중간에 추가할 수 있는거다. 그럼 execute() 함수 안에 추가적인 코드 작성 없이 캐시 기능을 추가할 수 있었다.

 

 

이제 데코레이터도 한번 해보자. 일단 안했을 때

 

 

사실 프록시와 거의 같다.

 

데코레이터 기능을 넣어보자. 아까는 접근 제어라서 프록시 패턴이라 한 거고, 이건 부가 기능 추가라 데코레이터 패턴이라고 하는거다.

 

 

여기에 실행 시간도 재는 새로운 기능도 넣어보자.

 

얘도 같은 부모를 상속하기 때문에 이어받고 이어받고.. 가 가능한 것이다.

 

데코레이터를 만들다보면 안에 component를 계속 받아 실제 기능에 영향을 주는지 헷갈릴 수도 있기 때문에 또 decorator라는 인터페이스를 따로 만들어 상속하게 할 수도 있다. 이건 자유다.

의문이 들 수 있는건 사실 프록시 패턴과 데코레이터 페턴은 거의 같다는 것이다. 실제로 같은 경우도 나온다. 근데 왜 굳이 다르게 취급하는가?

그것은 의도에 따라 다르다. 만약 접근을 제어하는 거라면 프록시 패턴이라고 하고, 추가적인 기능을 부여하는 거면 데코레이터 패턴이라고 한다.

 

 

이제 이걸 실제로 적용해보자. 안의 기능 코드는 전혀 건드리지 않고 추가하는거에 주목하면 된다.

일단 가장 간단한 v1부터

 

사실 아까 했던 프록시용 클래스, 인스턴스 만들고 직접 적용 했던것을 빈에 등록하는 단에서 적용하는 것이다. 다만 프록시에서 실제 함수를 실행해야 하기에, 여기서 원래 실행하려고 했던 클래스를 target으로 받아 실행한다.

 

여기가 핵심인데, 원래 설정에서 빈을 정상적인 클래스로 주던것과 달리, 여기 단에서 프록시를 거친 뒤에 주는거다. 그래서 그냥 가져다 사용하는 v1내부 입장에선 모른채로 그냥 사용이 가능한 것이다.

 

 

이제 구체 클래스만 있는 v2에도 적용해보자.

 

 

기능을 추가한다.

 

같은 ConcreteLogic 부모를 상속한다는게 핵심이다.

 

클라이언트는 어찌됐든 ConcreteLogic만 받으면 되어서 가능한 일.

 

 

프록시에서 했던 것처럼 빈에 등록시킬 때 프록시를 등록시키자.

얘는 인터페이스를 상속하는게 아니라 클래스를 상속하는 거라 implements가 아닌 extends인걸 알 수 있다.

 

 

 

그럼 이제 원본 코드를 수정하지 않고 프록시를 거치는 것으로 부가기능을 붙일 수 있었다. 이론적으론 인터페이스를 만드는게 좋지만 거의 변경할 일이 없는 클래스까지 일일히 하는건 좀 번거로울 때가 있다. 그래서 반드시 인터페이스를 만들 필요는 없다.

그런데 사실 하는건 logtrace 기능 하나인데 이걸 위한 프록시 클래스를 controller용, service용, repository용, 설정용 등 너무 많이 만들었고, 안의 내용들도 trace.begin() ... trace.end 로 다 똑같다는 것이다. 이건 다음 섹션에서 하는 동적 프록시로 해결할 수 있다.