Framework/Spring

Spring AOP 완벽 이해하기

Joonfluence 2026. 7. 30.

AOP 구현 방식 2가지

Spring은 AOP(관심사 분리 — 로깅·트랜잭션·보안 등 부가기능을 핵심 로직과 분리)를 프록시 객체로 구현한다. 대상 빈을 감싸는 프록시를 런타임에 생성하는 방법이 두 가지다.

1) JDK Dynamic Proxy

  • 조건: 인터페이스가 반드시 있어야 함
  • 원리: java.lang.reflect.Proxy + InvocationHandler로, 인터페이스를 구현하는 프록시 클래스를 런타임에 생성
  • 한계: 인터페이스에 선언된 메서드만 프록시 가능 (구현체의 인터페이스 외 메서드는 프록시 안 됨)
  • 성능: 생성되는 바이트코드가 단순해서 CGLIB보다 빠름

2) CGLIB

  • 조건: 인터페이스 불필요 — 구체 클래스를 상속해서 프록시 생성
  • 원리: ASM 바이트코드 조작 라이브러리로 대상 클래스의 서브클래스를 런타임 생성
  • 한계: final 클래스/메서드는 상속 불가라 프록시 불가, static 메서드도 프록시 불가
  • 성능: 바이트코드가 더 복잡해서 JDK Proxy보다 약간 느림 (단, 근래 버전은 격차가 많이 줄었음)

Spring/Spring Boot의 선택 기준 (헷갈리기 쉬운 지점)

  • Spring 프레임워크 자체의 기본 로직: 대상 빈이 인터페이스를 구현하면 JDK Proxy, 안 하면 CGLIB (proxy-target-class=false가 기본값)
  • Spring Boot 2.0부터: 이 기본값을 아예 뒤집어서 spring.aop.proxy-target-class=true를 디폴트로 설정 → 인터페이스가 있어도 CGLIB을 기본으로 사용
    • "인터페이스가 있으면 무조건 JDK Proxy"라고 착각하기 쉬운데, Spring Boot 프로젝트라면 실제로는 CGLIB이 기본값이라는 게 헷갈리는 핵심 포인트
    • spring.aop.proxy-target-class=false로 명시하면 JDK Proxy로 되돌릴 수 있음 (단 인터페이스 필수)

연관 함정: Self-invocation

CGLIB이든 JDK Proxy든, AOP는 프록시 객체를 통해 호출될 때만 적용된다. 같은 클래스 안에서 this.method()로 내부 호출하면 프록시를 거치지 않아 @Transactional 등이 씹힌다.

트랜잭션·보안·로깅에서의 활용

  • @Transactional, @Secured/@PreAuthorize, @Async 등이 전부 이 프록시 메커니즘 위에서 동작 — 어노테이션이 붙은 메서드 호출 전후로 프록시가 부가 로직(트랜잭션 시작/커밋, 권한 체크, 비동기 위임 등)을 끼워 넣는 구조
반응형

댓글