Pointcut Expression 예제들

2025. 6. 7. 16:32Spring Framework/Aspect Oriented Programming with Spring

🧩 Pointcut Expression 전체 구조:

execution(modifiers-pattern? return-type declaring-type? method-name-pattern(param-pattern) throws-pattern?)

 

🧪 Pointcut Expression 예제들


execution(
   *                // 리턴 타입: 아무 타입이나
   transfer         // 메서드 이름: transfer라는 이름
   (..)             // 파라미터: 개수와 타입 상관없이
)

 

🔍 세부 구성 설명

구성 요소 설명
execution AOP의 표현식 종류 중 하나로, 메서드 실행 조인포인트를 지정합니다. Spring AOP는 이 execution() 만 지원합니다.
* 리턴 타입 패턴입니다. 별표(*)는 어떤 리턴 타입이든 허용한다는 의미입니다. 예: void, String, Account, 등 전부 포함됩니다.
transfer 정확한 메서드 이름입니다. 여기서는 transfer라는 이름을 가진 메서드만 매칭됩니다.
(..) 파라미터 패턴입니다.
- ..은 0개 이상의 아무 타입의 파라미터를 의미합니다.
- 예: transfer(), transfer(String), transfer(Account, BigDecimal) 등 모두 포함됩니다.

 

✅ 이 표현식이 매칭하는 대상 예시

다음과 같은 메서드들이 포인트컷에 매칭됩니다:

public void transfer() { }

public String transfer(String fromAccount, String toAccount) { }

protected Account transfer(Account a, BigDecimal amount) { }

단, 메서드 이름이 정확히 transfer이어야만 합니다.

매칭된다는 의미
포인트컷 표현식이 해당 메서드를 조인포인트(Join Point)로 인식하여, 어드바이스(Advice)를 적용할 수 있는 대상이 된다는 의미입니다

 

❌ 이 표현식이 매칭되지 않는 대상 예시

public void transferMoney() { }          // 이름이 다름
public void doTransfer(String a) { }     // 이름이 다름

 

📌 참고: 자주 사용되는 변형 예시

표현식 의미
execution(public * *(..)) 모든 public 메서드 (리턴 타입, 이름, 파라미터 관계없이)
execution(* com.example.service.*.*(..)) 특정 패키지의 모든 클래스의 모든 메서드
execution(* transfer(..)) 이름이 정확히 transfer인 모든 메서드
execution(* *Transfer(..)) 이름이 *Transfer로 끝나는 메서드
execution(* transfer*(..)) 이름이 transfer로 시작하는 메서드

 

🧠 요약

요소 의미
execution 메서드 실행 조인포인트 지정
* 리턴 타입 상관없음
transfer 메서드 이름: 정확히 transfer
(..) 파라미터 개수/타입 상관없음

 


@Pointcut("within(com.intheeast.aspectjsupport.declaringpointcut.service.TransferService)")

이 포인트컷은 다음 의미를 가집니다:

 

클래스 TransferService 안에 선언된 모든 메서드 실행(join point)에 대해 포인트컷을 설정한다.

 

즉, TransferService 클래스 내의 모든 메서드가 어드바이스의 적용 대상이 됩니다.

 

🧩 구성 요소 분석

구성 요소 의미
@Pointcut(...) 이 메서드는 AOP 포인트컷 시그니처로 사용됨
"within(...)" 포인트컷 지시어 중 하나. 클래스 또는 패키지 범위로 "내부" 메서드들만 지정
com.intheeast.aspectjsupport.declaringpointcut.service.TransferService 대상 클래스의 FQCN (Fully Qualified Class Name)

 

 

✅ 예를 들어, 다음 클래스가 있다면:

package com.intheeast.aspectjsupport.declaringpointcut.service;

public class TransferService {

    public void transferMoney() { }

    public void logTransaction() { }

    private void audit() { }
}

위 포인트컷은 다음 메서드들을 모두 타겟팅합니다:

  • public void transferMoney()
  • public void logTransaction()
  • private void audit() ← (Spring AOP는 기본적으로 public method만 프록시로 감쌈 — 주의)

 

⚠️ 주의 사항: within vs execution

