공부하게 된 계기
최근 업무를 하면서 선임 개발자분께서 AOP를 적용한 코드를 보고, 테스트를 해야 할 일이 있었다. 작년에 강의를 들으면서 Spring AOP에 대해 공부한 적이 있어 어느 정도 코드 이해했지만,
- 선임과 대화하는 과정에서 헷갈리는 용어가 많아 원할한 커뮤니케이션이 힘들었고,
- 다음에 AOP를 적용할 부분이 있을 때, 막상 적용하기는 많이 헤맬 것 같다는 생각이 들었다.
그래서 오늘은 김영한님의 스프링 핵심원리 고급 편 PDF를 다시 복기하면서 개념을 복습하고, Annotation를 대상으로 AOP를 적용하는 예시를 통해서 실무적으로도 학습하려고 한다.
AOP 탄생 배경
핵심 기능과 부가 기능
- 핵심 기능
- 해당 객체가 제공하는 고유의 기능
- 부가 기능
- 핵심 기능을 보조하기 위해 제공되는 기능 (로그 추적, 트랜잭션 기능 등등)
- 여러 클래스에 걸쳐서 함께 사용되는 경우가 많다. (횡단 관심사)
- 위 사례 경우, 많은 반복, 여러 곳에 퍼져있는 중복 코드, 변경시의 많은 중복 코드 수정의 문제점이 있다.
AOP 탄생
핵심 기능과 부가 기능 분리
- 애스펙트(Aspect) : 부가 기능과 부가 기능을 어디에 적용할지 선택하는 기능을 하나의 모듈로 만든 것
- @Aspect : 스프링이 제공하는 어드바이저(부가 기능)과 포인트컷(적용 대상)을 가지고 있다.
- 애스펙트를 사용해서 프로그래밍 방식을 관점 지향 프로그래밍 AOP라고 한다.
AOP 적용 방식
- 컴파일 시점
- .java 소스 코드를 컴파일러를 사용해서. class를 만드는 시점에 부가 기능 로직 추가
- AspectJ가 제공하는 특별한 컴파일러를 사용해야 한다.
- 단점 : 특별한 컴파일러가 필요하며 복잡하다.
- 클래스 로딩 시점
- 자바를 실행하면 자바 언어는 .class 파일을 JVM 내부의 클래스 로더에 보관하는데, 이때 중간에. class 파일을 조작한 다음에 JVM에 올릴 수 있다. (로드 타임 위빙)
- 단점 : 로드 타임 위빙은 자바를 실행할 때 특별한 옵션(java -javaagent)을 통해 클래스 로더 조작기를 지정해야 하기 때문에 번거롭고 운영하기 어렵다.
- 런타임 시점 (프록시)
- 런타임 시점은 컴파일 이후, 클래스 로더에 클래스도 다 올라가서 이미 자바가 실행된 다음을 말한다.
- 프록시를 통해 스프링 빈에 부가 기능을 적용할 수 있다.
- 프록시를 사용하기에 AOP 기능에 일부 제약이 있다. 하지만 컴파일 시점, 클래스 로딩 시점의 단점을 모두 극복하고, 스프링만 있으면 얼마든지 AOP를 적용할 수 있다.
용어 정리
- 조인 포인트 (Join Point)
- 어드바이스가 적용될 수 있는 위치 (AOP가 적용할 수 있는 모든 지점)
- 스프링 AOP는 프록시 방식을 사용하므로 조인 포인트는 항상 메서드 실행 지점으로 제한된다.
- 포인트컷 (Pointcut)
- 조인 포인트 중에서 어드바이스가 적용될 위치를 선별하는 기능
- 프록시를 사용하는 스프링 AOP는 메서드 실행 지점만 포인트컷으로 선별 가능하다.
- 타켓 (Target)
- 어드바이스를 받는 객체
- 포인트컷을 통해 지정된다.
- 어드바이스 (Advice)
- 부가 기능
- 특정 조인 포인트에서 Aspect에 의해 취해지는 조치
- Around, Before, After와 같은 다양한 종류의 어드바이스가 있다.
- 애스팩트 (Aspect)
- 어드바이스 + 포인트컷을 모듈화 한 것
- 어드바이저 (Advisor)
- 하나의 어드바이스와 하나의 포인트 컷으로 구성
- 스프링 AOP에서만 사용되는 특별한 언어
- 위빙 (Weaving)
- 포인트 컷으로 결정한 타깃으로 조인 포인트에 어드바이스를 적용하는 것
- 위빙을 통해 핵심 기능 코드에 영향을 주지 않고 부가 기능을 추가할 수 있다.
- AOP 프록시
- AOP 기능을 구현하기 위해 만든 프록시 객체
- 스프링에서 프록시는 JDK 동적 프록시 또는 CGLIB 프록시이다.
커스텀 Annotation 대상으로 AOP 적용하기
1. Custom Annotation 만들기
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface DoSomething {
}
2. Aspect 정의하기
public class PointCuts {
@Pointcut("@annotation(com.min.advanced.aop.DoSomething)")
public void doSomethingPointcut() {}
}
@Aspect
@Slf4j
@Component
public class ExecuteSomethingAspect {
@Around("com.min.advanced.aop.PointCuts.doSomethingPointcut()")
public void doSomething(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("[doSomethingAop] do something start");
joinPoint.proceed();
log.info("[doSomethingAop] do something end");
}
}
@DoSomething 을 원하는 메서드에 붙여주면, AOP 적용 결과를 확인 할 수 있다.
'개발 > Spring' 카테고리의 다른 글
[Spring] AOP 기반 Redis 분산락 적용 (0) | 2024.05.11 |
---|---|
[Spring] JPA 낙관적 락 & 비관적 락 (0) | 2024.05.08 |
[MSA] 서킷브레이커 적용 (Resilience4j) (4) | 2024.01.23 |
[Spring] MyBatis 사용법 및 동적 쿼리 정리 (1) | 2024.01.09 |
[Spring] @Cacheable를 통해 성능 개선하기 (캐시) (2) | 2023.09.04 |