[Java] 21. 어노테이션 (Annotation)


Study/Java  2020. 5. 25. 16:51

안녕하세요. 명월입니다.


이 글은 Java에서 어노테이션 (Annotation) 사용법에 대한 글입니다.


어노테이션은 클래스나 메서드, 변수 등의 메타 데이터, 데이터의 정의를 설정하는 데이터라고 할 수 있습니다.

즉, 어노테이션의 데이터로는 실제 데이터를 적용하는 것은 아니고, 클래스, 메서드, 변수를 구분하기 위한 데이터라고 할 수 있습니다.


어노테이션은 일단 골뱅이 마크(@)로 설정합니다.

// 람다식용 인터페이스를 설정하는 어노테이션 (메서드를 두개 선언하면 에러 발생)
@FunctionalInterface
interface LambdaExpression{
  void run();
}
class Example {
  // 재정의 어노테이션 (Object까지 상속)
  @Override
  public String toString() {
    return "Hello world";
  }
  // 사용 금지를 명시하는 어노테이션
  @Deprecated
  public void print() {
    System.out.println("print");
  }
  // 경고를 없애주는 어노테이션
  @SuppressWarnings("unused")
  public void test() {
    int aaa = 0;
  }
}

정말 자주 사용하는 어노테이션으로는 Override, Deprecated가 있습니다.

Override는 클래스를 상속받고 재정의를 명시할 때 사용하는 어노테이션입니다. Deprecated는 메소드를 삭제할 수는 없고(기존에 사용하던 클래스였는데), 사용을 권장하지 않음을 알릴 때 사용하는 어노테이션입니다.

Deprecated는 이클립스에서 취소선이 생겨서 확실히 사용하면 안된다는 모습을 보여줍니다. 그러나 사용한다고 에러가 발생하는 건 아닙니다.


여기까지가 기본 Java의 어노테이션이고 이번에는 어노테이션을 만들어 보겠습니다.

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
// 클래스에서 사용하도록 설정 
@Target(ElementType.TYPE)
//어노테이션 선언
@interface TestClassAnnotaion {
  // 어노테이션 속성 (value는 속성 설정을 생략 가능하다.)
  public String value();
}
// 메서드에서 사용하도록 설정
@Target(ElementType.METHOD)
// 어노테이션 선언
@interface TestMethodAnnotaion {
  // 어노테이션 속성
  public String name() default "no name";
}
// 변수에 사용하도록 설정
@Target(ElementType.FIELD)
//어노테이션 선언
@interface TestVariableAnnotaion {
  // 어노테이션 속성 default가 없으면 필수 속성이 된다.
  public String name();
}
public class Example {
  // 클래스 어노테이션 설정
  @TestClassAnnotaion("test")
  class Node {
    // 메서드 어노테이션
    @TestMethodAnnotaion(name = "print")
    public void print() {
      // 콘솔 출력
      System.out.println("Hello world");
    }
  }
  @TestVariableAnnotaion(name = "node")
  private Node node = null;
  public Example() {
    // Node 인스턴스 생성
    node = new Node();
    // print함수 호출
    node.print();
  }
  // 시작 함수
  public static void main(String... args) {
    new Example();
  }
}

어노테이션은 특별히 프로그램 실행하는 데는 영향이 없습니다. 단지 메타 데이터로써 좀 더 여러가지 클래스와 함수, 변수등에 구분할 수 있는 정보의 활용으로써 사용이 가능합니다.

어노테이션을 생성할 때, Target으로 어노테이션의 사용 구분을 정할 수 있습니다.

타입 설명
@Target
TYPE 클래스, 인터페이스, enum에 설정
FIELD 멤버 변수에 설정
METHOD 메서드에 설정
PARAMETER 함수의 파라미터에 설정
CONSTRUCTOR 생성자에 설정
LOCAL_VARIABLE 로컬 변수에 설정
ANNOTATION_TYPE 어노테이션에 설정
PACKAGE 패키기에 설정
TYPE_PARAMETER 제네릭 타입에 설정
PACKAGE 모든 곳에 설정
@Retention
SOURCE 소스상에서만 사용되는 메타 정보
CLASS .class 파일까지는 남지만, Runtime에서는 보이지 않음
RUNTIME Runtime시에도 참조 가능함. Reflection을 통해 클래스나 메서드의 구분을 할 수 있음

그 밖에 @Inherited는 클래스 상속 시에 어노테이션도 상속받을 수 있습니다. @Documented는 자바 독을 생성할 때, 메타 데이터를 포함하는 어노테이션입니다.

사실 개인적인 생각으로는 Reflection이 아니면 어노테이션을 사용할 일은 크게 많지 않을 것 같습니다.

Reflection에서는 클래스나 메소드를 찾는 역할로 어노테이션을 사용합니다.

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

// 메서드에서 사용하는 어노테이션
@Target(ElementType.METHOD)
// Reflection에서 찾기 위한 어노테이션
@Retention(RetentionPolicy.RUNTIME)
// 어노테이션 생성
@interface CallMethod {
  // 어노테이션 속성
  public String name() default "no name";
}
// 클래스
class Example {
  // Runtime 어노테이션 설정
  @CallMethod
  public void run1() {
    // 콘솔 출력
    System.out.println("run1 call");
  }
  // 어노테이션 설정 안함
  public void run2() {
    // 콘솔 출력
    System.out.println("run2 call");
  }
  // Runtime 어노테이션 설정
  @CallMethod
  public void run3() {
    // 콘솔 출력
    System.out.println("run3 call");
  }
  // 실행 함수
  public static void main(String[] args) throws Throwable {
    // 인스턴스 생성
    Example ex = new Example();
    // Reflection에서 메서드 검색
    for(Method method : ex.getClass().getMethods()) {
      // CallMethod의 어노테이션 취득
      CallMethod anno = method.getAnnotation(CallMethod.class);
      // 어노테이션 설정이 없으면 null
      if(anno != null) {
        // CallMethod 어노테이션이 있는 함수가 실행
        method.invoke(ex);
      }
    }
  }
}

위 에제를 보면 Reflection에서 어노테이션을 설정한 함수만 호출하고 있습니다.

즉, 어노테이션으로 전략 패턴(Strategy pattern)이나 facade 패턴을 구현하는게 가능합니다.


그 외에 이클립스 같은 IDE 툴이나 Jankins 같은 CI툴에서 데이터를 주고 받기 위한 어노테이션입니다.

주석을 위한 어노테이션을 작성하는 것이라면 오히려 가독성이 떨어져서 소스가 지저분해 보이니 주석과 어노테이션은 확실히 구분해서 사용하는 게 좋습니다.


여기까지 Java에서 어노테이션 (Annotation) 사용법에 대한 글이었습니다.


궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.