작성자: 권지원
<aside>
IllegalStateException이 발생@annotation(com.example.DistributedLock))으로 바꿔 매칭만 요청, 어노테이션 인스턴스는 Reflection(signature.getMethod().getAnnotation())으로 직접 Annotation parameter 추출 → 바인딩 의존성 제거
</aside>@DistributedLock(분산락 AOP)과 @TrackExecutionTime(모니터링 AOP)을 같은 메서드에 붙였더니 서버 오류가 발생했다.
@DistributedLock(key = "'reservation:' + #reservationId")
@TrackExecutionTime("reservation.complete")
@Transactional
public ReservationResponseDto complete(Long memberId, Long reservationId) {
// ...
}
에러 메시지:
java.lang.IllegalStateException: Required to bind 2 arguments, but only bound 1
(JoinPointMatch was NOT bound in invocation)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.argBinding(...)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(...)
at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(...)
에러를 이해하려면 먼저 용어부터 짚고 넘어간다.
| 용어 | 의미 |
|---|---|
| Advice | @Around가 붙은 메서드 그 자체. 원래 메서드 전후에 끼어들어 실행되는 코드. lock(), measure() 같은 메서드를 말함 |
| Advice Method Parameter | Advice 메서드의 파라미터. ProceedingJoinPoint joinPoint, DistributedLock distributedLock 등 |
| 바인딩 (Binding) | Spring이 Advice 메서드 파라미터에 값을 자동으로 채워주는 것. joinPoint와 distributedLock에 Spring이 알아서 값을 넣어주는 과정 |
| 체이닝 (Chaining) | 한 메서드에 Advice가 여러 개 붙으면 줄줄이 호출되는 형태 |
| JoinPointMatch | 어떤 파라미터가 몇 번째 인덱스인지 Spring이 내부적으로 관리하는 매핑 정보 |
체이닝 실행 순서 예시:
RedisLockAspect.lock()
└─ joinPoint.proceed() 호출
└─ ExecutionTimeAspect.measure()
└─ joinPoint.proceed() 호출
└─ 실제 complete() 실행
두 Advice 모두 파라미터 바인딩 형태로 어노테이션 인스턴스를 받고 있었다.
// 분산락 AOP
@Around("@annotation(distributedLock)")
public Object lock(ProceedingJoinPoint joinPoint,
DistributedLock distributedLock) throws Throwable { }
// 모니터링 AOP
@Around("@annotation(trackExecutionTime)")
public Object measure(ProceedingJoinPoint joinPoint,
TrackExecutionTime trackExecutionTime) throws Throwable { }
@annotation(distributedLock) 처럼 소문자 변수명을 쓰면 Spring에게 두 가지를 동시에 요청하는 것이다.