표현식 설명
within(FQCN) 클래스 내부의 모든 메서드를 타겟으로 삼음.
단, 상속된 메서드는 포함되지 않음
execution(* com.intheeast..TransferService.*(..)) 해당 클래스의 메서드 실행뿐 아니라, 상속받은 메서드도 포함

 

📌 실무 팁: within은 보통 "범위 필터"로 사용

within(...)은 다음과 같은 경우에 자주 쓰입니다:

  • 특정 패키지 또는 클래스 안에서만 AOP 적용하고 싶을 때
  • execution(...) 보다 더 넓은 범위를 잡고 싶은 경우

예:

@Pointcut("within(com.intheeast..*)") // 특정 루트 패키지 아래 모든 클래스의 모든 메서드

 

🎯 정리

항목 내용
목적 특정 클래스 내부의 모든 메서드에 AOP를 적용하려는 포인트컷
표현식 @Pointcut("within(fully.qualified.ClassName)")
장점 코드 내 범위를 명확히 지정 가능
단점 상속 메서드 포함 안됨, 리턴타입·파라미터 조건 불가능
보완 세부 조정은 execution(...)과 조합

 


@Pointcut("this(com.intheeast.aspectjsupport.declaringpointcut.service.TransferService)")

이 포인트컷은 this 포인트컷 지시어를 사용한 것으로, 프록시 객체의 타입을 기준으로 매칭합니다. execution, within, target과는 완전히 다른 목적을 갖고 있으므로 그 차이를 이해하는 것이 중요합니다.

 

🔍 핵심 의미

현재 실행 중인 프록시 객체(this)의 런타임 타입이 TransferService 또는 그 하위 타입인 경우에 매칭된다.

즉, AOP 프록시 객체가 TransferService 타입이어야만 이 포인트컷에 매칭됩니다.

 

🧩 구성 요소 분석

요소 설명
@Pointcut(...) AOP 포인트컷 시그니처 정의
"this(...)" 프록시 객체의 실제 런타임 타입을 기준으로 조인포인트를 매칭
com.intheeast.aspectjsupport.declaringpointcut.service.TransferService 프록시 객체가 이 타입일 때만 매칭됨

 

✅ 예제

대상 클래스

public interface TransferService {
    void transfer();
}
@Component
public class TransferServiceImpl implements TransferService {
    public void transfer() {
        System.out.println("Transferring...");
    }
}

AOP 설정

@Aspect
@Component
public class LoggingAspect {

    @Pointcut("this(com.intheeast.aspectjsupport.declaringpointcut.service.TransferService)")
    public void transferServiceProxy() {}

    @Before("transferServiceProxy()")
    public void log(JoinPoint jp) {
        System.out.println("프록시가 TransferService 타입입니다: " + jp.getSignature());
    }
}

 

결과

  • TransferServiceImplSpring AOP에 의해 프록시로 감싸져 있고, 그 프록시가 TransferService 인터페이스를 구현하고 있다면 → 이 포인트컷이 매칭됩니다.
  • 즉, 프록시 객체가 TransferService 인터페이스 타입이어야 함

 

⚠️ 주의: this vs target

표현식 기준 객체 설명
this(...) 프록시 객체의 타입 즉, Spring이 만들어준 AOP 프록시 객체가 TransferService여야 매칭됨
target(...) 실제 타겟 객체의 타입 프록시 뒤에 감싸진 실제 구현 클래스의 타입 기준으로 매칭

 

예: JDK 동적 프록시일 경우

프록시 타입: com.sun.proxy.$Proxy123  (인터페이스 기반)
this(인터페이스) → ✔️ 매칭
this(구현 클래스) → ❌ 안 됨
target(구현 클래스) → ✔️ 매칭

 

예: CGLIB일 경우

프록시 타입: TransferServiceImpl$$EnhancerBySpringCGLIB
this(구현 클래스) → ✔️ 매칭
this(인터페이스) → ✔️ 매칭
target(구현 클래스) → ✔️ 매칭

 

✅ 정리

