자바 어노테이션은 단순한 코드 보다 훨씬 더 많은 것을 제공한다. 어노테이션은 메타데이터를 통해 코드의 동작을 정의하고, 컴파일러와 개발 도구가 코드를 더 잘 이해할 수 있도록 돕는다. 이로 인해 코드의 가독성이 향상되고, 유지보수가 용이해지며, 반복적인 코드의 양을 줄일 수 있다. 또한, 개발자가 실수로 발생할 수 있는 오류를 미연에 방지할 수 있다.
어노테이션은 '@'심볼로 시작하며, 이를 통해 컴파일러에게 추가적인 정보를 제공하거나, 코드에 메타데이터를 부여한다. 자바에서는 여러 기본 제공 어노테이션이 있으며, 개발자는 사용자 정의 어노테이션도 생성할 수 있다.
메타 어노테이션은 어노테이션을 정의할 때 사용된다.
개발자는 특정 목적에 맞는 사용자 정의 어노테이션을 만들 수 있다. 사용자 정의 어노테이션은 '@interface'키워드를 사용하여 정의된다. 예를 들어, 테스트 프레임워크에서 특정 메서드를 테스트 메서드로 지정하려는 경우 사용자 정의 어노테이션을 만들 수 있다.
package c.annotation;
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 TestMethod { // (3)
public String description() default "Default test method"; // (4) 기본 설명
}
(1) : @Target은 해당 어노테이션 사용 대상을 지정한다. 여기서는 ElementType.METHOD를 소괄호 안에 넣어줌으로써 이 어노테이션은 메소드에 사용될 수 있다고 지정한 것이다.
(2) : @Retention은 어노테이션 유지 정보를 지정하는데 사용한다. 소괄호 안에 RetentionPolicy.RUNTIME으로 지정하면 실행시에 이 어노테이션을 참조하게 된다.
(3) : 어노테이션 이름인 TestMethod 앞에는 @interface가 선언되어있다. 처음 보면 익숙하지 않겠지만, 클래스나 인터페이스를 선언할 때 처럼 @interface로 선언하면 @TestMethod 로 어노테이션이 사용가능해진다.
(4) : 어노테이션 선언 안에는 description()이라는 메소드가 있다. description()의 리턴 타입은 String이다. 이렇게 메소드처럼 어노테이션 안에 선언해놓으면, 이 어노테이션을 사용할 때 해당 항목에 대한 타입으로 값을 지정 가능하다. description() 를 보면 default라는 예약어를 쓴 뒤 문자열이 지정되어 있는 것을 볼 수 있다. default 예약어를 사용할 경우에는, default 뒤에 있는 값이 이 어노테이션을 사용할 때의 기본값이 된다.
위의 TestMethod 애노테이션을 사용하는 예는 아래와 같이 사용할 수 있다.
public class ExampleTests {
@TestMethod(description = "This tests the addition method.")
public void testAddition() {
assert(1 + 1 == 2);
}
}
어노테이션은 리플렉션을 통해 런타임에 조회되며, API 메서드에 대한 문서 생성, 테스트 자동화 등 다양한 분야에서 활용될 수 있다. 예를 들어, 어노테이션을 사용하여 API 메서드의 매개변수와 반환 값에 대한 설명을 자동으로 문서화할 수 있다.
어노테이션 프로세싱(Annotation Processing)은 컴파일 타임에 어노테이션을 분석하고 처리하는 과정을 말한다. 이를 활용하면 코드 생성, 코드 검증 등 다양한 작업을 자동화 할 수 있다. 아래는 간단한 어노테이션 프로세서를 개발하고, 이를 활용하는 예제를 통해서 어노테이션 프로세싱을 설명한다.
메서드가 실행될 때 시간을 로그로 남기고 싶을 때, 다음과 같이 "LogExecutionTime" 어노테이션을 생성할 수 있다.
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.SOURCE)
@Target(ElementType.METHOD)
public @interface LogExecutionTime {}
어노테이션 프로세서를 생성하기 위해서는 AbstractProcessor 클래스를 상속받아야한다. 이 클래스에서는 process 메서드를 오버라이드하여 어노테이션이 붙은 요소들을 처리한다.
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.Processor;
import javax.tools.Diagnostic;
@SupportedAnnotationTypes("LogExecutionTime")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class TimeLoggingProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
for (Element elem : roundEnv.getElementsAnnotatedWith(LogExecutionTime.class)) {
processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Found method: " + elem.getSimpleName());
}
return true; // No further processing of this annotation type
}
}
어노테이션 사용은 아래처럼 사용한다.
public class TimeLoggingExample {
@LogExecutionTime
public void performTask() {
// Task implementation
}
}
위의 예제를 컴파일할 때는 "TimeLoggingProcessor"를 포함시켜야한다. 대부분의 IDE나 빌드 시스템에서는 어노테이션 프로세서를 자동으로 인식하고 처리한다. 컴파일 중에 performTask 메서드를 찾아 로그 메시지를 출력한다.
이렇게 어노테이션 프로세싱을 통해 코드에 메타데이터를 제공하고, 이를 기반으로 추가적인 코드를 생성하거나 검증 작업을 자동화할 수 있다. 어노테이션 프로세서를 활용하면 코드의 유지보수성을 높이고, 반복 작업을 줄일 수 있는 강력한 도구가 될 수 있다.