Spring AOP

kangking·2024년 7월 8일
0

Spring Boot

목록 보기
10/10

AOP(Aspects Oriented Programming

관점 지향 프로그래밍

공통 관심 사항과 핵심 관심 사항을 분리하는 것

  • 공통 관심 사항(로깅, 트랜잭션 관리, 예외처리)

    여러 모듈이나 클래스에서 반복적으로 나타나는 기능

    주요 비즈니스 로직과 독립적으로 전체 시스템에 필수적인 기능들을 제공한다.

  • 핵심 관심 사항(장바구니 기능, 주문 처리)

    애플리케이션의 주요 비즈니스 로직을 구성하는 기능

AOP에선 Aspect라는 개념을 사용하여 공통 관심 사항을 정의하고, Pointcut을 통해 애플리케이션의 어느 부분에 Aspect를 적용할지를 결정한다.


AOP는 왜 사용하나?

코드의 불필요한 중복을 줄여 유지보수와 관리 효율을 높이고, 본연의 비즈니스 로직에 집중할 수 있게 해주기 때문이다.

위와 같이 Member를 추가하고 가져오는 Member의 핵심 기능(관심사)가 있는데 만약 이 메소드들의 실행 시간을 코드로 측정해달라는 요구가 발생했다고 해보자.
그렇다면 메소드마다 위와 같은 비즈니스 로직 앞뒤로 시간을 측정하는 코드를 작성해야 한다.

그냥 메소드로 만들어두고 앞뒤로 한줄씩 추가해도 될 것 같은데?

지금은 메소드나 기능이 몇개 없지만 대규모의 서비스라면? 메소드에 해당 코드를 작성하는데만 상당한 시간이 소요될 것이다.

게다가 만약 코드 작성중 변경사항이 발생하면 다시 처음부터 수정해야하는 큰 문제가 있다.

이런 경우 사용하는 것이 AOP기술이며 Spring은 이어서 설명할 원리와 기능으로 마법같은 이 기술을 가능하게 한다.

AOP적용 전

AOP를 사용하지 않으면 반복적으로 사용되는 시간측정이나 로그기록같은 추가 기능들(공통 관심사)가 비즈니스 로직에 강하게 결합되어 관리가 어려워진다.

AOP적용 후

하지만 위와 같이 AOP를 적용하여 공통 관심사(기능)를 묶어서 따로 관리하고 특정 조건을 만족할 때 적용하면 변동이나 추가가 발생하더라도 적은 비용으로 대처할 수 있다.

장점:

  • 전체 코드에 흩어져있던 공통사항을 하나로 응집시킬 수 있다.
  • 반복적인 코드들이 따로 관리되기 때문에 비즈니스 로직이 깔끔해진다.

핵심 용어

  • Target

    부가기능을 부여할 대상을 가리키는 말(공통 관심 사항이 적용될 실제 객체)

    //타겟은 실제 부가기능이 부여될 대상
    //(비즈니스 로직이있는 객체)
    public class MyService {
        public void doSomething() {
            // 비즈니스 로직
        }
    }
  • advice

    Target에게 제공할 부가기능을 담은 모듈로, 기능에 대한 코드와 시점이 명시되어있다.

    무슨 기능을 어느 시점에?

    @Before("execution(* com.example.service.*.*(..))")
    public void beforeAdvice(JoinPoint joinPoint) {
        System.out.println("Before method: " + joinPoint.getSignature());
    }
  • Pointcut

    advice를 적용할 JoinPoint를 고르는 작업

    어떤 메소드에?

    //포인트컷 안에 있는 경로에 해당하는 자바 파일들에 아래의 메소드를 적용(추가)시키겠다.
        @Pointcut("execution(* org.example.day0704.test..*.*(..))")
        private void cut(){
            System.out.println("cut");
        }
  • JoinPoint

    프로그램 실행 중 advice가 적용될 수 있는 특정 위치(시점).

    메소드 내에서 advice코드 적용 시점을 의미

    • 메소드 호출, 실행
    • 생성자 호출, 실행
    • 필드 접근
      어느 시점에?
    // 메서드 실행 전에 조언을 적용(시점 지정)
        @Before("serviceMethods()")
        public void beforeAdvice(JoinPoint joinPoint) {
            System.out.println("Before method: " + joinPoint.getSignature());
        }  
  • Aspect

    해당 클래스가 advice(공통 관심 사항)를 담고있는 모듈임을 명시

    @Aspect
    @Component
    public class MyAspect {
        @Pointcut("execution(* com.example.service.*.*(..))")
        public void serviceMethods() {}
    
        @Before("serviceMethods()")
        public void beforeAdvice(JoinPoint joinPoint) {
            System.out.println("Before method: " + joinPoint.getSignature());
        }
    }  
  • advisor

    PointCut과 advice를 하나씩 가지고 있는 Object

    @Aspect을 사용해 advice메소드를 포함하고 있는 클래스를 의미한다.

  • Before

    메소드 실행 전에 advice를 적용

  • After

    메소드 실행 후에 advice를 적용

  • AfterReturning

    메소드가 성공적으로 실행된 후에 advice를 적용

  • AfterThrowing

    메소드가 예외를 던진 후에 advice를 적용

  • Weaving

    AOP의 핵심 개념으로, Aspect를 핵심 기능 코드에 적용하는 과정이다.(AOP가 적용되는 원리)

    • 컴파일 타임: 컴파일 시점에서 바이트 코드 수준애서 주입

    • 로드 타임: JVM에 클래스를 로드할 때 클래스 로더가 주입

    • 런타임: 동적으로 메소드 호출을 가로채서 Aspect를 적용한 프록시 객체를 생성(스프링은 이 방식)

ControllerAdvice는 AOP?

ControllerAdvice도 비즈니스 로직에 붙어서 처리해야 했던 예외들을 한 곳으로 모아서 관리하고 처리하는데, 이것도 AOP인가?

  • ControllerAdvice는 관점지향프로그래밍 방식을 사용한 것
  • ControllerAdvice는 방법론만 AOP와 비슷한 것이지 실제로 AOP의 문법과 동작원리대로 만들어지진 않음

Spring AOP의 동작 원리(With 프록시)

AOP의 목표는 결과적으로 아래의 형태처럼 특정 부가기능을 수행하는 코드를 원하는 지점의, 원하는 시점에서 실행시키는 것이다.

하지만 이렇게 실행시키기 위해서는 부가기능에 대한 코드가 직접 메소드마다 작성 되어야 하는데, AOP는 모든 메소드에 코드를 작성하지 않고 한 번의 기능작성으로 마치 코드를 탈부착 하듯이 원하는 위치에서 실행시킨다.

어떤 원리로 이렇게 자동으로 동작할 수 있는걸까?

정답은 프록시 객체를 활용하는 것에 있다.

위의 자료에서 create기능의 비즈니스 로직을 호출하기 위한 방법은 service.create()이다. 그렇다면 부가 기능에 대한 실행코드가 끝나고 해당 메소드를 호출시키면 되지 않을까?

이를 시각적으로 표현하면 아래와 같다.

다른 객체의 다른 메소드에서 부가기능을 작성하여 실행하다가 원하는 시점에 진짜 실행하고자 하는 비즈니스 로직을 호출하는 형태로 만들면 비즈니스 로직에 직접적인 코드 작성이 필요 없어진다.

Spring AOP는 이러한 부가기능을 따로 작성해두고 위와같이 원하는 대상의 프록시(가짜)객체를 실제 객체 대신 Bean으로 등록 및 주입한다.

즉, 원래 객체 대신 주입되어 실제 메소드의 호출을 가로채서 AOP에 정의된 부가기능을 포함시킨 자신의 메소드를 실행시키는 방식으로 동작하는 것이다.

객체를 Bean으로 등록할 때, AOP에 정의된 기능을 적용해야하는 객체라면 위와 같은 형태의 프록시 객체를 만들어 실제 객체대신 등록하기 때문에 해당 객체의 메소드를 호출하면 실제 의존성 주입된 프록시 객체의 메소드를 호출하게 된다.

  • AOP 적용 대상이 아니라면 실제 객체를 Bean으로 등록하여 주입

  • AOP 적용 대상이라면, 프록시 객체를 Bean으로 등록하여 대신 주입(부가기능+비즈니스로직)

Proxy 객체의 Bean 등록 과정

테코톡 콩하나님의 발표 자료가 상당히 잘되어 있어 해당 영상자료 사용
[10분 테코톡] 콩하나의 스프링 AOP

1. 빈 생성후 후처리기에 전달

2. 포인트 컷을 참조해 프록시 적용 대상인지 확인

3. 프록시 적용 대상인 빈들의 프록시 객체를 생성

4. 프록시가 생성된 빈이면 프록시 객체를, 아니라면 원래 빈을 반환한다.

5. 반환받은 객체를 스프링 컨테이너의 빈으로 등록한다.

profile
하루하루 의미있게

0개의 댓글