본문 바로가기
Spring&SpringBoot/<토비의 스프링 3.1 Vol1.1>, 이일민

6장 AOP part1

by 민휘 2023. 4. 11.

6장은 양이 많아서 이번 주는 1절부터 4절까지 읽었다.

이번주는 이해가 잘 안 됐던 주이기도 하다. 리플렉션에 대해서 들어보기는 했는데 이렇게 써보는게 처음이라 낯설기도 했고,

무엇보다 다이나믹 프록시가 자바 코드로 보이는 것이 아니니 생소했다.

그리고 양이 너무.. 많다..

나의 언어로 바꾸느라 애를 먹었던 주이기도 하다.

 

6장은 서비스 코드에서 트랜잭션의 완전한 분리를 위해 개선해나가는 과정이다.

여기서 다이나믹 프록시도 사용되고 리플렉션도 나오고 전략 패턴이나 템플릿 콜백도 나온다.

아무튼 지금까지 배운 것의 총집합이었다.

 

이 포스팅에서는 part1의 큰 도약에 대해서만 요약한다.

6장 이해에 어려움을 겪으시는 분들께 도움이 되기를 🙏

 

목표 

서비스의 트랜잭션 코드 분리.

확장 가능한 설계를 고려하여 서비스나 부가기능이 늘어나더라도 코드 변경을 최소화할 것

 

 

코드를 분리했을 때 장점

1. 관심사 분리를 통한 관심사 집중과 트랜잭션 코드 재사용

2. 테스트 스텁으로 쉽게 교체 가능하므로 고립된 단위 테스트 가능. 더 빠르고 가벼운 테스트, 오류 원인도 찾기 쉽다.

 

 

Step1 : 서비스에 데코레이터 적용

서비스의 트랜잭션 코드를 분리하기 위해 데코레이터 패턴을 적용하여 프록시를 만듦. 이때 프록시는 클라이언트와 타깃 사이에서 요청을 중개하며 기능 위임과 부가기능을 적용할 메소드를 선택하고 부가기능을 적용하는 역할을 한다. 프록시를 코드로 직접 생성하면 기능위임 코드를 일일이 작성해야하고, 부가기능 코드가 메소드와 타깃마다 반복된다.

 

 

Step2 : 다이나믹 프록시로 변경

프록시 클래스를 코드로 작성해서 발생한 문제이므로 런타임 시점에 동적으로 프록시 클래스를 만드는 방법을 적용한다. 다이나믹 프록시는 리플렉션으로 생성한다. 리플렉션을 사용하면 Method 인터페이스 invoke로 기능위임 코드를 추상화할 수 있다. 어떤 부가기능을 어떤 메소드에 적용할지는 개발자가 정보를 알려줘야하므로 handler에 이 책임을 넘겼다. 이때 클라이언트, 다이나믹 프록시, handler, 타겟 관계에서 책임이 할당된 양상을 보면 다이나믹 프록시는 클라이언트의 요청을 받아주고 handler에게 클라이언트의 요청 정보를 넘기는 요청 중개의 책임이 있다. Handler는 기능 위임, 부가기능 적용 메소드 선택, 부가기능 적용의 책임을 맡고 있다. 이때 handler는 책임을 수행하기 위한 상태 정보인 타깃 오브젝트에 의존한다.

 

 

Step3 : 팩토리 빈 도입

다이나믹 프록시를 스프링 컨텍스트로 DI하려면 기본 생성자로 만들고 세터로 주입하는 방식은 사용할 수 없다. 다이나믹 프록시는 기본 생성자를 사용할 수 없고, Proxy 클래스의 static 메소드를 사용하기 때문이다. 그래서 기본 생성자가 있으면서 다이나믹 프록시를 생성하는 역할을 가진 팩토리 빈을 스프링 빈으로 등록한다. 다이나믹 프록시 객체를 생성하는 시점에 필요한 의존성(target, handler의 의존성)을 명시적으로 주입하는 방법을 사용한다.

 

 

Step4 : 다이나믹 프록시 생성을 추상화한 스프링 다이나믹 프록시 팩토리 빈

기존 JDK 다이나믹 프록시를 사용하는 방법은 Handler가 너무 많은 책임을 가진다. 타깃이나 부가기능이 추가될 때마다 새로운 다이나믹 프록시를 필요로 하기 때문에 설정정보에 팩토리빈 중복, Handler 클래스도 중복되는 문제가 발생한다. 그래서 스프링은 다이나믹 프록시를 생성하는 방법을 추상화해서 부가기능을 여러 타깃에 재사용할 수 있도록 한다. 부가기능 자체를 담은 어드바이스와 부가기능을 적용할 메소드를 선택하는 포인트컷을 순수한 코드로 분리해 빈으로 등록하고, 이 둘의 조합을 어드바이저로 묶어 빈으로 등록한다. 프록시 팩토리 빈을 등록할 때는 타깃과 어드바이저를 선택하면 된다. 어드바이저는 타깃에 의존하지 않으므로 프록시에 의해 공유될 수 있다.