프로그래밍/Java 기초

어노테이션(Annotation)

수니모스 2017. 10. 13. 13:37

 안녕하세요. 이번 글은 자바의 어노테이션에 관한 내용입니다.


 자바의 코드를 보다보면 @가 붙은 내용들이 간혹 눈에 보이실텐데요. 이것이 어노테이션입니다.


 어노테이션의 기본적인 목적은 자바의 메타데이터[각주:1]로 사용하기 위함입니다. 어노테이션 작성법은 '접근 제한자 @interface 어노테이션명 { 내용 }'입니다. 다른 용도로도 많이 사용합니다만 예제에서는 메타데이터 용도만 살펴보겠습니다. 이제 예제로 만든 어노테이션을 통해 사용법을 알아보도록 하죠.



1. CustomAnnotation



package com.tistory.sunimohs.study;  import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;  /**  * 커스텀 어노테이션<br/>  * Documented : javadoc으로 문서 생성 시 현재 어노테이션 설명 추가 <br/>  * Retention : 어노테이션을 유지하는 정책 설정<br/>  * Target : 어노테이션 적용 위치  *   * @author sunimohs  */ @Documented @Retention(RetentionPolicy.SOURCE) @Target(ElementType.FIELD) public @interface CustomAnnotation {   /**    * 어노테이션 기본 값 value()로 설정된 데이터는 '@어노테이션명(파라메터 값)'로 설정 가능하다.<br/>    * 그 외의 데이터는 '@어노테이션(파라메터 명 = 파라메터 값)'으로 사용    */   String value();      /**    * 'default 파라메터 기본 값'의 형식으로 작성 시 입력하지 않을 경우 기본 값 적용<br/>    * default를 지정하지 않을 경우는 무조건 입력받아야 함.    */   String comment() default ""; }



 여기서 살펴보면 작성한 CustomAnnotation위로 @Documented, @Retention, @Target 이라는 어노테이션이 보이는데요. 이 어노테이션들은 자바에 포함되어있는 기본 어노테이션에 속해 있습니다.

 하나씩 설명을 드리면 @Documented는 javadoc[각주:2]으로 api 문서를 만들 때 어노테이션에 대한 설명도 포함하도록 지정해주는 것입니다. 

 @Retention은 어노테이션의 유지 정책을 설정하는 것인데요. 괄호 안에 RetentionPolicy 형식의 값을 할당할 수 있습니다. RetentionPolicy는 열거형(enum)[각주:3]으로 되어있습니다. 선택할 수 있는 내용은 SOURCE, CLASS, RUNTIME 세 가지로 SOURCE는 말 그대로 소스까지 유지됩니다. 컴파일 시 사라지는 구조입니다. CLASS는 컴파일까지만 유지됩니다. 컴파일 후 생성되는 class 파일에는 남아있지만 자바가 실행되는 동안(runtime)에는 사용할 수 없습니다. RUNTIME은 자바가 VM에서 실행되는 동안에도 유지되는 것입니다.

 @Target은 어노테이션을 적용할 수 있는 위치라고 보시면 됩니다. 배열로 입력할 수도, 하나만 입력할 수도 있습니다. 입력할 수 있는 형식은 enum ElementType입니다. TYPE은 클래스, 인터페이스, enum에 적용할 수 있습니다. FIELD는 말 그대로 필드(인스턴스 변수)나 enum의 상수에 적용할 수 있습니다. METHOD는 메소드에 적용가능합니다. PARAMETER는 파라메터에, CONSTRUCTOR는 생성자에, LOCAL_VARIABLE은 지역 변수에 적용 가능합니다. ANNOTATION_TYPE은 어노테이션에만 적용할 수 있게됩니다. PACKAGE는 패키지에만 적용됩니다. 너무 당연한 설명이라 제외해도 될 뻔 했네요 ^^;


 이제 어노테이션의 내용을 어떻게 작성하는가에 대해 확인해보죠. 앞에 Retention등의 어노테이션을 사용하며 확인하셨을테지만 어노테이션을 특정한 곳에 사용할 경우에 받는 값들은 괄호 안에 작성하게 되어있습니다. 괄호안에 받는 값들은 어노테이션 내용에 메소드 정의만 작성하여 표기하죠. 위의 코드를 보시면 String value(); String comment() default "";가 보이네요. 위와 같이 작성할 경우 @CustomAnnotation을 사용할 때 @CustomAnnotation(value = "값", comment = "값")의 형식으로 입력받을 수 있습니다. 메소드 명이 어노테이션 괄호 안에 입력할 수 있는 변수 명이 되는 것이죠.


2. CustomAnnotation2



package com.tistory.sunimohs.study;  import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;  /**  * default 지정하지 않은 어노테이션  */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface CustomAnnotation2 {   String value();   String comment(); }



 이 어노테이션은 @Retention을 RUNTIME으로 잡고 String comment()의 default를 제외했습니다. 위의 어노테이션과 다른 점은 아래에 작성하도록 하겠습니다.