항목 내용
포인트컷 표현식 this(com.intheeast.aspectjsupport.declaringpointcut.service.TransferService)
기준 프록시 객체의 런타임 타입이 TransferService일 때 매칭
사용 목적 **인터페이스 기반 프록시 (JDK 동적 프록시)**에 적합
차이점 target()은 실제 타겟 객체 기준, this()는 프록시 기준
주의사항 프록시 생성 전략(JDK vs CGLIB)에 따라 매칭 여부가 달라짐

 


@Pointcut("target(com.intheeast.aspectjsupport.declaringpointcut.service.SpecialService)")

이 포인트컷 표현식은,

현재 조인포인트(메서드 호출)의 실제 타겟 객체(프록시 뒤의 target 객체)의 런타임 타입이 SpecialService 타입이거나 그 하위 타입일 경우에 매칭된다.

즉, 프록시 뒤에 있는 실제 구현 객체의 타입을 기준으로 판단합니다.

 

🧩 구성 요소 분석

구성 요소 설명
target(...) AOP 프록시가 호출을 위임하는 타겟 객체의 타입을 기준으로 매칭
SpecialService 해당 타입이거나 그 하위 타입이면 매칭됨
@Pointcut(...) 어드바이스에서 사용할 포인트컷 시그니처 정의

 

✅ 예제 코드

1. 대상 클래스

package com.intheeast.aspectjsupport.declaringpointcut.service;

public interface SpecialService {
    void perform();
}
@Component
public class SpecialServiceImpl implements SpecialService {
    public void perform() {
        System.out.println("업무 실행 중...");
    }
}

2. Aspect 클래스

@Aspect
@Component
public class LoggingAspect {

    @Pointcut("target(com.intheeast.aspectjsupport.declaringpointcut.service.SpecialService)")
    public void specialServiceTarget() {}

    @Before("specialServiceTarget()")
    public void logBefore(JoinPoint jp) {
        System.out.println("👉 타겟 객체 타입이 SpecialService: " + jp.getSignature());
    }
}

 

🧪 실행 흐름

Spring AOP는 프록시 객체를 생성하고, 해당 프록시는 내부적으로 타겟 객체 (SpecialServiceImpl) 에게 실제 호출을 위임합니다.
이때 target(...) 포인트컷은 실제 위임받는 타겟 객체(SpecialServiceImpl)의 타입이 SpecialService인지 확인하여 어드바이스 적용 여부를 결정합니다.

 

⚠️ target vs this 차이

항목 this(...) target(...)
기준 프록시 객체의 타입 타겟 객체(프록시 뒤 실제 객체)의 타입
JDK 동적 프록시 this(인터페이스)만 가능 target(구현 클래스) 가능
CGLIB 프록시 this(인터페이스 또는 클래스) 가능 target(구현 클래스) 가능
주의점 프록시 타입에 따라 매칭 안 될 수 있음 실제 객체 타입이 기준이므로 매칭률 높음

 

예시

SpecialService proxy = new SpecialServiceImpl();  // 프록시 객체

 

표현식 매칭 여부 (JDK 프록시 기준)
this(SpecialService) ✅ 매칭됨
this(SpecialServiceImpl) ❌ 매칭 안 됨
target(SpecialServiceImpl) ✅ 매칭됨

 

🧠 정리

항목 내용
포인트컷 표현식 target(com.intheeast.aspectjsupport.declaringpointcut.service.SpecialService)
기준 실제 타겟 객체의 타입
사용 목적 구현 클래스 기반 매칭이 필요할 때
장점 프록시 생성 전략(JDK or CGLIB)에 관계없이 매칭 성공률 높음
실무 팁 this()는 인터페이스 기반 AOP일 때 주로 사용, target()은 구현체 기준 판단 시 안전

 


@Pointcut("args(String, ..)")

이 포인트컷은 Spring AOP에서 메서드 호출 시점의 실제 아규먼트(=런타임 파라미터)의 타입을 기준으로 매칭하는 args 포인트컷입니다.

 

🔍 핵심 의미

이 포인트컷은 첫 번째 아규먼트가 String 타입이고, 그 뒤에 0개 이상의 어떤 아규먼트가 추가로 존재해도 상관없는 메서드를 타겟으로 지정합니다.

