2025년 11월 26일 수요일 (123일차)

Jeonghoon·2025년 11월 26일

jeonghoon's Study

목록 보기
125/128

AOP와 Custom Annotation: Target과 Retention

Java 환경(Spring AOP 등)에서 관점 지향 프로그래밍(AOP)을 적용하기 위해 커스텀 어노테이션(Custom Annotation)을 만들 때, 반드시 설정해야 하는 두 가지 핵심 메타 어노테이션인 @Target@Retention에 대한 정리

1. @Target

@Target은 어노테이션을 "어디에 부착할 수 있는가"를 결정합니다. 즉, 어노테이션의 적용 대상(Scope)을 제한하는 역할을 합니다.

주요 ElementType (java.lang.annotation.ElementType)

타입 (ElementType)설명AOP 관련 주요 용도
METHOD메소드 선언부가장 많이 사용됨. 특정 메소드 실행 전후에 로깅, 트랜잭션, 권한 체크 등을 수행할 때 사용.
TYPE클래스, 인터페이스, 열거형클래스 전체에 공통 로직을 적용하거나, Controller/Service 등을 식별할 때 사용.
FIELD멤버 변수(필드)필드 값 검증이나 특정 필드 암호화/복호화 등의 처리에 사용.
PARAMETER메소드 파라미터메소드 인자 값의 유효성 검사나 데이터 가공 시 사용.
CONSTRUCTOR생성자생성자 실행 시점의 로직 제어.

예시 코드

@Target({ElementType.METHOD, ElementType.TYPE}) // 메소드와 클래스에 붙일 수 있음
public @interface MyCustomAop { ... }

2. @Retention

@Retention은 어노테이션이 "언제까지 살아있는가"를 결정합니다. 즉, 어노테이션의 생명 주기(Lifecycle)를 설정합니다.

주요 RetentionPolicy (java.lang.annotation.RetentionPolicy)

1. SOURCE

  • 설명: 소스 코드(.java) 상에만 존재하고, 컴파일 시점에 컴파일러에 의해 삭제됩니다.
  • 바이트코드(.class): 포함되지 않음.
  • 용도: 롬복(@Getter), @Override 등 컴파일 타임에만 의미가 있거나 문서화 목적으로 사용할 때.
  • AOP 활용: 런타임에 동작하는 AOP에서는 사용할 수 없습니다.

2. CLASS (Default)

  • 설명: 컴파일된 바이트코드(.class)에는 포함되지만, JVM이 클래스를 로딩할 때 무시하여 런타임에는 사용할 수 없습니다.
  • 바이트코드(.class): 포함됨.
  • 런타임 가용성: 없음 (Reflection 불가).
  • 용도: 라이브러리 개발 시 jar 파일에는 포함되어야 하지만 런타임 로직에는 관여하지 않을 때.

3. RUNTIME (핵심)

  • 설명: 바이트코드에도 포함되고, JVM이 런타임에 로딩하여 리플렉션(Reflection)을 통해 정보를 읽을 수 있습니다.
  • 바이트코드(.class): 포함됨.
  • 런타임 가용성: 있음.
  • AOP 활용: Spring AOP 등 대부분의 AOP 프레임워크는 런타임에 동적으로 Proxy를 생성하고 어노테이션 유무를 확인하므로, 반드시 RUNTIME 정책을 사용해야 합니다.

예시 코드

@Retention(RetentionPolicy.RUNTIME) // 런타임까지 유지되어야 AOP 동작 가능
public @interface MyCustomAop { ... }

3. 종합 예제 (Spring AOP)

실제 AOP 적용을 위해 어노테이션을 정의할 때는 보통 아래와 같이 구성합니다.

3.1. 커스텀 어노테이션 정의

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)          // 1. 메소드에만 붙일 수 있도록 제한
@Retention(RetentionPolicy.RUNTIME)  // 2. 런타임에 리플렉션으로 읽을 수 있도록 유지
public @interface LogExecutionTime {
}

3.2. Aspect 구현

@Aspect
@Component
public class LogAspect {

    // @LogExecutionTime 어노테이션이 붙은 메소드를 타겟으로 함
    @Around("@annotation(LogExecutionTime)") 
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        
        Object proceed = joinPoint.proceed(); // 실제 메소드 실행
        
        long executionTime = System.currentTimeMillis() - start;
        System.out.println(joinPoint.getSignature() + " executed in " + executionTime + "ms");
        
        return proceed;
    }
}

0개의 댓글