[Spring] Spring AOP의 동작원리, JDK Dynamic Proxy와 CGLIB

2025. 4. 28. 21:22·Backend/Spring

오늘은 Spring AOP에 대해서 공부해보겠습니다.

1. 관점 지향 프로그래밍(AOP)

1) 등장 배경, 횡단 관심사

객체지향 프로그래밍(OOP)는 프로그램을 객체(Object)라는 기본단위로 나누고 이들의 상호작용을 바탕으로 프로그래밍 하는 방법입니다. 하지만 객체지향 방식만으로 프로그래밍을 하다보면 문제 상황이 발생합니다. 

예를들어, 각 서비스 로직이 걸리는 시간을 로그로 출력한다고 가정해봅시다.

// 서비스 마다 걸리는 시간을 로그로 표시한다고 가정
try {
    ... business ...
} finally {
    log.info("time = {}", watch.stop());
}

 

수많은 서비스로직에 하나하나 이와같은 로그를 남긴다면 코드 양도 어마어마해질거고 반복된 작업을 계속해야 할 것입니다.

로깅이외에도 보안, 트랜잭션에서도 이처럼 수많은 반복을 이어나가야 할 것입니다.

로그, 보안, 트랜잭션 처럼 핵심기능을 가로지르는 기능을 횡단관심사라고 합니다.

횡단관심사




2) 관점지향 프로그래밍(AOP)

이러한 부가기능들을 핵심기능에서 분리하여, 마치 하나의 관점처럼 모듈화 하는 방식으로 코드를 구성하기위해 관점 지향 프로그래밍이 등장했습니다.

관점 지향 프로그래밍은 횡단관심사를 정형화된 규칙으로 모듈화하고 컴파일, 로딩, 런타임 단계 중 한 시점에 자동 결합하여 핵심 로직을 순수하게 유지하는 프로그래밍입니다.

관점지향 프로그래밍(AOP)는 객체지향 프로그래밍(OOP)를 보완하며, OOP의 핵심 단위는 Class인 반면, AOP의 핵심단위는 관점(Aspect)이라고 할 수 있습니다.



3) Spring에서의 AOP

Spring에서 AOP를 구현하는 방법에는 두가지가 있습니다.

  • Spring AOP
    • 컨테이너에 올라간 빈과 메서드 실행만 대상으로 함
    • 런타임 프록시를 사용해 가볍게 적용
  • AspectJ
    • 빈 여부와 상관없이 모든 클래스, 모든 조인 포인트에 대해 훨씬 폭넓은 제어 가능

오늘은 이중에서 Spring AOP에 대해서 알아보겠습니다.



2. Spring AOP

1) Spring AOP의 핵심용어

Spring AOP의 핵심용어에 대해 간단하게 알아보겠습니다.

  • Aspect
    • 여러 클래스를 가로질러 나타나는 관심사를 모듈화한 것
    • Java에서 트랜잭션 관리가 대표적
    • Spring AOP에서는 일반 클래스로 구현하거나, @Aspect 어노테이션이 붙은 클래스로 구현
  • Join Point
    • 프로그램 실행 도중에 AOP가 개입할 수 있는 지점
    • Spring AOP에서는 항상 "매서드 실행"을 의미
  • Advice
    • Aspect가 특정 Join Point에서 수행하는 동작
    • @Around, @Before, @After 등
  • Pointcut
    • 어떤 Join Point에 Advice를 적용할지 결정하는 조건식(predicate)
    • Advice는 pointcut 표현식이 매칭한 모든 Join Point에서 실행
      ex) (execution(* ..Service.*(..)))
  • AOP proxy
    • Aspect 계약을 수행하기 위해 AOP 프레임워크가 만든 객체
    • 메서드 호출을 가로채 Advice 체인을 실행
    • JDK Dynamic Proxy, CGLIB Proxy
  • Target
    • 실제 핵심 로직을 담은 원본 객체
  • Weaving
    • Aspect + Target을 결합해 Advised Object(proxy)를 만드는 과정
    • Spring AOP는 Runtime에 weaving

이러한 용어들과 개념은 Spring AOP에 한정되는것이 아니고 AOP 자체의 용어입니다.
이에 Spring AOP에 한정되는 개념들은 따로 명시해두었습니다.

추가적으로 Advice의 종류에 대해서도 알아보겠습니다.

  • Before advice
    • Join point 이전에 실행된다. 흐름을 중단할 수는 없고, 예외를 던지면 중단된다.
  • After returning advice
    • Join point가 정상 종료됐을 때 실행된다(예외 없이 메서드가 리턴한 경우).
  • After throwing advice
    • 메서드가 예외를 던지고 종료될 때 실행된다.
  • After (finally) advice
    • 메서드가 정상·예외 어떤 방식으로 끝나든 항상 실행된다.
  • Around advice
    • Join Point(메서드 호출) 전후로 둘러싸는 Advice
    • proceed() 호출 여부로 원본 메서드 실행 자체를 건너뛰거나 예외, 값을 임의로 바꿔서 리턴 가능



2) AOP의 핵심 동작원리, AOP Proxy

Spring AOP에서 Proxy는 용어에서 설명하였듯이 Aspect를 수행하기 위해 AOP 프레임워크가 만든 객체입니다.

proxy는 대리자 또는 대역이라는 뜻으로 실제 비즈니스 로직(Target)을 직접 호출하지 않고, 호출 전/후에 부가기능(Advice)를 끼워넣기 위해 사용합니다.

즉, Spring AOP는 관점(Aspect)과 핵심로직(Target)을 결합한 AOP Proxy를 만들고(weaving),
메서드 호출을 가로채 지정한 Advice를 실행한 후 핵심 로직을 호출하는 방식으로 동작합니다.


