AOP
Spring의 핵심 개념 중 하나인 DI가 애플리케이션 모듈들 간의 결합도를 낮춘다면,
AOP(Aspect-Oriented Programming)는 애플리케이션 전체에 걸쳐 사용되는 기능을 재사용하도록 지원하는 것이다. Aspect-Oriented Programming이란 단어를 번역하면 관점(관심) 지향 프로그래밍 이 된다.
프로젝트 구조를 바라보는 관점을 바꿔보자는 의미이다.
각각의 Service의 핵심기능에서 바라보았을 때 User과 Order는 공통된 요소가 없다.
하지만 부가기능 관점에서 바라보면 이야기가 달라진다.
부가기능 관점에서 바라보면 각각의 Service의 getXX 메서드를 호출하는 전후에 before과 after라는
메서드가 공통되는 것을 확인할 수 있습니다.
기존에 OOP에서 바라보던 관점을 다르게 하여 부가기능적인 측면에서 보았을때 공통된 요소를 추출하자는 것입니다.
이때 가로(횡단) 영역의 공통된 부분을 잘라냈다고 하여, AOP를 크로스 컷팅(Cross-Cutting) 이라고 부르기도 합니다.
OOP : 비즈니스 로직의 모듈화
모듈화의 핵심 단위는 비즈니스 로직
AOP : 인프라 혹은 부가기능의 모듈화
대표적인 예 : 로깅, 트랜잭션, 보안 등
각각의 모듈들의 주 목적 외에 필요한 부가적인 기능들
1. AOP 관련 핵심 용어 3가지
1) 조인포인트(JoinPoint) : AOP를 적용시킬 수 있는 메소드 전체 - 예시) 목록, 상세, 삽입, 수정, 삭제
2) 포인트컷(PointCut) : 조인포인트 중에서 AOP를 동작시킬 메소드 - 예시) 삽입, 수정, 삭제
3) 어드바이스(Advice) : 포인트컷에 동작시킬 AOP 동작 자체 - 예시) 트랜잭션
2. 어드바이스 동작 시점
| 의미 | 포인트컷 반환 타입
-----------|---------------------------------------------------|------------------------------------
1) @Before | 포인트컷 동작 이전에 수행(인터셉터와 동일한 시점) | void
2) @After | 포인트컷 동작 이후에 수행 | void
3) @Around | 포인트컷 동작 이전/이후에 모두 수행 | Object (포인트컷의 실행 결과를 반환)
3. 어드바이스 동작 순서
@Before -> @Around -> @After
4. 표현식(Expression) 작성 방법
1) 형식
execution(반환타입 패키지.클래스.메소드(매개변수))
2) 의미
(1) 반환타입
① * : 모든 반환타입
② void : void 반환타입
③ !void : void를 제외한 반환타입
(2) 매개변수
① .. : 모든 매개변수
② * : 1개의 모든 매개변수
▶ BeforeAop
@Slf4j -> SLF4J (Simple Logging Facade for Java) 로깅을 위한 코드를 자동으로 생성합니다.(Lombok)
@Aspect -> 클래스가 Aspect로 사용됨을 나타냅니다. Aspect는 횡단 관심사(cross-cutting concerns)를 정의, 적용
beforeAdvice(JoinPoint joinPoint) ->
Before 어드바이스로, 설정한 Pointcut에 해당하는 메서드들이 실행되기 전에 동작합니다.
이 메소드는 다음과 같은 역할을 수행합니다:
HTTP 요청 정보를 추출하기 위해 HttpServletRequest를 사용합니다.
이를 통해 요청 방식 (GET, POST 등), 요청 주소 등을 얻을 수 있습니다.
① 요청 파라미터를 Map으로 변환하여 요청 파라미터 정보를 획득합니다.
② 요청 파라미터를 출력할 형식을 만들고 로깅합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
package com.gdu.app10.aop;
import java.util.Arrays;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import lombok.extern.slf4j.Slf4j;
@Component
@Slf4j
@Aspect
public class BeforeAop {
@Pointcut("execution(* com.gdu.app10.controller.*Controller.*(..))")
public void setPointCut() { } // 이름만 제공하는 메소드(이름은 마음대로 본문도 필요 없다.)
// 어드바이스 : 무슨 동작을 하는가?
@Before("setPointCut()")
public void beforeAdvice(JoinPoint joinPoint) {
/*
* Before 어드바이스
* 1. 반환타입 : void
* 2. 메서드명 : 마음대로
* 3. 매개변수 : JoinPoint
*/
/* ContactController의 모든 메서드가 동작하기 전에 요청 (방식/주소/파라미터) */
// 1. HttpServletRequest
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
HttpServletRequest request = servletRequestAttributes.getRequest();
// 2. 요청 파라미터 -> Map 변환
Map<String, String[]> map = request.getParameterMap();
// 3. 요청 파라미터 출력 형태 만들기
String params = "";
if(map.isEmpty()) {
params += "No Parameter";
} else {
for(Map.Entry<String, String[]> entry : map.entrySet()) {
params += entry.getKey() + ":" + Arrays.toString(entry.getValue()) + " ";
}
}
// 4. 로그 찍기 (치환 문자 {} 활용)
log.info("{} {}", request.getMethod(), request.getRequestURI()); // 요청 방식, 요청 주소
log.info("{}", params); // 요청 파라미터
}
}
|
cs |
▶ AfterAop
스프링 AOP (Aspect-Oriented Programming)를 사용하여 로깅을 수행하는 클래스인 AfterAop입니다.
AOP는 관점 지향 프로그래밍으로, 애플리케이션의 흩어진 관심사(로깅, 보안, 트랜잭션 관리 등)를
모듈화하고 중앙화된 방식으로 처리할 수 있도록 도와줍니다.
setPointCut() -> spect의 Pointcut을 정의하는 메소드입니다. Pointcut은
어떤 메서드가 언제 실행될 것인지를 지정합니다.
여기에서는 com.gdu.app10.controller 패키지 내의 모든 컨트롤러 클래스의 모든 메서드에 해당하는 Pointcut을 정의한다.
afterAdvice(JoinPoint joinPoint) -> @After 어드바이스로, 설정한 Pointcut에 해당하는 메서드들이 실행된 이후에 동작
로그를 출력하여 메소드 실행 후에 추가 정보를 기록합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
package com.gdu.app10.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.interceptor.TransactionInterceptor;
import lombok.extern.slf4j.Slf4j;
@Component
@Aspect
@Slf4j
public class AfterAop {
@Autowired
private TransactionInterceptor transactionInterceptor;
// 포인트컷 : 언제 동작하는가?
@Pointcut("execution(* com.gdu.app10.controller.*Controller.*(..))")
public void setPointCut() { }
// 어드바이스 : 무슨 동작을 하는가?
@After("setPointCut()")
public void afterAdvice(JoinPoint joinPoint) {
/*
* After 어드바이스
* 1. 반환타입 : void
* 2. 메서드명 : 마음대로
* 3. 매개변수 : JoinPoint
*/
// 로그 찍기
log.info("======================================================");
}
}
|
cs |
▶ AroundAop
aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) ->
@Around 어드바이스로, 설정한 포인트컷에 해당하는 메서드들이 실행되기 전과 후에 동작합니다.
① 로그를 출력하여 메서드 실행 전과 후에 추가 정보를 기록합니다.
② proceedingJoinPoint.proceed()를 호출하여 실제 메서드를 실행합니다.
이것은 메서드 실행을 계속하도록 하는데, 이 메소드 실행 지점에서 실행됩니다.
③ 실행 후에는 시간 정보를 로그에 추가로 기록합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
package com.gdu.app10.aop;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
@Component
@Aspect
@Slf4j
public class AroundAop {
// 포인트컷 : 언제 동작하는가?
@Pointcut("execution(* com.gdu.app10.controller.*Controller.*(..))")
public void setPointCut() { }
// 어드바이스 : 무슨 동작을 하는가?
@Around("setPointCut()")
public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
/*
* Around 어드바이스
* 1. 반환타입 : Object
* 2. 메서드명 : 마음대로
* 3. 매개변수 : ProceedingJoinPoint
*/
// 로그 찍기
log.info("======================================================"); // 포인트컷 실행 이전에 실행할 코드(@Before 이전에 동작)
Object obj = proceedingJoinPoint.proceed(); // 포인트컷이 실행되는 시점
log.info("{}", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date())); // 포인트컷 실행 이후에 실행(@After 이전에 동작)
return obj;
}
}
|
cs |
'코딩기록 저장소 🐕 > spring(JPA)🌱' 카테고리의 다른 글
트랜잭션 (0) | 2023.10.12 |
---|---|
SpringMyBatis (0) | 2023.10.12 |
Springjdbc (0) | 2023.10.11 |
SpringLogger (0) | 2023.10.10 |
jdbcJunitTest (0) | 2023.10.10 |