1. AOP(Aspect Oriented Programming)란?
관점 지향 프로그래밍이라고 부른다. IOC가 낮은 결합도와 관련된 것이라면 AOP는 높은 응집도와 관련이 있다.
서비스들의 비즈니스 메소드들은 복잡한 코드로 구성되어 있는데 그 중 핵심 로직은 얼마 안되고 대부분 트랜잭션, 로깅 처리, 인증과 관련된 코드들이 있다. 이럴 때 비핵심이지만 꼭 필요하고 공통화할 수 있는 부분을 따로 빼서 관리(횡단 분리)하는 것이다.
2. AOP 용어
- 조인포인트(Joinpoint) : 클라이언트가 호출하는 모든 비즈니스 메소드, 조인포인트 중에서 포인트 컷 되기 때문에 포인트컷의 후보로 생각할 수 있다. 적용 시점.
- 포인트컷(Pointcut) : 특정 조건에 의해 필터링된 조인포인트, 수많은 조인포인트 중에 특정 메소드에서만 횡단 공통기능을 수행시키기 위해서 사용한다. 적용해야할 위치. 표현식: 리턴타입 패키지경로 클래스명 메소드명(매개변수)
- 어드바이스(Advice) : 횡단 관심에 해당하는 공통 기능의 코드, 독립된 클래스의 메소드로 작성한다. Aspect 안의 기능.
- 어드바이스의 동작 시점
동작시점 | 설명 |
Before | 메소드 실행 전에 동작 |
After | 메소드 실행 후에 동작 |
After-returning | 메소드가 정상적으로 실행된 후에 동작 |
After-throwing | 예외가 발생한 후에 동작 |
Around | 메소드 호출 이전, 이후, 예외발생 등 모든 시점에서 동작 |
- 위빙(Weaving): 포인트컷으로 지정한 핵심 관심 메소드가 호출될 때, 어드바이스에 해당하는 횡단 관심 메소드가 삽입되는 과정을 의미한다. 이를 통해 비즈니스 메소드를 수정하지 않고도 횡단 관심에 해당하는 기능을 추가하거나 변경이 가능해진다.
- 애스팩트(Aspect): 포인트컷과 어드바이스의 결합이다. 어떤 포인트컷 메소드에 대해 어떤 어드바이스 메소드를 실행할지 결정한다. 묶어 놓은 모듈.
3. AOP 적용 방식
1) 컴파일: 컴파일 시 이미 코드 내용대로 조작이 된 바이트코드를 만들어냄
2) 로딩 타임: 컴파일에서는 적용되지 않고 각 Aspect를 따로 바이트 코드로 가지고 있지만 로드 시 설정한 대로 로드 후 생성
3) 런타임: 스프링 안에서 사용
A라는 클래스 타입의 Bean을 만들 때 A*인 "프록시 빈"을 생성 후 Aspect를 호출
4. 포인트컷 표현식
포인트컷을 이용하면 어드바이스 메소드가 적용될 비즈니스 메소드를 정확하게 필터링할 수 있다.
4-1. 지시자(PCD, Aspect Joinpointcut Designators)의 종류
- excution: 가장 정교한 포인트컷을 만들 수 있다. 리턴타입 패키지경로 클래스명 메소드명(매개변수)
- within: 타입패턴 내에 해당하는 모든 것들을 포인트컷
- bean: bean 이름으로 포인트컷
4-2. 리턴타입 지정
표현식 | 설명 |
* | 모든 리턴타입 허용 |
void | 리턴타입이 void인 메소드 선택 |
!void | 리턴타입이 void가 아닌 메소드 선택 |
4-3. 패키지 지정
표현식 | 설명 |
com.dev.domain | 정확하게 com.dev.domain 패키지만 선택 |
com.dev.domain.. | com.dev.domain 패키지로 시작하는 모든 패키지 선택 |
4-4. 클래스 지정
표현식 | 설명 |
UserBO | 정확하게 UserBO 클래스만 선택 |
*BO | 이름이 BO로 끝나는 클래스만 선택 |
BaseObject+ | 클래스 이름 뒤에 '+'가 붙으면 해당 클래스로부터 파생된 모든 자식 클래스 선택, 인터페이스 이름 뒤에 '+'가 붙으면 해당 인터페이스를 구현한 모든 클래스 선택 |
4-5. 메소드 지정
표현식 | 설명 |
*(..) | 모든 메소드 선택 |
update*(..) | 메소드명이 update로 시작하는 모든 메소드 선택 |
4-6. 매개변수 지정
표현식 | 설명 |
(..) | 모든 매개변수 |
(*) | 반드시 1개의 매개변수를 가지는 메소드만 선택 |
(com.dev.domain.user.model.User) | 매개변수로 User를 가지는 메소드만 선택. 꼭 풀패키지명이 있어야함 |
(!com.dev.domain.user.model.User) | 매개변수로 User를 가지지않는 메소드만 선택 |
(Integer, ..) | 한 개 이상의 매개변수를 가지되, 첫번째 매개변수의 타입이 Integer인 메소드만 선택 |
(Integer, *) | 반드시 두 개의 매개변수를 가지되, 첫번째 매개변수 타입이 Integer인 메소드만 선택 |
5. JoinPoint 인터페이스
어드바이스 메소드를 의미있게 구현하려면 클라이언트가 호출한 비즈니스 메소드의 정보가 필요하다. 예를들면 예외가 터졌는데, 예외 발생한 메소드의 이름이 뭔지 등을 기록할 필요가 있을 수 있다. 이럴 때 JoinPoint 인터페이스가 제공하는 유용한 API 들이 있다.
메소드 | 설명 |
Signature getSignature() |
클라이언트가 호출한 메소드의 시그니처(리턴타입, 이름, 매개변수) 정보가 저장된 Signature 객체 리턴 |
Object getTarget() | 클라이언트가 호출한 비즈니스 메소드를 포함하는 비즈니스 객체 리턴 |
Object[] getArgs() | 클라이언트가 메소드를 호출할 때 넘겨준 인자 목록을 Object 배열로 리턴 |
Signature API
메소드 | 설명 |
String getName() | 클라이언트가 호출한 메소드 이름 리턴 |
String toLongString() | 클라이언트가 호출한 메소드의 리턴타입, 이름, 매개변수(시그니처)를 패키지 경로까지 포함하여 리턴 |
String toShortString() | 클라이언트가 호출한 메소드 시그니처를 축약한 문자열로 리턴 |
사용법
JoinPoint를 어드바이스 메소드 매개변수로 선언해야 한다. 이때 인자는 스프링 컨테이너가 넘겨준다. ex) 메소드명(JoinPoint pjp)
이때 Around 어드바이스만 다른 어드바이스와 약간 다른데, ProceedingJoinPoint 객체를 인자로 선언해야한다.(proceed() 등이 추가로 구현되어 있음) ProceedingJoinPoint는 JoinPoint를 상속받는다.
'개발 Note > SPRING' 카테고리의 다른 글
[SPRING] Spring Boot 단위 테스트 (1) - Controller (0) | 2022.04.26 |
---|---|
[SPRING] Spring boot - 외부 jar 추가 후 테스트하기 (0) | 2022.04.23 |
[SPRING] Spring Security (0) | 2022.03.26 |