스프링MVC - 스프링 AOP [1]

600g (Kim Dong Geun)·2020년 8월 21일
0

본 글은 스프링 MVC에 대해 지식을 정리하고 나중에 헷갈릴 때 다시 보기 위한 글입니다 👀

본 게시글은 Spring MVC Quick Start를 참조하여 정리한 글입니다. 📖 👀

본 게시글은 Spring MVC Documentation를 참조하여 정리한 글입니다 📚 👀

사실 스프링 프레임워크 하면 빠질 수 없는 두 기능이 있다.
첫 번째는 앞에서 언급했던 Ioc와 DI
두 번째는 바로 AOP이다.

AOP란? (Aspect oriented Programming)

앞서, IoC가 스프링 프레임워크에 객체들의 생성과 라이프 사이클들의 관리를 넘겨 객체들간의 결합도를 낮췄다면, 이번 AOP는 응집도와 관련된 기능이라고 볼 수 있다.

스프링 AOP에서 가장 중요한 단어가 있다

그것은 바로 관심 분리 다. 😁

사실 책에서 이렇게 설명하고 썩 와닿는 설명은 아니었다. 그런데 지금은 이 말이 아니면 설명하기 힘들다.

메소드마다 공통으로 등장하는 로깅이나 예외, 트랜잭션 처리같은 코드 들을 이제 횡단관심이라 하고, 사용자의 요청에 따라 실제로 수행되는 부분을 핵심관심이라 한다.

AOP는 이 두 관심을 분리하여 응집도를 높여 전체적으로 유지보수하기 좋은 코드를 하게 만든다.

음 예를들자면 바로 이런 거다.

TV의 동작을 메소드로 만들어보자.
먼저 전원부 클래스에서 TV의 전원을 공급하는 메소드를 실행하고,
주파수 클래스로부터 TV 프로그램의 정보를 받아야하는 메소드를 실행하고,
디스플레이 클래스로부터 TV 프로그램 영상을 출력하는 메소드를 실행하고,
스피커 클래스로부터 소리를 나오게하는 메소드를 실행해야한다.

그럼 TV를 켜는 일련의 작업에 로그를 남긴다고 적어보자.
서로 다른 클래스에 각각의 로그함수를 집어넣을 것이다.

스프링 AOP는 이런 문제를 해결해서 하나의 클래스에서 일련의 로깅과정들을 해결할 수 있게 해준다.
이렇게 함으로써 얻는 이득이 뭐냐고? 개발자는 비즈니스로직에만 신경을 쓸 수 있게 되고, 좀 더 바꿔말하면 응집도 높은 코드를 얻을 수 있다는 것이 되는 것

아무튼 이제 AOP 에 대한 용어부터 알아보자

AOP 용어 정리

Join Point(조인 포인트)

클라이언트가 호출되는 모든 비즈니스 메소드를 JoinPoint라고 생각하면 된다.

조인 포인트는 후에 나올 포인트 컷의 후보가 된다.

Point Cut(포인트 컷)

클라이언트가 호출되는 모든 비즈니스 메소드가 조인포인트라면, 포인트 컷은 개발자에 의해 필터링 된 조인 포인트이다. 로그를 남기고 싶은 메소드라던지, 트랜잭션을 걸 메소드란지 선택을 받으면 포인트컷이 된다.

즉, 비즈니스 메소드 중 AOP를 적용시킬 메소드를 선택하는 것

<aop:config>
  <aop:pointcut id="pointCutName" expression="execution (* com.~~.*(..))"/>
</aop:config>

식으로 선언할 수 있다.

추후에 말할 것지만, expression 내에서 어떤 클래스의 어떤 메소드, 변수를 선택할 건지 정의한다.

expression내 코드가 execution| (* |com.x.y.|*Impl.|*(..))와 같다면

기능선언리턴타입클래스 위치클래스 명메소드명 혹은 매개변수
코드execution(*com.x.y.*Impl.*(..))

라고 볼 수 있겠다.

Advice (어드바이스)

어드바이스는 횡단관심에 해당하는 공통기능의 코드를 의미하며, 독립된 클래스의 메소드로 동작하는 것.
즉 로그를 남길 것인지 트랜잭션을 남길 것인지, 비즈니스 메소드를 실행전에 실행할 것인지 후에 실행할 것인지 아니면 전후로 실행할 것인지 등을 설정할 수 있다.

스프링에서 제공하는 동작 시점은
before,after,after-returning,after-throwing,around등이 있다.

Weaving (위빙)

위빙은 포인트 컷으로 지정한 핵심관심 메소드가 호출될 때, 어드바이스에 해당하는 횡단 관심 메소드가 삽입되는 과정을 말한다.

즉, 비즈니스메소드가 호출될 때, 로깅 메소드를 삽입하는 그 과정

위빙을 처리하는 방식은 크게 컴파일 타임 위빙, 로딩 타임 위빙, 런타임 위빙 방식이 존재하는데,
스프링에서는 런타임 위빙 방식만을 지원한다.

Aspect Or Advisor(애스펙트 또는 어드바이저)

AOP라는 이름에서 알 수 있듯이 AOP의 핵심은 바로 Aspect이다.
Aspect는 포인트 컷과 어드바이스의 결합으로써, 어떤 포인트컷 메소드에 대해서 어떤 어드바이스 메소드를 실행할 지 결정한다.

Aspect = PointCut + Advice

AOP의 전체적인 과정


구글에 잘 표현한 그림들이 없어서,, 짜장국 성님들의 자료를 들고왔다.
(공식문서에서도 안보이더라,,)