즉, 아래 조건을 만족하는 조인포인트(join point) 에만 어드바이스가 적용됩니다:

  • 메서드를 실행할 때,
  • 첫 번째 파라미터가 String 타입이고,
  • 그 뒤는 어떤 타입이든, 몇 개든 허용

 

🧩 구성 요소 분석

표현식 의미
args(...) 런타임 시점에 메서드에 전달되는 실제 아규먼트의 타입으로 매칭
String, .. 첫 번째 아규먼트는 String, 그 이후는 0개 이상의 아무 타입

 

✅ 예제 코드

AOP 설정

@Aspect
@Component
public class ArgumentAspect {

    @Pointcut("args(String, ..)")
    public void firstArgIsString() {}

    @Before("firstArgIsString()")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("📢 첫 번째 아규먼트가 String입니다: " + joinPoint.getSignature());
    }
}

타겟 클래스

@Component
public class ExampleService {

    public void greet(String name) {
        System.out.println("Hello " + name);
    }

    public void greet(String name, int count) {
        for (int i = 0; i < count; i++) {
            System.out.println("Hello " + name);
        }
    }

    public void test(int num, String str) {
        System.out.println(num + ", " + str);
    }

    public void noArgs() {
        System.out.println("Nothing");
    }
}

 

매칭 결과

메서드 시그니처 매칭 여부 이유
greet(String) 첫 번째 인자가 String
greet(String, int) 첫 번째 인자가 String, 이후는 허용
test(int, String) 첫 번째 인자가 int
noArgs() 인자가 없음

 

⚠️ 주의: args vs execution의 차이

표현식 기준 매칭 기준
execution(* greet(String, ..)) 정적(static) 시그니처 메서드의 정의에 따라 컴파일 시점에 판단
args(String, ..) 동적(dynamic) 아규먼트 타입 런타임에 실제 아규먼트 타입을 기준으로 판단

즉, args(...)는 런타임에 다형성을 반영할 수 있고, execution(...)정확한 시그니처만 매칭합니다.

 

🎯 정리

항목 내용
표현식 args(String, ..)
의미 첫 번째 아규먼트가 String이고, 이후는 어떤 타입이든 허용
기준 런타임 시점에 전달된 아규먼트 타입
실무 활용 특정 타입의 입력 파라미터가 들어올 때만 로깅, 인증 등 적용 가능
비교 args는 동적 타입 검사 / execution은 정적 타입 검사

 


@Pointcut("@annotation(com.intheeast.aspectjsupport.declaringpointcut.annotation.Loggable)")

이 포인트컷은 Spring AOP에서 애노테이션을 기준으로 어드바이스를 적용하기 위해 사용됩니다.
이 표현식은 매우 유용하며, 실제 실무에서 로깅, 보안, 트랜잭션 처리 등에 많이 활용됩니다.

 

✅ 핵심 의미

@Loggable 애노테이션이 붙은 메서드에 대해 AOP 어드바이스를 적용하라.

즉, 해당 포인트컷은 @Loggable 애노테이션이 선언된 메서드에만 어드바이스가 적용됩니다.

🧩 구성 요소 설명

요소 설명
@Pointcut(...) 포인트컷 시그니처를 정의하는 메서드
@annotation(...) 조인포인트의 메서드에 붙은 애노테이션을 기준으로 매칭
com.intheeast.aspectjsupport.declaringpointcut.annotation.Loggable 사용자 정의 애노테이션 (Fully Qualified Name)

 

✅ 예제 코드 구성

1. 사용자 정의 애노테이션 @Loggable

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Loggable {
}
  • @Target(ElementType.METHOD): 메서드에만 적용 가능
  • @Retention(RetentionPolicy.RUNTIME): 런타임에 유지되어 AOP에서 접근 가능

2. 애노테이션을 사용하는 서비스

@Component
public class MyService {

    @Loggable
    public void performTask() {
        System.out.println("작업 수행 중...");
    }

    public void doNothing() {
        System.out.println("아무 일도 하지 않음");
    }
}

3. Aspect 정의

@Aspect
@Component
public class LoggingAspect {

