본문 바로가기

Java

[Java] 어노테이션(Annotation)의 개념과 직접 정의하는 방법

어노테이션을 사용하는 이유 (효과) 는 무엇일까?

자바에서 어노테이션(annotation)은 소스 코드에 메타데이터를 추가하는 방법이다. 이러한 메타데이터는 컴파일러나 런타임 시에 특정 작업을 수행하도록 정보를 전달하는 데 사용된다. 이를테면, 어노테이션을 사용하여 컴파일러에게 코드의 유효성을 확인하도록 지시하거나, 런타임에 특정 작업을 수행하도록 지시할 수 있다.

어노테이션의 사용 이유와 효과는 다음과 같다.

  1. 코드 문서화: 어노테이션을 사용하여 코드에 메타데이터를 추가함으로써, 코드를 문서화하고 다른 개발자들에게 코드의 의도나 사용 방법을 명확하게 전달할 수 있다.
  2. 컴파일 타임 체크: 어노테이션을 사용하여 컴파일러에게 특정한 규칙을 검사하도록 지시할 수 있다. 예를 들어, @Override 어노테이션을 사용하여 메서드가 슈퍼 클래스나 인터페이스의 메서드를 오버라이드하는지를 컴파일러가 확인하도록 할 수 있다.
  3. 런타임 처리: 어노테이션을 사용하여 런타임에 특정한 작업을 수행하도록 할 수 있습니다. 예를 들어, 스프링 프레임워크에서는 @Autowired 어노테이션을 사용하여 의존성 주입을 자동으로 처리한다.
  4. 코드 분석 및 처리: 어노테이션을 사용하여 코드를 분석하고 처리할 때 유용하다. 예를 들어, 테스트 프레임워크에서는 @Test 어노테이션을 사용하여 테스트 메서드를 식별한다.
  5. 코드 생성: 어노테이션 프로세서를 사용하여 어노테이션에 대한 추가적인 코드를 생성할 수 있다. 이를 통해 반복적이고 지루한 작업을 자동화할 수 있다.
  6. 런타임 동적 처리: 리플렉션(reflection)을 통해 런타임에 어노테이션 정보를 읽어서 동적으로 특정 작업을 처리할 수 있다.

이러한 이유로 어노테이션은 자바에서 매우 유용하게 사용된다.

 

나만의 어노테이션은 어떻게 만들 수 있을까?

  1. @interface 키워드 사용: 어노테이션을 정의하기 위해 @interface 키워드를 사용한다.
  2. 원하는 속성 정의: 어노테이션에 필요한 속성을 정의한다. 이 속성은 메서드처럼 선언되며, 반환 타입을 명시해야 한다.
  3. 기본값 설정(Optional): 속성에 기본값을 설정할 수 있다. 이를 통해 어노테이션 사용 시 값을 지정하지 않아도 된다.
  4. @Retention 및 @Target 어노테이션 설정(Optional): @Retention 어노테이션을 사용하여 어노테이션의 보존 정책을 지정하고, @Target 어노테이션을 사용하여 어노테이션을 적용할 수 있는 대상을 지정한다.

다음은 어노테이션을 만드는 간단한 예시이다.

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME) // 런타임까지 어노테이션 정보 유지
@Target(ElementType.METHOD) // 메서드에만 적용 가능
public @interface MyAnnotation {
    String value(); // 속성 정의
    int number() default 0; // 기본값 설정 가능
}

 

위의 코드는 MyAnnotation이라는 어노테이션을 정의한다. 이 어노테이션은 메서드에만 적용되며, value와 number라는 두 개의 속성을 가지고 있다. value 속성은 필수로 값을 가져야 하며, number 속성은 기본값으로 0을 가진다.

이제 이 어노테이션을 다음과 같이 사용할 수 있다.

public class MyClass {
    @MyAnnotation(value = "Hello", number = 10)
    public void myMethod() {
        // 메서드 내용
    }
}

 

위의 코드에서 @MyAnnotation 어노테이션을 myMethod 메서드에 적용하고, 속성인 valuenumber에 값을 지정했다.

 

 


 

다음은 로깅 기능에 사용자 정의 어노테이션을 적용하는 예시이다.

 

1. 어노테이션 정의

메서드 실행 시간을 측정하겠다는 의미의 @LogExecutionTime 어노테이션 정의

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LogExecutionTime {
}

 

2. 어노테이션을 사용하는 서비스 클래스

@LogExecutionTime이 붙은 doWork() 메서드는 실행 시간을 측정하고, 붙지 않은 noLogMethod() 메서드는 그냥 실행된다.  

public class MyService {
    @LogExecutionTime
    public void doWork() throws InterruptedException {
        // 실제 처리 로직
        Thread.sleep(1000); //1초 대기
        System.out.println("작업 완료");
    }

    public void noLogMethod() {
        System.out.println("이 메서드는 로깅 안함");
    }
}

 

3. 어노테이션 처리 클래스 (리플렉션 활용)

LogProcessor: 리플렉션으로 해당 어노테이션이 붙은 메서드를 찾아 실행 시간을 측정하는 클래스

import java.lang.reflect.Method;

public class LogProcessor {
    public static void main(String[] args) throws Exception {
        MyService service = new MyService();
        Class<?> clazz = service.getClass();

        for (Method method : clazz.getDeclaredMethods()) {
            if (method.isAnnotationPresent(LogExecutionTime.class)) {
                long start = System.currentTimeMillis();
                method.invoke(service); // 어노테이션이 붙은 메서드 실행
                long end = System.currentTimeMillis();

                System.out.println("실행 시간 (" + method.getName() + "): " + (end - start) + "ms");
            } else {
                method.invoke(service); // 일반 메서드 실행
            }
        }
    }
}

 

⇒ 실행 결과 예시

작업 완료
실행 시간 (doWork): 1001ms
이 메서드는 로깅 안함

 

⇒ 이와 같이 사용자 어노테이션을 활용하면 실제 비즈니스 로직인 MyService는 로깅과 분리되어 관심사의 분리(Separation of Concerns)가 잘 이뤄짐을 알 수 있다.