leelee.log

[Spring] Spring AOP를 구현하는 Proxy Pattern 본문

개발/Backend

[Spring] Spring AOP를 구현하는 Proxy Pattern

leeleelee3264 2020. 2. 16. 15:23

spring framework

 스프링이 제공해주는 제일 큰 기능으로는 IoC, AOP, PSA가 있다. 이중 AOP는 Aspect Oriented Programming의 줄임말이라고 하는데 강의를 들어보니 Aspect가 핵심이다. Aspect는 관점이라는 뜻이 있는데 나는 국면이라고 해석을 했다. AOP의 주요 기능은 내가 원하는 행동을 매번 어느 특정 국면(또는 시점)에 들어왔을 때 할 수 있게 해준다.

 예를 들어서 Client로부터 Controller가 Request를 받고, Response를 작성해서 return하는 시간을 측정한다고 하자. Controller가 하나면 한 번 만 코드를 작성하면 되니까 상관이 없겠지만 AController, BController, CController처럼 컨트롤러가 여러개면 매번 똑같은 코드를 작성해줘야 한다. 상당히 번거롭고, 코드가 길어진다. 이럴 필요 없이 한 번만 코드를 작성하고, 내가 원하는 국면(시점)에 실행시켜줘~ 하고 스프링에게 알려주기만 하면 스프링은 묵묵하게 내 요청을 들어준다. 이게 바로 Spring의 AOP이다.

 AOP를 구현하는 방법은 여러개라고 한다. 내 요청을 코드 컴파일 시점에 넣어주거나 로딩 시점에 넣어주거나 등등... 내가 지금 공부하고 있는 Spring에서는 디자인 패턴 중 하나인 Proxy Pattern을 이용해서 구현했다고 한다. 오늘은 이 Proxy Pattern에 대해 알아보겠다.

 

*Bean과 IoC에서 배운 개념처럼 Spring은 객체를 Singleton Pattern으로 관리하고, AOP를 Proxy Pattern으로 구현했다고 하니 Spring에서는 디자인 패턴이 많이 들어가 있는 것 같다.*

 


 

 Proxy Pattern의 특징은 패턴을 도입함으로 인해 코드를 몇 줄 더 추가하는 일이 있어도 이미 짜여져서 작동하고 있는 코드는 수정하지 않는게 원칙이라고 한다. 내가 원하는 일은 대부분 성능 측정, 로그 출력등의 크리티컬한 영역이 아니고 부차적인 영역인데 이걸 본코드 영역에 직접 넣지 않고, 본코드와 동일한 interface를 상속받는 class를 만들어 거기다가 내가 원하는 일+원래 돌아가야되는 일이 되도록 짜라는 소리였다. 처음에는 이게 무슨 말인지 잘 몰랐는데 예제 코드를 구현하다 보니 이해가 갔다. 그런데 이번에는 코드를 가져오기 전에 대충 Proxy의 개념을 먼저 설명하겠다.

 

proxy server

 나는 Http를 공부하고 있을 때 처음으로 'proxy'라는 단어를 들어봤다. Proxy 서버에 대한 내용이었는데 간략하게 말하면 내가 어떤 페이지를 요청하면 사용자인 입장인 나는 내 요청이 바로 본 서버로 들어간 후 응답이 온다고 생각하지만, 사실은 나와 본서버 사이에 Proxy 서버라는 간이 서버가 있다. Proxy 서버의 역할은 내가 자주 요청하는 페이지 (ex 네이버, 페이스북 등등)을 저장해두고 내가 요청을 하면 본서버에 갈 것도 없이 그대로 나에게 저장했던 페이지를 주는 것이다. 내가 자주 요청하는 만큼 매번 본서버에 다녀오려면 비효율적이니 저장을 해서 응답시간을 줄인 것이다.

 지금 공부하고 있는 Proxy Pattern의 개념도 비슷하다. 어쨌든 Proxy가 '사이에 끼어있는 무엇'이라는 개념은 어디서든 변하지 않는다. 이제 한 번 구현을 해 보겠다. 밑의 그림은 내가 구현할 코드들의 구조를 보기 위해 만든 UML이다.

 

 

구조 UML

 

Skeleton.class

-Client 코드인 BodyImpl과 프록시인 ProxyImpl 이 구현할 Skeleton interface. 메소드 doSomething은 각 클래스의 특성에 맞게 다르게 구현된다.

 

BodyImpl.class

-원래 동작하는 Client코드인 BodyImpl. 여기서는 자기의 할 일만 하고 내가 부수적으로 원하는 일을 하는 코드를 포함하지 않는다.

 

ProxyImpl.class

-Proxy Pattern의 핵심인 ProxyImpl 클래스. 눈여겨 봐야할 부분은 특정 조건이 맞을때만 BodyImpl 객체를 진짜로 생성한다는 부분이다. 그리고 BodyImpl과는 다르게 내가 원하는 부수적인 일(stopwatch를 사용해서 성능 측정)이 포함이 되어있고 원래 일을 하는 Client 코드 BodyImpl 메소드를 호출한다는 부분도 아주 중요하다. 결국 이것 때문에 부수적인 일+ 원래 해야하는 일이 Client 코드 수정없이 추가가 가능해졌다.

 

Caller.class

-Client코드를 사용(호출)하기 위한 용도의 Caller 클래스.

 

ProxyTest.class

