AOP, Aspect-Oriented Programming

김태훈·2024년 1월 19일
0

Spring

목록 보기
15/16

AOP, Aspect-Oriented Programming

AOP는 핵심 로직과 부가 기능을 분리하여 애플리케이션 전체에 걸쳐 사용되는 부가 기능을 모듈화하여 재사용할 수 있도록 지원하는 것입니다.

주로 중복되는 부분을 제거하고 횡단 관심사를 개별 모듈로 분리하여, 깔끔하게 유지보수 할 수 있도록 도와줍니다.

횡단 관심사를 개별 모듈로 분리한다는 것은 위의 예시에서 쉽게 확인할 수 있습니다.
실제 비즈니스 로직에서의 예시에서는 DB 연결, 로깅, 파일 입출력 등이 있습니다.

AOP의 주요 개념

  • Aspect(관점): 횡단 관심사를 정의한 모듈. 로깅, 보안, 트랜잭션 관리 등이 Aspect의 예시입니다.
    Join Point + Pointcut을 모듈화한 것

  • Join Point(결합 지점): 프로그램 실행 중에 Aspect가 적용될 수 있는 지점. 메서드 호출, 객체 생성, 예외 발생 등이 Join Point의 예시입니다.

  • Advice(조언): Aspect가 Join Point에서 수행하는 동작. Before, After, Around 등의 Advice 유형이 있습니다.

  • Pointcut(지점 컷): 어떤 Join Point에 Advice를 적용할지를 정의하는 표현식. Spring은 프록시 기반이기 때문에 Join Point가 메소드 실행 시점밖에 없고, Pointcut도 메소드 실행 시점만 가능합니다.

  • Weaving(위빙): Aspect를 실제 코드에 적용하는 프로세스. 코드 컴파일 시점, 클래스 로딩 시점, 실행 시점에 이루어질 수 있습니다.

Spring에서의 AOP 적용방식

AOP의 적용 방식은 크게 3가지로
1. 컴파일 시점 2. 클래스 로딩 시점 3. 런타임 시점이 있습니다.
Spring은 프록시 기반으로 AOP를 구현하며 런타임 시점으로 적용합니다.

  • 컴파일이 끝나고 클래스 로더에 이미 다 올라가 자바가 실행된 다음에 동작하는 런타임 방식
  • 실제 대상 코드는 그대로 유지되고 프록시를 통해 부가 기능이 적용
  • 프록시는 메서드 오버라이딩 개념으로 동작하기 때문에 메서드에만 적용 가능 -> 스프링 빈에만 AOP를 적용 가능합니다.
  • 특별한 컴파일러나, 복잡한 옵션, 클래스 로더 조작기를 사용하지 않아도 스프링만 있으면 AOP를 적용할 수 있기 때문에 스프링 AOP는 런타임 방식을 사용합니다.

프록시 기반이란, AOP가 대상 객체를 직접 호출하는 대신, 대상 객체에 대한 접근을 제어하고 중간에서 추가적인 동작을 수행하는 프록시 객체를 사용하는 것을 의미합니다.

Advice

Advice는 실질적으로 프록시에서 수행하게 되는 로직을 정의하는 곳입니다
스프링에서는 Advice에 대한 5가지 애너테이션이 이씃ㅂ니다.
애너테이션은 메소드에 붙이며, 해당 메소드는 advice의 로직을 정의하고 Pointcut에 지정된 대상 메소드에서 Advice가 실행되는 시점을 정할 수 있습니다.

  1. @Before
  • 조인 포인트 실행 이전에 실행(실제 target 메소드 수행 전)
  • 입력값은 조작 불가능
  • 입력값 내부에 setter 등을 통해 내부값은 수정 가능
  1. @AfterReturning
  • 조인 포인트가 정상 완료 후 실행(실제 target 메소드 수행 후)
  • 반환 값은 조작 불가능
  • 반환 값 내부에 setter 등이 있다면 수정 가능
  1. @AfterThrowing
  • 메소드가 예외를 던지는 경우 실행
  • 예외 조작 불가능
  1. @After
  • 조인 포인트의 정상, 예외 동작과 관계없이 실행
  1. @Around
  • 위의 4가지 애너테이션을 모두 포함하는 애너테이션
  • 메소드 호출 전후 작업 명시 가능
  • 조인 포인트 실행 여부 선택 가능
  • 반환값 자체를 조작 가능
  • 예외 자체를 조작 가능
  • 조인 포인트를 여러 번 실행 가능(재시도)

Spring에서 AOP 사용하기

// aop
    implementation 'org.springframework.boot:spring-boot-starter-aop:2.7.4'

Spring에서 AOP를 사용하기 위해서는 먼저 의존성을 추가해주어야 합니다.

@Slf4j
@Aspect
public class AspectV6Advice {

    @Around("execution(* com.example.mvc.order..*(..))")
    public Object doTransaction(ProceedingJoinPoint joinPoint) throws Throwable {
        try {
            // @Before 수행
            log.info("[트랜잭션 시작] {}", joinPoint.getSignature());
            // @Before 종료

            // Target 메서드 호출
            Object result = joinPoint.proceed();
            // Target 메서드 종료

            // @AfterReturning 수행
            log.info("[트랜잭션 커밋] {}", joinPoint.getSignature());
            // @AfterReturning 종료

            // 값 반환
            return result;
        } catch (Exception e) {
            // @AfterThrowing 수행
            log.info("[트랜잭션 롤백] {}", joinPoint.getSignature());
            throw e;
            // @AfterThrowing 종료
        } finally {
            //@ After 수행
            log.info("[리소스 릴리즈] {}", joinPoint.getSignature());
            //@ After 종료
        }
    }
}

@Around의 예시를 통해 나머지 4개의 애너테이션의 동작 위치를 확인할 수 있습니다.

exection

execution( com.example.mvc.order..(..))*) 은 com.example.mvc.order 패키지안의 모든 메소드를 지정합니다.

    • : 임의의 반환 타입을 의미합니다.
  • com.example.mvc.order : 패키지 이름으로, order 패키지를 나타냅니다.
  • .. : 패키지와 클래스를 나타내는 경로의 임의의 길이를 의미합니다.
    • : 클래스 이름으로, order 패키지 내의 모든 클래스를 나타냅니다.
  • (..) : 임의의 매개변수를 가진 임의의 메소드를 선택합니다.

ProceedingJoinPoint

JoinPoint의 주요 메소드입니다.

  • getArgs() : 메소드 인수 반환
  • getThis() : 프록시 객체 반환
  • getTarget() : 대상 객체 반환
  • getSignature() : 조인되는 메소드의 시그니쳐 반환
  • proceed() : 타겟 메소드 실행






reference

https://velog.io/@backtony/Spring-AOP-%EC%B4%9D%EC%A0%95%EB%A6%AC

0개의 댓글