암튼 여러 Join Point들이 실행되고, 이때 특정 포인트 컷으로 필터링 된 포인트 컷이 호출되는 순간 어드바이스 객체의 어드바이스 메소드가 실행된다. 어드바이스 메소드 시점은 개발자가 설정된대로 호출이 되며, 포인트컷으로 지정한 메소드가 호출될 때 어드바이스 메소드를 삽입하도록 하는 설정을 애스펙트라고 한다. 이 에스팩트 설정에 따라 위빙이 처리가 되는 것이다.

AOP의 엘리먼트 종류

<aop:config>

AOP 설정에서 <aop:config>루트 엘리먼트이다.
스프링 설정 파일 내에서는 <aop:config>가 여러번 사용될 수 있다.
반드시 <aop:config> 하위에 pointCut이나 aspect등의 element들이 와야 한다.

  • applicationContext.xml
<beans>
  <context:component-scan base-package="com.x.y"/>
  <aop:config>
    <aop:pointcut id="" expression=""/>
    <aop:aspect />
  </aop:config>
</beans>

요런식으로다가 무슨 말인지 알간?

<aop:pointcut>

<aop:pointcut> 엘리먼트는 포인트 컷을 지정하기 위해 사용하며 <aop:config>의 자식이나 <aop:aspect>의 자식 엘리먼트로 사용할 수 있다.

이때, 당연히 <aop:aspect>의 자식 엘리먼트로 지정된 포인트 컷은 그 상위 element에서만 한정되겠쥬?

<aop:pointcut id="pointCutName" expression="execution(*~~.*(..))"/>

으로 사용할 수 있다.

<aop:aspect>

aspect는 <aop:aspect> 엘리먼트로 설정하며, 핵심관심에 해당하는 포인트 컷 메소드에 횡단 관심에 해당하는 어드바이스 메소드를 결합하기 위해 사용된다.

즉, Aspect를 어떻게 설정하느냐에 따라 Weaving 결과가 달라짐.

<aop:aspect ref="bean name">
  <aop:before pointcut-ref="pointCutName" method="memthodName"/>
</aop:aspect>

before 말고도 after,after-throwing,after-returning,around 등이 있다.
뒤에가서 더 서술할 예정!

<aop:advisor>

<aop:advisor>는 PointCut과 Advice를 결합한다는 점에서 Aspect와 같은 기능을 한다.

그러나 트랜잭션 설정 같은 몇몇 특수한 경우는 Aspect가 아닌 Advisor를 사용해야 한다.

<tx:advice id="txAdvice" transaction-manager="txManager">
</tx:advice>

<aop:config>
  <aop:pointcut id="allPointCut" expression="execution(* com.*.*(..)"))/>
  <aop:advisor pointcut-ref="allPointCut" advice-ref="txAdvice"/>
</aop:config>

처럼 말이다.

포인트 컷 표현식

앞에서 잠깐 설명했지만

기능선언리턴타입클래스 위치클래스 명메소드명 혹은 매개변수
코드execution(*com.x.y.*Impl.*(..))

로 사용할 필터를 선택할 수 있다고 했다.

1. 리턴 타입 지정

표현식설명
*모든 리턴타입 허용
void리턴 타입이 void 인 메소드 선택
!void리턴 타입이 void가 아닌 메소드 선택
이하 생략ㅇㅇㅇ 이하 생략

2. 패키지 지정

패지키 경로를 지정할 떄는 *.. 을 주로 사용한다.

표현식설명
com.x.y.z정확하게 com.x.y.z 패키지만 선택
com.x.y.z..com.x.u.z로 시작하는 모든 패키지 선택
com.x.y.z..implcom.x.y.z 패키지로 시작하면서 마지막 패키지 이름이 impl로 끝나는 패키지 선택

3. 클래스 지정

클래스 이름을 지정할 때는 *,+ 를 주로 이용한다.

표현식설명
BoardServiceImpl정확하게 BoardServiceImpl클래스만 선택
*Impl클래스 이름이 Impl로 끝나는 클래스만 선택
BoardService+클래스 이름 뒤에 +가 붙으면 해당 클래스로부터 파생된 모든 자식 클래스 선택 (인터페이스도 마찬가지)

4. 메소드 지정

메소드를 지정할 때는 주로 *캐릭터를 사용하고 매개변수를 지정할 때는 ..을 사용한다.

표현식설명
*(..)가장 기본 설정으로 모든 메소드 선택
get*(..)메소드 이름이 get으로 시작하는 모든 메소드 선택

5. 매개변수 지정

매개변수를 지정할 때는 ..,*를 사용하거나 정확한 타입을 지정한다.

표현식설명
(..)가장 기본적인 설정으로써 매개변수의 개수와 타입이 제약없음을 의미
(*)반드시 1개의 매개변수를 가지는 메소드만 선택
(com.x.y.z.UserDao)매개변수로 UserDao를 가지는 메소드만 선택, 이떄 클래스 패키지 경로가 반드시포함
!(com.x.y.z.UserDao)아닌것만!
(Integer,..)한 개 이상의 매개변수를 가지되, 첫 번쨰 매개변수 타입이 무조건 Integer인 메소드만 선택
(Integer,*)반드시 두 개의 매개변수를 가지되, 첫번째 매개변수의 타입이 Integer인 메소드만 선택

정리할 내용이 많아서 AOP는 두장으로 나눠야 겠다 하하..

내일써야지 힘들다.. 헥헥 😖😖

profile
수동적인 과신과 행운이 아닌, 능동적인 노력과 치열함

0개의 댓글