노트

스프링 AOP

Spring AOP (Aspect-Oriented Programming)

백엔드#spring#pattern · 연결된 개념 10개

쉽게 말하면

스프링 AOP는 직원마다 출입 기록과 결재를 따로 챙기지 않고, 문 앞의 비서가 모든 일의 앞뒤에서 대신 처리해 주는 거예요. 로깅이나 트랜잭션 코드를 비즈니스 코드마다 반복해 쓰지 않아도 돼요.

비유가 깨지는 곳 비서는 문을 거쳐 들어온 호출만 잡아요. 그래서 같은 클래스 안에서 this로 부르거나 private·final 메서드면 적용되지 않고, 필드 접근까지 잡으려면 AspectJ 위빙이 필요해요.

AOP(Aspect-Oriented Programming, 관점 지향 프로그래밍)는 로깅·트랜잭션·보안·실행 시간 측정처럼 여러 클래스에 흩어져 반복되는 횡단 관심사(cross-cutting concern)를 한 곳에 모아, 비즈니스 코드를 건드리지 않고 메서드 호출 앞뒤에 끼워 넣는 방법이다. 스프링 AOP는 이를 프록시로 구현한다.

용어

  • Aspect: 끼워 넣을 부가 기능 모듈(무엇을)
  • Advice: 언제 실행할지와 그 코드. @Before, @AfterReturning, @AfterThrowing, @After, @Around
  • Pointcut: 어느 메서드에 적용할지 고르는 표현식(어디에)
  • JoinPoint: 적용 가능한 지점. 스프링 AOP에서는 항상 메서드 실행이다
  • Target: 부가 기능이 적용되는 원래 빈
  • Weaving: Aspect를 대상에 엮는 일. 스프링은 런타임에 프록시를 만들어 엮는다
@Aspect
@Component
public class TimingAspect {
    @Around("execution(* com.example.service..*(..))")
    public Object time(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.nanoTime();
        try {
            return pjp.proceed();                 // 원래 메서드 실행, 반환값을 꼭 돌려준다
        } finally {
            log.info("{} {}ms", pjp.getSignature(), (System.nanoTime() - start) / 1_000_000);
        }
    }
}
  • @Around에서 proceed()의 반환값을 돌려주지 않으면 원래 메서드의 결과가 사라진다
  • 어노테이션으로 대상을 고를 수도 있다: @Around("@annotation(com.example.Timed)")
  • 여러 Aspect의 순서는 @Order로 정한다
  • 스프링 부트는 spring-boot-starter-aspectj(3.x까지는 spring-boot-starter-aop)만 추가하면 자동 설정된다(2026 기준)
sequenceDiagram
  participant C as 호출자
  participant P as 프록시
  participant A as Advice (@Around)
  participant T as Target 메서드
  C->>P: 메서드 호출
  P->>A: Pointcut에 맞으면 Advice 실행
  A->>A: 시작 시간 기록
  A->>T: pjp.proceed()
  T-->>A: 반환값
  A->>A: 실행 시간 로그
  A-->>P: 반환값을 그대로 돌려줌
  P-->>C: 결과

한계

  • 프록시 방식이라 같은 클래스 안에서 this.method()로 부르면 적용되지 않는다(자기 호출 문제, self-invocation). @Transactional이 안 먹는 흔한 이유다
  • private·final 메서드에는 적용되지 않는다
  • 필드 접근·생성자 같은 지점까지 필요하면 AspectJ 컴파일 타임 위빙(compile-time weaving)을 쓴다

패턴으로 보면

호출을 가로채 앞뒤에 기능을 더하는 구조는 데코레이터 패턴과 같고, 대리 객체가 원본을 감싸는 점은 프록시 패턴이다(구조 패턴). 서블릿 단계에서 비슷한 일을 하는 것이 필터체인이고, 파이썬에서는 데코레이터가 같은 역할을 한다. 장고와의 비교는 스프링과 장고 비교를 본다.

출처: Spring Framework 문서: AOP Concepts · Understanding AOP Proxies

연결된 개념

이 노트를 가리키는 문서

뜻이 가까운 노트

  • 스프링에서 외부 API 호출

    스프링 앱에서 다른 서버의 REST API를 부르는 도구는 여러 세대가 있다. 지금 새 코드라면 동기 호출은 RestClient, 리액티브·비동기는 WebClient, 인터페이스 선언 방식은 HTTP Interface나 OpenFeign이 흔한 선택이다(2026 기준).

  • 스프링 빈과 IoC 컨테이너

    스프링 빈은 스프링 IoC(Inversion of Control, 제어의 역전) 컨테이너가 만들고, 의존성을 연결하고, 생명주기(lifecycle)를 관리하는 평범한 자바 객체(POJO, Plain Old Java Object)다. 컨테이너(ApplicationContext)는 이 빈들을 담는 공간이고, 내가 new로 만든 객체는 컨테이너가 모른다.

  • 스프링 설정 외부화와 프로파일

    스프링 부트는 설정값을 코드 밖(application.yml·환경변수·명령줄 인자 등)에 두고, 실행 환경에 따라 다른 값을 주입하게 해 준다. 같은 빌드 산출물을 개발·QA·운영에 그대로 쓰고 설정만 바꾸는 것이 목표다.

  • 의존성 주입 (DI)

    객체가 필요한 협력 객체를 직접 new로 만들지 않고, 바깥(스프링 컨테이너)에서 넣어 받는 방식. 무엇을 언제 만들고 어떻게 연결할지의 제어가 내 코드에서 컨테이너로 넘어가므로 제어의 역전(Inversion of Control, IoC)이라고도 부른다.

  • 동기·비동기와 블로킹·논블로킹

    동기·비동기는 결과를 언제 어떻게 받느냐, 블로킹·논블로킹은 기다리는 동안 호출한 스레드가 멈추느냐의 문제다.

보기 옵션