개발일기/CS(면접)

Spring AOP 란?

w.llama 2024. 8. 12. 21:02

Spring AOP란?

Spring AOP는 관점 지향 프로그래밍을 구현한 것으로, 애플리케이션의 여러 부분에 걸쳐 나타나는 공통 관심사를 모듈화하여 코드의 중복을 줄이고 유지보수성을 향상시키는 프로그래밍 패러다임이다.

주요 용도:

  • 로깅
  • 보안
  • 트랜잭션 관리
  • 성능 모니터링

AOP 핵심 개념

 Aspect

  • 여러 객체에 공통으로 적용되는 기능을 모듈화한 것
  • 예: 로깅 Aspect, 보안 Aspect

Join Point

  • Aspect를 적용할 수 있는 지점
  • Spring AOP에서는 메서드 실행 지점만 Join Point로 사용

 Pointcut

  • Join Point의 부분 집합으로, 실제로 Aspect를 적용할 지점을 선별
  • 예: "get"으로 시작하는 모든 메서드에 적용

 Advice

  • Aspect가 실제로 수행하는 작업과 그 시점을 정의
  • 주요 타입:
    1. Before: 메서드 실행 전
    2. After: 메서드 실행 후 (결과에 상관없이)
    3. After-returning: 메서드가 정상적으로 결과를 반환한 후
    4. After-throwing: 메서드가 예외를 던진 후
    5. Around: 메서드 실행 전과 후

Weaving

  • Aspect를 대상 객체에 적용하여 새로운 프록시 객체를 생성하는 과정

Spring AOP의 특징

  1. 프록시 기반 AOP
    • 대상 객체를 감싸는 프록시 객체를 생성하여 AOP를 구현
  2. 메서드 조인 포인트만 지원
    • 메서드 실행 시점에만 Aspect 적용 가능
    • 필드 접근, 객체 생성 등의 조인 포인트는 지원하지 않음
  3. 런타임 Weaving
    • 애플리케이션 실행 중에 프록시 객체 생성 및 Aspect 적용
  4. Spring 컨테이너와의 통합
    • Spring의 IoC 컨테이너와 완벽하게 통합되어 사용 용이

Spring AOP 사용 예시

@Aspect
@Component
public class ValidBindingResultAop {

    @Pointcut("execution(* com.sparta.ottoon.auth.controller.UserController.*(..))")
    private void auth() {}

    @Before("auth() && args(requestDto, bindingResult)")
    public void handleBindingResult(SignupRequestDto requestDto, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            List<FieldError> fieldErrors = bindingResult.getFieldErrors();
            for (FieldError fieldError : fieldErrors) {
                // 로깅 또는 원하는 처리 수행
                System.out.println("BindingResult Error: " + fieldError.getField() + " - " + fieldError.getDefaultMessage());
            }

            throw new CustomException(ErrorCode.FAIL);
        }
    }
}

Spring AOP의 장단점

장점

  • 코드 중복 감소
  • 비즈니스 로직과 공통 관심사의 분리
  • 유지보수성 향상
  • 모듈화를 통한 재사용성 증가

단점

  • 런타임 시 약간의 성능 오버헤드
  • 복잡한 AOP 설정은 코드 이해도를 낮출 수 있음
  • 메서드 조인 포인트로 제한된 기능

Spring AOP 관련 주요 어노테이션

@Aspect

  • 해당 클래스가 Aspect임을 나타냄
  • Aspect 클래스에 사용됨
@Aspect
@Component
public class LoggingAspect {
    // Aspect 내용
}

@Pointcut

  • Pointcut을 정의
  • 메서드 시그니처와 함께 사용되며, 실제 메서드 본문은 비워둔다.
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceLayer() {}

@Before

  • 대상 메서드 실행 전에 실행될 Advice를 정의
@Before("serviceLayer()")
public void logBefore(JoinPoint joinPoint) {
    // 메서드 실행 전 로직
}
@After
  • 대상 메서드 실행 후에 실행될 Advice를 정의
  • 메서드의 정상 종료나 예외 발생 여부와 관계없이 실행
@After("serviceLayer()")
public void logAfter(JoinPoint joinPoint) {
    // 메서드 실행 후 로직
}
@AfterReturning
  • 대상 메서드가 정상적으로 결과를 반환한 후에 실행될 Advice를 정의
@AfterReturning(pointcut = "serviceLayer()", returning = "result")
public void logAfterReturning(JoinPoint joinPoint, Object result) {
    // 메서드가 정상적으로 결과를 반환한 후 로직
}
@AfterThrowing
  • 대상 메서드에서 예외가 발생했을 때 실행될 Advice를 정의
@AfterThrowing(pointcut = "serviceLayer()", throwing = "ex")
public void logAfterThrowing(JoinPoint joinPoint, Exception ex) {
    // 메서드에서 예외 발생 시 로직
}
@Around
  • 대상 메서드 실행 전후에 실행될 Advice를 정의
  • 가장 강력한 Advice로, 메서드 실행을 완전히 제어할 수 있음
@Around("serviceLayer()")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
    // 메서드 실행 전 로직
    Object result = joinPoint.proceed(); // 실제 메서드 실행
    // 메서드 실행 후 로직
    return result;
}
@EnableAspectJAutoProxy
  • Spring AOP 지원을 활성화
  • 주로 설정 클래스에서 사용
@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
    // AOP 설정
}
어노테이션 사용 시 주의사항
  1. 성능 고려: 과도한 AOP 사용은 애플리케이션 성능에 영향을 줄 수 있다.
  2. 가독성: 너무 많은 Aspect를 사용하면 코드 흐름을 파악하기 어려울 수 있다.
  3. 순서 관리: 여러 Advice가 동일한 Join Point에 적용될 때는 실행 순서를 관리해야 한다.
  4. 프록시 제한사항: 자기 호출(self-invocation)에는 AOP가 적용되지 않는다.