-Test코드. ProxyImpl 객체가 만들어진다는 사실이 중요하다. BodyImpl은 조건이 맞는다면 생성이 될 것이고 아니라면 프록시인 ProxyImpl만으로 작업을 끝낼 것이다. 쓸모없을 경우 BodyImpl을 생성하지 않아 자원 절약을 할 수 있다! 

 

 

결과

원래 더 복잡하게 짜야하지만 ProyImpl에 selection 변수를 두어 true, false로 BodyImpl을 생성하거나 생성하지 않았다.

BodyImpl 생성 안 됨
BodyImpl도 생성이 됨
ProxyImpl없이 BodyImpl만 실행

그냥 BodyImpl만 생성해서 실행했더니 성능을 측정하는 부분이 사라졌다!

 

 


 디자인 패턴을 막 배우기 시작해서인지 아니면 공식 응용을 잘 못해서 수학을 잘 못하던 나의 특성 때문인지 어떤 때 프록시 패턴을 사용할지 확실하게 감이 올까? 하는 의구심이 들었다. 이런 의구심에 도움이 될까 하여 프록시 패턴을 흔하게 사용하는 상황을 찾아봤다.

 

1. 객체를 생성할 때 많은 자원(메모리, cpu 등등)이 들 때 (내 예제)

 예제에서는 Client 코드나 프록시 둘 다무거운 게 없었다. 그런데 만약 내가 사용하게 될지말지 모르는데 일단 구현해둔 객체가 엄청 무거워서 성능을 잡아먹는다면 별로 달갑지 않을 것이다. 이때 Proxy pattern을 이용해서 프록시를 먼저 만들고, 진짜 그 객체를 사용해야할 조건이 충족 되었을 때 Client 코드를 만들면 자원도 절약이 되고 좋다. 이런 형태를 구조만 만들었다고 해서 Skeleton이라고 하는걸 어디서 본 것 같은데... 아니면 Lazy initialization라고 한다.

 

2. 캐쉬를 사용해서 성능 개선을 하고 싶을 때

 아까 prxoy server의 개념이 있었는데 이 개념을 그대로 코드로 녹여서 만들었다. 매번 Client 코드에 요청을 보내서 만들지 말고 한 번 Client 코드 에서 만들어서 보내준 정보를 캐싱해서 요청이 오면 프록시가 알아서 캐쉬된 정보를 보내주면 속도 면에서 훨씬 좋다.

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

 

Design Patterns: Proxy in Java

/ Design Patterns / Proxy / Java Proxy in Java Proxy is a structural design pattern that provides an object that acts as a substitute for a real service object used by a client. A proxy receives client requests, does some work (access control, caching, etc

refactoring.guru


3. 등급에 따라서 정보에 대한 접근을 다르게 해 보안을 견고하게 하고 싶을 때

 맨처음에는 그럼 처음부터 등급별로 볼 수 있게 코드를 짰으면 되는거 아니었을까? 싶었는데 Proxy Pattern의 특징은 변한 요구 사항을 구현은 해야하는데 원래의 코드에 직접 수정을 가하지 말아야할 때 필수로 사용하는 것 이었다. 처음에는 보안 등급에 따른 차등 정보제공이 없었는데 갑자기 이제는 차등제공을 해야 할 때 사용한다는 얘기 아니었을까.

https://jdm.kr/blog/235

 

프록시 패턴(Proxy Pattern) :: JDM's Blog

프록시 패턴 정의 실제 기능을 수행하는 객체Real Object 대신 가상의 객체Proxy Object를 사용해 로직의 흐름을 제어하는 디자인 패턴입니다. 프록시 패턴 특징 원래 하려던 기능을 수행하며 그외의 부가적인 작업(로깅, 인증, 네트워크 통신 등)을 수행하기에 좋습니다. 비용이 많이 드는 연산(DB 쿼리, 대용량 텍스트 파일 등)을 실제로 필요한 시점에 수행할 수 있습니다. 사용자 입장에서는 프록시 객체나 실제 객체나 사용법은 유사하므로 사용성이

jdm.kr

 

4. DB에 왔다갔다 할 때 혼선을 방지하기 위해 트랜잭션 처리를 할 때

 이때도 본 역할(db에 들어가서 crud하기)를 수행하는 코드가 있고 그 코드 사이사이로 트랜잭션 처리를 위한 코드가 들어가있다. Spring에서 흔히 사용하는 @Transcational 어노테이션도 proxy pattern으로 되어있다. 링크는 @Transcational을 사용할 때 알아두면 좋은 추가 옵션 설정하는 방법

https://goddaehee.tistory.com/167

 

[Spring] Transactional 정리 및 예제

[Spring] @Transactional 정리 및 예제 안녕하세요. 갓대희 입니다. 이번 포스팅은 [ 스프링 어노테이션 @Transactional ] 입니다. : ) 들어가기 앞서...... SI를 할때, 현재 어느 쇼핑몰 운영을 맡으며 개발 소..

goddaehee.tistory.com

 

'개발 > Backend' 카테고리의 다른 글

[Spring] Bean LifeCycle  (0) 2020.03.13
[Spring] PSA란 무엇인가?  (2) 2020.03.05
[Spring] IoC 컨테이너와 Bean, DI  (0) 2020.02.23
[Spring] @annotaion으로 Spring AOP 기능 구현  (0) 2020.02.18
[Spring] Inversion of Control  (0) 2020.02.08