반응형
스프링의 중요 개념 중 하나인 AOP (Aspect-Oriented Programming)에 대해서 알아보는 시간을 가져보려고 한다.
AOP란?
- AOP란 개발을 하면서 중복코드를 제거하고 모듈성을 향상하기 위해서 사용되는 프로그래밍 패러다임이다.
- spirng은 이러한 AOP를 지원하고 있다.
- AOP의 핵심은 “횡단 관심사”를 분리하는 것이다. 즉, 애플리케이션의 주요 로직과는 별도로 존재하는 공통된 기능들을 한 곳에 모아서 관리하는 것 이러한 공통 기능을 “Aspect”라고 하며, 이를 원하는 부분에 주입하여 적용한다!
AOP 장점
- 모듈성 향상
- : 핵심 비즈니스 로직과 공통 기능들을 분리하여 코드의 가독성과 유지보수성을 개선한다.
- 중복 코드 제거
- : 공통 기능이 여러 곳에서 필요한 경우, AOP를 사용하여 중복 코드를 제거하고 코드 베이스를 간결하게 유지할 수 있습니다.
- 관점 지향
- : 관심사를 명확하게 분리하여 개발자가 핵심 비즈니스 로직에만 집중
- 코드 재사용성
- : 여러 모듈에서 같은 공통 기능을 사용해야 할 때, AOP를 이용하여 해당 기능을 한 번 작성하고 재사용 가능
AOP 주요 용어
- Aspect(관점)
- 공통 관심사(Concern)를 모듈화 한 단위, 횡단 관심사(Cross-cutting Concerns)라고도 함
- 로깅, 트랜잭션, 보안 등 애플리케이션 내 여러 모듈에서 반복적으로 사용되는 기능들을 Aspect로 추상화
- Aspect는 Advice와 Pointcut의 결합체, 메서드 실행 시점과 관련된 로직(Adive)과 어떤 Joinpoint를 선택할지를 정의하는 표현식(Pointcut)이 함께 포함된다.
- Joinpoint
- Aspect가 적용될 수 있는 실행 시점
- 메서드 호출, 메서드 실행, 필드 접근 등 애플리케이션 내에서 특정시점에서 일어나는 지점을 의미한다
- 각 Advice를 적용할 수 있는 지점, 조인포인트들 중에서 선택한 지점에 advice 적용
- 각 Advice에 파라미터로 Joinpoint를 넣게 되면 접근할 수 있게 된다.
- Advie
- Aspect의 실제 로직을 정의하는 부분, Jointpoint에서 실행되는 행위
- 어떤 시점에 어떤 로직을 수행할지 결정
- 주요 Advice의 유형
- Before: 메서드 실행 전에 실행되는 Advice
- @Before("execution(* com.example.myapp.service.*.*(..))") public void beforeServiceMethods() { // 메서드 실행 전에 수행할 로직 }
- After: 메서드 실행 후에 실행되는 Advice
- @After("execution(* com.example.myapp.service.*.*(..))") public void afterServiceMethods() { // 메서드 실행 후에 수행할 로직 }
- AfterReturning: 메서드 정상 실행 후에 실행되는 Advice
- @AfterReturning(pointcut = "execution(* com.example.myapp.service.*.*(..))", returning = "result") public void afterReturningServiceMethods(Object result) { // 메서드가 정상적으로 반환된 후에 수행할 로직, result에 반환값이 들어옵니다. }
- AfterThrowing: 메서드에서 예외 발생 시 실행되는 Advice
- @AfterThrowing(pointcut = "execution(* com.example.myapp.service.*.*(..))", throwing = "ex") public void afterThrowingServiceMethods(Exception ex) { // 메서드실행 중 예외 발생 후에 수행할 로직, ex에 예외 객체가 들어옵니다. }
- Around: 메서드 실행 전후에 실행되며, 메서드 실행 여부를 제어할 수 있는 가장 강력한 Advice
- @Around("execution(* com.example.myapp.service.*.*(..))") public Object aroundServiceMethods(ProceedingJoinPoint joinPoint) throws Throwable { // 메서드 실행 전에 수행할 로직 Object result = joinPoint.proceed(); // 원래 메서드 실행 // 메서드 실행 후에 수행할 로직 return result; }
- Pointcut
- 어떤 Jointpoint를 선택할지 정의하는 표현식
- Aspect가 어떤 Joinpoint에서 Advice를 적용할지 결정하는 데 사용된다.
- Pointcut 표현식은 메서드 실행 시점, 패키지 내의 메서드, 어노테이션이 적용된 메서드 등 다양한 기준으로 작성할 수 있다.
- 여러 Advice에서 같은 Pointcut을 사용할 경우 하나의 메서드를 만들어 둔 뒤 Advice에서 해당 메서드를 value로 넣는 방법도 가능하다.
- @Slf4j @Aspect @Component public class SimpleAop { @Pointcut("execution(* com.posco.web.controller..*.*(..))") private void cut() { } @Before("cut()") // @Before("execution(* com.posco.web.controller..*.*(..))") public void beforeParamLog(JoinPoint joinPoint) { } @AfterReturning(value = "cut()", returning = "returnObj") // @AfterReturning(value = "execution(* com.posco.web.controller..*.*(..))", returning = "returnObj") public void afterReturnLog(JoinPoint joinPoint, Object returnObj) { } }
- weaving
- Aspect를 핵심 비즈니스 로직에 적용하는 과정
- Aspect가 적용되어 있는 코드를 핵심 비즈니스 로직과 결합하여 실제 실행 가능한 코드로 만드는 과정
- 컴파일 타임, 로드 타임, 런타임 등 다양한 시점에 Weaving이 수행되고, Spring AOP는 주로 프록시 기반의 런타임 Weaving을 사용한다.
사용 방법
(예시 - 컨트롤러의 파라미터 로깅 남기기)
package com.example.web.aop;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Slf4j
@Aspect
@Component
public class SimpleAop {
@Pointcut("execution(* com.example.web.controller..*.*(..))")
private void cut() {
}
@Before("cut()")
public void beforeParamLog(JoinPoint joinPoint) {
Method method = getMethod(joinPoint);
log.info(">>>>> method name = {} >>>>", method.getName());
Object[] args = joinPoint.getArgs();
if (args.length <= 0) log.info("no Parameters");
for (int i = 0; i < args.length; i++) {
log.info("({}) parameter type = {}", i, args[i].getClass().getSimpleName());
log.info("({}) parameter value = {}", i, args[i]);
}
log.info("==============================================");
}
@AfterReturning(value = "cut()", returning = "returnObj")
public void afterReturnLog(JoinPoint joinPoint, Object returnObj) {
Method method = getMethod(joinPoint);
log.info("<<<<<< method name = {} <<<<", method.getName());
log.info("return type = {}", returnObj.getClass().getSimpleName());
log.info("return value = {}", returnObj);
log.info("=============================================");
}
private Method getMethod(JoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
return signature.getMethod();
}
}
- Aspect 클래스 작성하기
- @Aspect 어노테이션을 사용하여 Aspect클래스 선언
- @Component도 선언하거나 Aspect를 Bean으로 등록해야 한다.
- Pointcut 정의
- @Pointcut 어노테이션을 사용하여 Pointcut 표현식 정의
- Pointcut은 어느 Joinpoint를 선택할지 지정하는 표현식
- Advice 정의
- @Before, @After, @Around 등 Advice 어노테이션을 사용하여 특정 JoinPoint에서 실행될 로직 정의 ⇒ 주요 Advice의 유형 참고
- Advice 메서드의 실행 시점을 지정하여 로직 적용
- @EnableAspectJAutoProxy 어노테이션 추가
- AOP가 동작하도록 하려면 @EnableAspectJAutoProxy 어노테이션을 설정 클래스에 추가 등록
- 만약 spring boot에서 사용 중이라면 의존성에 spring-boot-starter-aop를 추가하면 자동으로 해당 어노테이션도 포함된다.
@SpringBootApplication @EnableAspectJAutoProxy public class PoscoApplication extends SpringBootServletInitializer { public static void main(String[] args) { SpringApplication.run(PoscoApplication.class, args); } }
반응형
'Library & Framework > Spring' 카테고리의 다른 글
[spring boot] 스프링 스케줄러 사용해보기 (0) | 2023.10.19 |
---|---|
필터(filter), 인터셉터(interceptor), AOP(Aspect-Oriented Programming) 살펴보기 (0) | 2023.07.22 |
[Spring boot] war로 배포하기, SpringBootServletInitalizer (0) | 2023.07.22 |
[Spring boot] 설정 정보(application.yml / properties) 분리시키기 - spring profiles (0) | 2023.06.12 |
[Spring boot] 설정 정보 외부에서 관리하기 2 - submodule (0) | 2023.03.21 |