    @Pointcut("@annotation(com.intheeast.aspectjsupport.declaringpointcut.annotation.Loggable)")
    public void loggableMethods() {}

    @Before("loggableMethods()")
    public void logBefore(JoinPoint jp) {
        System.out.println("🎯 @Loggable 애노테이션이 붙은 메서드 실행 전: " + jp.getSignature());
    }
}

4. 실행 결과

[MyService.performTask()] 호출 시:
🎯 @Loggable 애노테이션이 붙은 메서드 실행 전: void performTask()
작업 수행 중...

[MyService.doNothing()] 호출 시:
아무 일도 하지 않음
(어드바이스 적용 안 됨)

 

🔍 @annotation의 특징

항목 설명
동작 시점 런타임 시, 메서드에 붙은 애노테이션을 검사
활용 사례 로깅, 감시(auditing), 보안 체크, 권한 검사 등
전달 가능성 어드바이스 메서드에 애노테이션 자체를 파라미터로 받을 수도 있음
@Before("@annotation(loggable)")
public void logBefore(JoinPoint jp, Loggable loggable) {
    System.out.println("설명 추가: " + loggable.description());
}

단, 이 경우 @Loggable 안에 String description() 같은 필드가 있어야 합니다.

 

🧠 정리

항목 내용
표현식 @annotation(FQCN)
대상 메서드에 지정된 애노테이션
활용도 ★★★★★ (실무에서 가장 자주 쓰이는 유형 중 하나)
장점 선언적 제어가 가능하므로 코드 분리성이 뛰어남
주의 클래스가 아닌 메서드 수준에만 적용됨 (클래스 레벨은 @within 사용)

 


@Pointcut("@within(com.intheeast.aspectjsupport.declaringpointcut.annotation.SpecialComponent)")

이 포인트컷은 Spring AOP의 고급 포인트컷 표현 중 하나인 @within 을 사용하는 경우입니다. 이 표현식이 정확히 어떤 조인포인트에 매칭되는지, 언제 쓰는지, @annotation, @target, @within, @args 등과 어떤 차이가 있는지 전문가 수준으로 설명드리겠습니다.

 

✅ 핵심 의미

클래스에 @SpecialComponent 애노테이션이 선언되어 있다면,
그 클래스 안의 모든 메서드 실행 조인포인트에 AOP를 적용하라.

즉, 해당 포인트컷은 특정 애노테이션이 클래스에 붙어 있을 때, 그 클래스 안의 메서드들이 타겟이 됩니다.

 

🧩 구성 요소 설명

구성 요소 설명
@Pointcut(...) 포인트컷 시그니처 정의
@within(...) 조인포인트의 선언 클래스에 지정된 애노테이션을 기준으로 매칭
com.intheeast.aspectjsupport.declaringpointcut.annotation.SpecialComponent 클래스에 선언된 사용자 정의 애노테이션

 

📦 예제 구성

1. 사용자 정의 애노테이션

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SpecialComponent {
}

2. 서비스 클래스에 클래스 레벨 애노테이션 적용

@SpecialComponent
@Component
public class PaymentService {

    public void process() {
        System.out.println("결제 처리 중...");
    }

    public void cancel() {
        System.out.println("결제 취소");
    }
}

3. Aspect 정의

@Aspect
@Component
public class AuditingAspect {

    @Pointcut("@within(com.intheeast.aspectjsupport.declaringpointcut.annotation.SpecialComponent)")
    public void auditedComponent() {}

    @Before("auditedComponent()")
    public void beforeAdvice(JoinPoint jp) {
        System.out.println("🎯 클래스에 @SpecialComponent가 붙음: " + jp.getSignature());
    }
}

 

✅ 실행 결과

[PaymentService.process()] 호출 시:
🎯 클래스에 @SpecialComponent가 붙음: void process()
결제 처리 중...

[PaymentService.cancel()] 호출 시:
🎯 클래스에 @SpecialComponent가 붙음: void cancel()
결제 취소

 

🔍 @within vs 관련 표현식 비교

