2024. 1. 16. 21:00ㆍ스프링
프로젝트 어제 끝나고 이제 다시 이론 공부하는 날,, 너무 빡세서 힘든 날이었다.. 제대로 쉬지도 못해서 ㅠ
그래도 공부한 거 정리 시작해볼까,,?
AOP(Aspect-Oriented Programming)?


위 사진처럼 흩어진 관심사를 Aspect로 모듈화하고 핵심적인 비즈니스 로직에서 분리하여 재사용하겠다는 것이 AOP의 취지다.
부가 기능은 대표적으로 로깅, 보안, 트랜잭션 관리 같은 것들이 있다.
사실 이미, AOP를 적용해본 적이 있다고한다. 대표적으로 @Transactional 어노테이션을 통해서 말인데! 트랜잭션은 결국 시작되고, 최종적으로 Commit되면서 종료가 된다. 만약 , @Transactional 가 없다면, 트랜잭션이 필요한 서비스 method 마다 아래와 같은 코드를 작성해줘야한다. Spring에서는 이를 모듈화 해야하는 부가기능으로 보고, @Transactional 어노테이션을 제공해주는것이다.
class SomeService {
fun doTransaction() {
val tx = em.getTransaction();
tx.begin();
...
// DB 관련 로직
...
tx.commit();
}
}
AOP를 적용하기 위한 주요 개념들에 어떤 것들이 있을까?
Aspect
횡단 관심사(부가기능)를 모듈화한 단위다. Aspect는 부가기능을 정의하는 Advice와 적용 위치를 결정하는 PointCut으로 구성된다.
PointCut
Aspect가 적용될 프로그램상 실제 위치로 생각 하면 된다.
JoinPoint
PointCut의 후보군 입니다. Aspect가 적용될 수 있는 위치들을 말한다. method가 호출되는 시점, 특정 class의 생성자가 호출되는 시점, exception이 발생하는 시점 등이 될 수 있다.
Advice
실질적으로 부가 기능 로직이 정의되어있는 객체라고 볼 수 있다.
Weaving
Aspect를 실제 코드에 적용하는 과정을 나타낸다.
결국에는 부가기능을 모듈화하고, 본인의 애플리케이션 코드 중에 적용할 부분을 찾아 적용하는것이라고 볼 수 있다.
위와 같은 개념들을 기반으로, Java기반 AOP를 적용할 수 있는 프레임워크 여러가지가 있다. 그중 대표적인 두개가 Spring AOP와 AspectJ 입니다!
Spring AOP와 AspectJ의 차이점은 Weaving의 차이에 따라 구분된다.
Weaving 방법은 크게 아래와 같이 세가지가 있는데,
Compile-time Weaving
AOP가 적용되어 있는 것을 확인하고, 컴파일 시점에 AOP가 적용된 클래스들을 만들어 준다. 즉 우리가 작성한 클래스가 변경된다.
Load-time Weaving
Java기반 프로그램은 실행되기 전에 JVM에서 클래스를 로딩하는 시점이 있다. 이때, 변경된 바이트 코드를 사용하게 함으로써, 원본 클래스는 변경하지 않고, AOP를 적용하는 방식이다.
Run-time Weaving
객체에 직접 접근하기보다, 중간에 프록시(대리)를 두고 프록시를 통해 객체에 접근하여 AOP가 적용된다.
Compile-time Weaving 와 Load-time Weaving 는 AspectJ, Run-time Weaving는 AOP로 적용된다.
이러한 차이가 있으므로, 두 프레임워크의 특성이 달라진다고 한다.
성능 - AspectJ가 SpringAOP에 비해 8~35배 정도 빠릅니다. SpringAOP는 프록시를 거쳐야하기 때문에, 오버헤드가 있다.
JoinPoint - AspectJ는 모든 부분에 JoinPoint의 적용이 가능하다. 하지만, Spring AOP의 경우 프록시 패턴을 이용하기 때문에, 항상 프록시에 해당하는 sub class를 만들어야한다. 이때, Aspect를 적용하려는 Class가 final class 일때에는, 적용이 되지 않다. 마찬가지로 Java의 final 혹은 static method에도 적용이 되지 않다. 그렇기 때문에 Spring AOP는 일반 method만을 JoinPoint로 사용할 수 있다. 또한, Spring에서 관리하는 Bean에서만 작동한다.
복잡성 - AspectJ는 별도의 컴파일러나 weaver가 필요하기 때문에, Spring AOP에 비해 매우 복잡하다. 반면, Spring AOP는 간단하게 사용이 가능하다.
AOP 특징
- 프록시 패턴 기반의 AOP 구현체, 프록시 객체를 쓰는 이유는 접근 제어 및 부가기능을 추가하기 위해서다.
- 스프링 빈에만 AOP를 적용 가능하다.
- 모든 AOP 기능을 제공하는 것이 아닌 스프링 IoC와 연동하여 엔터프라이즈 애플리케이션에서 가장 흔한 문제(중복코드, 프록시 클래스 작성의 번거로움, 객체들 간 관계 복잡도 증가 ...)에 대한 해결책을 지원하는 것이 목적이다.
AOP를 사용하기 위해서 implementation("org.springframework.boot:spring-boot-starter-aop") 를 설치해주고 main 파일에다가 @EnableAspectJAutoProxy이라는 어노테이션을 추가해준다.
@Aspect
@Component
class TestAop {
@Around("execution(* com.example.courseregistration.domain.course.service.CourseService.getCourseById(..))")
//* 전체 .. 하나이상의 뭔가가 있다.
fun thisIsAdvice(joinPoint: ProceedingJoinPoint){
println("AOP START!!!")
joinPoint.proceed()
println("AOP END!!!")
}
}
@Aspect 는 Aspect를 정의할 때 사용한다. 위 처럼 Class를 정의하시고, 해당 어노테이션을 붙여주면 된다.
@Around 는 Advice의 적용 시점 중 하나다. JoinPoint를 기준으로, Advice가 언제 동작할지를 정의한다.
- @Around : 메서드 실행 전후로 동작한다.
- @Before : 메서드가 호출되기 전에 Advice가 실행된다.
- @After : 메서드 결과와 관계없이, 메서드가 완료되면 Advice가 실행된다.
- @AfterReturning : 메서드가 정상적으로 반환 했을시에만 Advice가 실행된다.
- @AfterThrowing : 메서드가 예외를 발생시킬때 Advice가 실행된다.
ProceedingJoinPoint 는 AOP가 적용되는 메서드를 의미한다. joinPoint.proceed() 가 메소드를 실행하는 것이라고 볼 수 있다.
execution(* ...) 는 PointCut Expression으로 Advice가 적용될 대상에 대해 서술해주는 부분이다.
나중에 코틀린이라는 언어에 익숙해지면 이 사이트를 들어가서 참고하라고 한다.
Kotlin으로 Spring AOP 극복하기! | 카카오페이 기술 블로그
Kotlin의 문법적 기능을 사용해서 Spring AOP 아쉬운 점을 극복한 경험을 공유합니다.
tech.kakaopay.com
'스프링' 카테고리의 다른 글
TIL 20240118 (0) | 2024.01.18 |
---|---|
TIL 20240117 (0) | 2024.01.17 |
TIL 20240115- 뉴스피드 프로젝트 KPT 회고 (2) | 2024.01.15 |
TIL 20240110- MVC 패턴 (0) | 2024.01.10 |
TIL 20240109- JPA Auditing (0) | 2024.01.09 |