3. CustomAnnotation3



package com.tistory.sunimohs.study;  /**  * value() 사용 안 한 어노테이션  */ public @interface CustomAnnotation3 {   String[] values(); }



 간단하게 작성해 보았습니다. 이 어노테이션에는 @Retention, @Target을 지정하지 않았습니다. @Retention은 지정하지 않을 경우 CLASS가 디폴트로 적용됩니다. 내용에는 String[] values();가 있네요.


4. AnnotationClass


 어노테이션을 적용해보기 위해 생성한 클래스입니다.



package com.tistory.sunimohs.study;  /**  * 어노테이션을 적용한 변수들만 있는 클래스.  *   * @author sunimohs  */ public class AnnotationClass {   @CustomAnnotation("a")   private String a = "a";      @CustomAnnotation2(value = "b", comment = "설명을 쓰세요.")   private String b = "b";      @CustomAnnotation3(values = "")   private String c = "c";      @CustomAnnotation3(values = { "" })   private String d = "d"; }



 여기보면 재미있는 것이 보이는데요. 이전에 @Retention, @Target의 괄호에 값을 입력할 때는 아무런 변수명을 입력하지 않고 바로 값만 넣었었죠? 여기에도 그런게 보이네요 a 변수를 보시면 @CustomAnnotation("a")라고 입력되어 있습니다. 이런게 가능한 것은 약속된 메소드를 사용하였기 때문인데요. String value()로 정의한게 바로 이 부분입니다. value()로 정의하게 되면 기본 입력으로 설정되어 아무런 변수명을 입력하지 않고도 입력이 가능합니다. 그리고 @CustomAnnotation에 정의했던 comment()는 default를 선언해 놓았기 때문에 아무런 값도 입력하지 않아도 됩니다. default에 입력했던 값이 적용되어 value = "a", comment = ""와 같은 의미가 됩니다.
 @CustomAnnotation2는 comment()에 default를 정의하지 않았기 때문에 2개의 항목을 다 입력해야합니다.
 @CustomAnnotation3는 values()로 value()를 사용하지 않았기 때문에 하나만 입력해도 변수명을 적어야하네요.
 d 변수를 보면 @CustomAnnotation3의 values에 배열을 담았습니다. String[] values()가 배열을 입력받을 수 있기 때문인데요. 배열일 경우 하나만 입력할 때는 {} 중괄호를 제외하여 표시하여도 관계없습니다.


5. MainClass


 어노테이션이 컴파일 시 어떻게 되는지 알아볼 메인 클래스입니다.



package com.tistory.sunimohs.study;  import java.lang.reflect.Field;  /**  * 어노테이션 테스트 메인 클래스  *   * @author sunimohs  * @since 2017. 10. 10.  */ @CustomAnnotation3(values = "") public class MainClass {   public static void main(String[] args) {     AnnotationClass ac = new AnnotationClass();          try {       Class<? extends AnnotationClass> acCls = ac.getClass();              Field a = acCls.getDeclaredField("a");       Field b = acCls.getDeclaredField("b");       Field c = acCls.getDeclaredField("c");       Field d = acCls.getDeclaredField("d");              if (a.isAnnotationPresent(CustomAnnotation.class)) {         System.out.println("a 필드 어노테이션 확인");       } else if (b.isAnnotationPresent(CustomAnnotation2.class)) {         System.out.println("b 필드 어노테이션 확인");       } else if (c.isAnnotationPresent(CustomAnnotation3.class) || d.isAnnotationPresent(CustomAnnotation3.class)) {         System.out.println("c 혹은 d 필드 어노테이션 확인");       }     } catch (Exception e) {       e.printStackTrace();     }   } }



 AnnotationClass객체에서 class를 가져와 a, b, c, d 필드 객체를 가져옵니다. 각각 a, b, c, d 필드 객체에 CustomAnnotation, CustomAnnotation2, CustomAnnotation3이 적용되어있는지 확인하고 System.out.println() 메소드를 통해 표시합니다. 결과는 아래와 같습니다. 위에서 말한 바와 같이 Retention이 RUNTIME으로 지정된 CustomAnnotation2만 확인이 되네요.



b 필드 어노테이션 확인

첨부파일
0.00MB

  1. https://ko.wikipedia.org/wiki/%EB%A9%94%ED%83%80%EB%8D%B0%EC%9D%B4%ED%84%B0 [본문으로]
  2. http://docs.oracle.com/javase/8/docs/technotes/tools/windows/javadoc.html, 자바에서 지정한 형태의 주석들을 인식하여 html로 된 api 문서 형태로 만들어주는 도구입니다. [본문으로]
  3. https://docs.oracle.com/javase/tutorial/java/javaOO/enum.html, 같은 의미로 사용되는 상수를 여러개 묶어서 사용할 수 있도록 정의하는 형식. 예) 요일은 월, 화, 수, 목, 금, 토, 일로 일정하기 때문에 고정 값인 상수로 지정해서 사용하면 편하다. [본문으로]