[Spring] Spring AOP

DaeHoon·2023년 6월 14일
0

AOP (Aspect Oriented Programming)

  • 횡단 관심사 (Cross-Cutting Concern)의 분리를 허용함으로써 모듈성을 증가시키는 것이 목적인 프로그래밍 패러다임
  • 여러 객체에 공통으로 적용할 수 있는 기능을 분리해서 개발자는 반복 작업을 줄이고 핵심 기능 개발에만 집중할 수 있다.

Example

  • 팩토리얼 연산을 재귀로 구현한 함수의 실행시간을 측정.
  • AOP를 이용하지 않으면 해당 함수에 실행시간을 측정하는 로직을 추가해야 됨
    • 중복 코드 발생
    • 기존 코드 수정 필요
  • 위와 같은 상황을 해결하기 위해 프록시를 사용

Proxy

  • 자신이 클라이언트가 사용하려고 하는 실제 대상인 것처럼 위장해서 클라이언트의 요청을 받아주는 것 (대리인, 대리자)
  • 사용 목적
    • 클라이언트가 타겟에 접근하는 방법을 제어 -> 프록시 패턴
    • 타겟에 부가적인 기능을 부여 -> 데코레이터 패턴

Example

  • 실행시간을 구현한 ExecutionTimeCalcuator라는 프록시 객체 구현
  • 생성자 인자로 실제로 시간 측정 계산을 진행할 delegate 인자를 받는다.

  • 이런 식으로 실제로 계산할 Calculator의 의존성을 프록시에 주입해준다.

다시 AOP

  • 핵심 기능인 계산하기와 부가 기능인 실행시간 측정, 로깅
  • 즉, 부가 기능에서 바라보는 공통된 부분을 추출하는 것이 AOP의 개념. 이 부가 기능을 횡단 관심사 (Cross-Cutting Concern)라고 부른다.

AOP 용어

  • Target Object: 부가 기능을 부여할 대상
  • Aspect: AOP의 기본 모듈. 부가될 기능을 정의한 Advice와 Advice를 어디에 적용할지 결정하는 Pointcut을 함께 가진다.
  • Advice: 타겟에게 제공할 부가 기능을 담은 모듈. 타겟이 필요없는 순수한 부가 기능. Aspect가 무엇을 언제 할지 정의
  • Pointcut: Advice에 적용할 JoinPoint를 선별하는 작업 또는 그 기능을 정의한 모듈
  • Joinpoint: 프로그램의 실행 내부에서 Advice(부가 기능)가 적용될 수 있는 위치

Spring AOP

용어

  • Target Object: Spring AOP는 런타임 프록시로 구현되므로 타겟은 항상 프록시이다.
  • Aspect: 트랜잭션 관리가 가장 좋은 예시. @Aspect를 사용하여 구현
  • Advice: Around, Before, AfterThrowing 등 다양한 Advice가 존재
  • joinPoint: Spring AOP는 프록시 방식을 사용하므로 joinPoint는 항상 메서드 실행 지점이다.
  • pointcut: Spring AOP의 joinPoint는 메서드의 실행이므로, 메서드를 선정하는 기능을 한다.

구현

  • 런타임 시점에 프록시 객체를 생성하여 공통 기능 삽입
    • 컴파일러나 클래스 로더를 설정하지 않아도 된다.
    • 프록시는 메서드 오버라이딩 개념으로 동작하기 때문에, 스프링 AOP는 메서드 실행 시점에만 AOP를 적용할 수 있다.
    • 스프링 컨테이너가 관리할 수 있는 빈에만 AOP를 적용할 수 있음.
    • AspectJ를 직접 사용하는 것이 아닌 AspectJ의 문법을 차용하고 프록시 방식의 AOP 적용

스프링 AOP는 메서드 실행 시점에만 AOP를 적용할 수 있다?

public class A {
    public void doSomething() {
        // 어떤 작업을 수행하는 코드
        System.out.println("메서드 실행 중...");
    }
}
  • A라는 클래스에는 doSomething()이라는 메서드가 있고, 어떤 작업을 수행하고 결과를 반환한다.
@Aspect
@Component
public class LoggingAspect {
    @Before("execution(* com.example.A.doSomething(..))")
    public void beforeDoSomething(JoinPoint joinPoint) {
        System.out.println("메서드 실행 전에 로깅 수행");
    }

    @After("execution(* com.example.A.doSomething(..))")
    public void afterDoSomething(JoinPoint joinPoint) {
        System.out.println("메서드 실행 후에 로깅 수행");
    }
}
  • Spring AOP를 사용하여 doSomething() 메서드 실행 전후에 로깅을 추가
  • @Before 어노테이션이 붙은 beforeDoSomething() 메서드는 doSomething() 메서드 실행 전에 로깅을 수행하고, @After 어노테이션이 붙은 afterDoSomething() 메서드는 doSomething() 메서드 실행 후에 로깅을 수행한다.
메서드 실행 전에 로깅 수행
메서드 실행 중...
메서드 실행 후에 로깅 수행
  • 이를 통해 Spring AOP에서 joinPoint를 사용하여 메서드 시점 추가 동작을 수행할 수 있다는 것을 알 수 있다.
  • execution에서 doSomething을 제외해도 결과는 같다.

Advice

  • @Around: 메서드 실행 , 후 또는 익셉션 발생 시점
  • @Before: 메서드 호출 전
  • @AfterReturning: 메서드가 익셉션 없이 실행된 이후
  • @AfteThrowing: 메서드를 실행하는 도중 익셉션이 발생한 경우
  • @AfterAdvice: 익셉션 발생 여부 상관없이 메서드 실행 후

Reference: https://www.youtube.com/watch?v=hjDSKhyYK14&ab_channel=%EC%9A%B0%EC%95%84%ED%95%9C%ED%85%8C%ED%81%AC

profile
평범한 백엔드 개발자

0개의 댓글