표현식 매칭 기준 대상
@annotation(A) 메서드에 애노테이션 A가 붙어 있어야 매칭 메서드 애노테이션
@within(A) 클래스에 애노테이션 A가 붙어 있어야 매칭 클래스 애노테이션
@target(A) 런타임 시 타겟 객체의 클래스에 A가 붙어 있어야 매칭 클래스 애노테이션
within(pkg.Class) 클래스 내부의 모든 메서드 매칭 (애노테이션 관계 없음) 클래스 기준
execution(...) 정적 메서드 시그니처 기준 메서드 기준

 

🎯 정리

항목 설명
표현식 @within(FQCN of Annotation)
의미 클래스에 해당 애노테이션이 붙어 있으면, 그 안의 모든 메서드가 타겟
사용 예 컴포넌트 스캔 범위 필터, 로깅, 보안, 감사 처리
장점 클래스 단위로 관심사를 적용할 수 있어 구조가 명확
주의 메서드 레벨이 아니라 클래스 레벨에 붙은 애노테이션이 기준

 


@Pointcut("@target(com.intheeast.aspectjsupport.declaringpointcut.annotation.SpecialComponent)")

이 포인트컷은 @target 포인트컷 디자인이터를 사용하는 것으로, 비슷하게 보이는 @within과는 중요한 차이점이 있습니다. 

 

✅ 핵심 의미

AOP 프록시의 타겟 객체의 클래스@SpecialComponent 애노테이션이 런타임에 존재하면, 그 객체의 모든 메서드 호출 조인포인트에 어드바이스를 적용하라.

즉,

  • 클래스에 @SpecialComponent가 붙어 있고,
  • 그 클래스가 Spring AOP에 의해 프록시로 감싸진 상태에서
  • 해당 클래스의 메서드가 실행되면
    → 이 포인트컷이 매칭됩니다.

 

🧩 구성 요소 설명

구성 요소 설명
@target(...) 런타임 시점에 타겟 객체의 클래스에 지정된 애노테이션이 존재하는지 여부로 매칭
SpecialComponent 사용자 정의 애노테이션

즉, 런타임 리플렉션을 통해 실제 프록시의 대상 객체 클래스에 애노테이션이 있는지를 검사합니다.

 

✅ 예제 코드

1. 사용자 정의 애노테이션

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SpecialComponent {
}

2. 애노테이션이 붙은 서비스 클래스

@SpecialComponent
@Component
public class OrderService {

    public void placeOrder() {
        System.out.println("주문 처리 중...");
    }
}

3. Aspect 클래스

@Aspect
@Component
public class MonitoringAspect {

    @Pointcut("@target(com.intheeast.aspectjsupport.declaringpointcut.annotation.SpecialComponent)")
    public void targetWithSpecialComponent() {}

    @Before("targetWithSpecialComponent()")
    public void beforeAdvice(JoinPoint jp) {
        System.out.println("🎯 런타임 타겟 객체에 @SpecialComponent 존재함: " + jp.getSignature());
    }
}

 

✅ 실행 결과

[OrderService.placeOrder()] 호출 시:
🎯 런타임 타겟 객체에 @SpecialComponent 존재함: void placeOrder()
주문 처리 중...

 

⚠️ @within vs @target 차이점

표현식 검사 시점 기준 설명
@within(A) 컴파일 타임 기준 클래스 선언부 클래스에 A 애노테이션이 "정적으로" 붙어 있는지
@target(A) 런타임 기준 실제 타겟 객체의 클래스 프록시의 대상 객체가 런타임에 A 애노테이션을 "보유"하고 있는지

예외 상황 (동적 프록시 활용 시)

  • @within클래스의 컴파일 정의 기준이므로 항상 동일
  • @target은 런타임 프록시 객체가 다른 객체를 감쌀 경우, 그 감싸진 객체에 따라 매칭 결과가 달라질 수 있음

 

🧠 요약

항목 설명
표현식 @target(com.intheeast.aspectjsupport.declaringpointcut.annotation.SpecialComponent)
적용 대상 런타임 기준으로 실제 타겟 객체의 클래스에 @SpecialComponent가 붙어 있는 경우
사용 목적 런타임에서 AOP가 적용될지 동적으로 판단하고 싶을 때
차이점 @within: 정적 클래스 기반 / @target: 동적 런타임 기반

 