Spring AOP에서 사용하는 proxy는 두가지 종류가 있습니다.

위 그림과 같이 Proxy Factory Bean에서 인터페이스 유무를 확인하고 인터페이스가 있으면 JDK Dynamic Proxy, 없으면 CGBLIB Proxy를 사용합니다. 어떤 차이가 있는지 좀 더 자세히 알아보겠습니다.



3) 'JDK Dynamic Proxy' vs 'CGLIB Proxy'

3.1) JDK Dynamic Proxy

JDK Dynamic Proxy

JDK Dynamic Proxy는 위와 같습니다.

어떤 식으로 동작하는지 알아보겠습니다.

 

client의 호출은 proxy객체로 전달됩니다. 프록시가 받은 모든 메서드 호출은 InvocationHandler의 invoke() 메서드로 전달됩니다. 여기서 인터셉터 체인 즉 원본 메서드 호출 전/후의 추가 로직을 실행하게 됩니다. Advice 실행 후, 핵심 로직을 실행합니다. 

여기서 중요한 사실은 JDK Dynamic Proxy는 인터페이스 기반으로 동작한다는 것 입니다.

또한 JDK Dynamic Proxy는 Reflection 기능을 사용하여 기능을 구현하기 때문에 상대적으로 성능이 떨어진다는 단점이 있습니다.

3.2) CGLIB

CGLIB Proxy

CGLIB 또한 알아보겠습니다.

Client의 호출은 마찬가지로 proxy 객체로 전달됩니다. proxy는 MethodInterceptor의 intercept() 메서드를 호출합니다. InvocationHandler와 같은 역할을 합니다. Advice 처리 후에 super.method() 호출을 통해 원본 핵심 로직을 호출합니다.


중요한점은 CGLIB은 상속을 통해 이루어지며 final과 private같이 Override를 지원하지 않는 경우 해당 메서드에 Aspect를 적용할 수 없습니다.

CGLIB은 실제 바이트 코드를 조작하기에 상대적으로 빠릅니다. 또한 인터페이스 구현이 필요없기 때문에 SpringBoot에서는 인터페이스가 있더라도 Default로 CGLIB을 사용하고 있습니다.



3. 구현 예시

간단한 구현예시를 마지막으로 글을 마쳐보려고 합니다.

저번에 사용했던 annotation의 예시를 그대로 가져다 쓸 수 있습니다.

@Aspect
@Component
public class AnnotationTest {

    @Around("@annotation(test)")
    public Object printInfo(ProceedingJoinPoint joinPoint, TestAnnotation test) throws Throwable {

        String className = joinPoint.getTarget().getClass().getSimpleName();
        String methodName = joinPoint.getSignature().getName();
        CrudType crudType = test.value();
        System.out.println("Test Annotation!!" + "\n" +
                "message = " + test.message() + "\n" +
                "className = " + className + ", methodName = " + methodName + ", crudType = " + crudType);
        return joinPoint.proceed();
    }
}

 

해당 테스트 컨트롤러를 호출했을때의 결과를 확인해보겠습니다.

    @TestAnnotation(value = CrudType.READ, message = "테스트 메세지!")
    @Operation(summary = "서버가동 체크용", description = "서버가동체크")
    @GetMapping("/api/alive")
    public String alive() {
        return "OK";
    }

 

잘 작동하는것을 확인할 수 있습니다.

 

 

'Backend > Spring' 카테고리의 다른 글

[Spring] Redis Sorted Set, ZSet 을 이용한 매칭 시스템 구현하기  (0) 2025.10.15
[Spring] 로컬, AWS LightSail에서 AWS parameter store로 환경변수 관리하기  (0) 2025.07.19
[Spring] Apache.commons.exec 사용, 외부 명령어 실행 API 만들기, Java에서 Shell 사용  (0) 2024.05.26
[WebSocket] Spring, React, Stomp로 실시간 채팅, 저장 구현하기  (12) 2024.02.19
서블릿(Servlet)과 서블릿 컨테이너(Servlet Container)  (0) 2023.12.03
'Backend/Spring' 카테고리의 다른 글
  • [Spring] Redis Sorted Set, ZSet 을 이용한 매칭 시스템 구현하기
  • [Spring] 로컬, AWS LightSail에서 AWS parameter store로 환경변수 관리하기
  • [Spring] Apache.commons.exec 사용, 외부 명령어 실행 API 만들기, Java에서 Shell 사용
  • [WebSocket] Spring, React, Stomp로 실시간 채팅, 저장 구현하기
단군왕건영
단군왕건영
널리 세상을 이롭게 하고 싶은 개발자
  • 단군왕건영
    홍익인간 개발자
    단군왕건영
  • 전체
    오늘
    어제
    • 분류 전체보기 (81) N
      • TroubleShooting (14)
      • Backend (11) N
        • Java (2)
        • Spring (7) N
        • JPA (2)
      • DB (1)
      • Algorithm (7)
        • 백준 (4)
      • Frontend (0)
        • React (0)
      • Infra (3)
      • CS (37)
        • 컴퓨터구조 (25)
        • 네트워크 (12)
      • Git (3)
      • Mac (2)
      • 회고 (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • GitHub
  • 공지사항

  • 인기 글

  • 태그

    spring
    java
    MariaDB
    springboot
    Jenkins
    컴퓨터 구조
    컴퓨터구조
    docker
    네트워크
    백준
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
단군왕건영
[Spring] Spring AOP의 동작원리, JDK Dynamic Proxy와 CGLIB
상단으로

티스토리툴바