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