📌 실전 팁

상황 추천 포인트컷
선언된 클래스에 따라 적용 @within(...)
런타임에서 동적으로 적용하고 싶을 때 @target(...)
메서드에 직접 애노테이션 붙인 경우 @annotation(...)

 


@Pointcut("@args(com.intheeast.aspectjsupport.declaringpointcut.annotation.Validated)")

이 포인트컷은 Spring AOP의 고급 포인트컷 표현식 중 하나로, @args 지시어를 사용합니다. 이 표현식은 실무에서 자주 쓰이지는 않지만, 파라미터 객체의 타입에 선언된 애노테이션을 기준으로 조인포인트를 매칭하는 매우 정교한 조건을 제공합니다.

 

✅ 핵심 의미

메서드 호출 시 전달된 실제 아규먼트(파라미터 객체)의 런타임 클래스가 @Validated 애노테이션이 붙은 타입일 경우에만 포인트컷이 매칭된다.

즉,

  • 메서드에 전달된 아규먼트 중
  • 그 타입의 클래스에 @Validated 애노테이션이 붙어 있을 경우
  • 어드바이스가 적용된다.

 

🔍 용어 정리

용어 의미
args(...) 전달된 런타임 아규먼트의 타입을 기준으로 매칭
@args(...) 전달된 런타임 아규먼트의 타입에 붙은 애노테이션을 기준으로 매칭

 

📦 예제 코드 구성

1. 사용자 정의 애노테이션

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Validated {
}

2. @Validated 애노테이션이 붙은 DTO

@Validated
public class PaymentRequest {
    private String cardNumber;
    private int amount;
    // getters/setters
}

3. 타겟 서비스

@Component
public class PaymentService {

    public void process(PaymentRequest request) {
        System.out.println("결제 처리: " + request.getCardNumber());
    }

    public void test(String name) {
        System.out.println("단순 문자열 처리: " + name);
    }
}

4. Aspect 정의

@Aspect
@Component
public class ValidationAspect {

    @Pointcut("@args(com.intheeast.aspectjsupport.declaringpointcut.annotation.Validated)")
    public void validatedArgs() {}

    @Before("validatedArgs()")
    public void beforeAdvice(JoinPoint joinPoint) {
        System.out.println("✅ 전달된 객체 타입에 @Validated 애노테이션이 붙어 있음: " + joinPoint.getSignature());
    }
}

 

✅ 실행 결과

paymentService.process(new PaymentRequest());
// 출력: ✅ 전달된 객체 타입에 @Validated 애노테이션이 붙어 있음

paymentService.test("hello");
// 출력 없음 (매칭 안 됨)

 

⚠️ 주의 사항

항목 설명
검사 대상 아규먼트의 런타임 타입 클래스에 선언된 애노테이션
비교 대상 @annotation(...)은 메서드에 붙은 애노테이션을 검사하고, @args(...)파라미터 객체 타입에 선언된 애노테이션을 검사
JDK 프록시의 경우 @args(...)는 파라미터 객체가 인터페이스일 경우 동작하지 않을 수 있음. 실제 클래스에 애노테이션이 선언되어 있어야 함

 

✅ 요약 비교표

표현식 설명
@annotation(A) 메서드에 애노테이션 A가 선언됨
@within(A) 클래스에 애노테이션 A가 선언됨
@target(A) 런타임 타겟 객체의 클래스에 A가 선언됨
@args(A) 파라미터 객체의 타입에 애노테이션 A가 선언됨

 

🧠 정리

항목 내용
포인트컷 표현식 @args(com.intheeast.aspectjsupport.declaringpointcut.annotation.Validated)
매칭 기준 런타임에 전달된 아규먼트의 클래스에 @Validated 애노테이션이 붙어 있는지
실용 사례 DTO에 @Validated, @Secure, @Sensitive 같은 도메인 애노테이션 붙이고, 자동으로 검증 또는 감시 적용
주의 단일 아규먼트만 매칭하거나, 여러 아규먼트 중 일부에만 적용되면 포인트컷이 매칭되지 않을 수 있음