Spring AOP

Spring AOP 강좌 04강 — Pointcut 표현식 제대로 쓰기

🎯 학습 목표

  • execution 표현식의 문법을 읽고 쓴다.
  • @annotation, within 등 다른 지정자를 사용한다.
  • Pointcut을 조합(&&, ||, !)한다.

📖 execution 문법

execution(수식어? 반환타입 패키지.클래스.메서드(파라미터) 예외?)

execution(* com.example.service.*.*(..))
          │  │                   │ │  └ 파라미터: (..) = 개수/타입 무관
          │  │                   │ └ 메서드: * = 모든 메서드
          │  │                   └ 클래스: * = 모든 클래스
          │  └ 패키지
          └ 반환타입: * = 모든 타입

💻 자주 쓰는 패턴

// service 패키지 + 하위 패키지(..) 전체
@Pointcut("execution(* com.example.service..*(..))")

// 이름이 find 로 시작하는 메서드만
@Pointcut("execution(* com.example..*.find*(..))")

// 반환타입이 특정 타입
@Pointcut("execution(java.util.List com.example..*(..))")

// 파라미터가 (Long) 하나인 메서드
@Pointcut("execution(* com.example..*(Long))")

💻 다른 지정자

// within: 특정 타입(클래스) 내부의 모든 조인포인트
@Pointcut("within(com.example.service.MemberService)")

// @annotation: 특정 어노테이션이 붙은 메서드 (5·6강 핵심)
@Pointcut("@annotation(com.example.aop.LogExecutionTime)")

// @within: 특정 어노테이션이 붙은 클래스의 모든 메서드
@Pointcut("@within(org.springframework.stereotype.Service)")

// bean: 특정 이름의 스프링 빈
@Pointcut("bean(memberService)")

💻 조합

// service 패키지이면서 + Repository는 제외
@Pointcut("execution(* com.example.service..*(..)) " +
          "&& !execution(* com.example.service..*Repository.*(..))")

// 두 Pointcut 메서드를 조합
@Before("serviceLayer() && loggable()")
public void advice(JoinPoint jp) { /* ... */ }

⚠️ 주의사항

  • 스프링 AOP는 메서드 실행(execution) 조인포인트만 지원합니다(필드 접근 등은 불가 — 그건 AspectJ 위빙 필요).
  • 표현식이 너무 넓으면(예: execution(* *(..))) 모든 빈에 프록시가 생겨 성능·예측성이 나빠집니다.

💡 팁

  • 공통 Pointcut은 별도 클래스에 모아 @Pointcut 메서드로 정의하고 여러 Aspect에서 참조하세요.
  • 실무에서는 패키지 표현식보다 커스텀 어노테이션(@annotation) 방식이 의도가 명확해 선호됩니다(다음 강).