공부방/JAVA

Annotation 커스텀 하기 - 백기선 자바라이브스터디

EVO. 2023. 9. 30. 00:07

애노테이션(annotation)은 사전에서 '주석' 이라고 정의한다. 주석은 /** .. */으로 표현을 하는데 이는 우리 개발자들이 소스코드를 보는 데 도움을 주는 주석이라면 애노테이션은 우리뿐 아니라 컴파일러에게 도움을 주기 위해 작성하는 주석이라고 볼 수 있다.

 

이번 글에서는 애노테이션을 이미 만들어진 built-in 애노테이션(@Override , @Deprecated , @SuppressWarnings , @SafeVarags)를 소개하는 것은 아니고 직접 애노테이션을 정의해보면서 이해를 해보도록 한다.

 

  • @Override : 컴파일러에게 오바리이딩하는 메서드라는 것을 알린다.
  • @Deprecated : 앞으로 사용하지 않을 것을 권장하는 대상에 붙인다.
  • @SuppressWarnings : 컴파일러의 특정 경고메시지가 나타나지 않게 해준다.
  • @SafeVarargers : 제네릭스 타입의 가변인자에 사용한다. (JDK 1.7)
  • @FunctionalInterface : 함수형 인터페이스라는 것을 알린다. (JDK 1.8)
  • @Native : native 메서드에서 참조되는 상수 앞에 붙인다. (JDK 1.8)

 

애노테이션 정의하는 방법

새로운 애노테이션을 정의하는 방법은 아래와 같다. '@'기호를 붙이는 것을 제외하면 인터페이스를 정의하는 것과 동일하다.

애노테이션 내에 선언된 메서드가 있는데 이를 '애노테이션의 요소(element)'라고 하며, Enum도 작성할 수 있고 자신이 아닌 다른 애노테이션도 포함할 수 있다.

 

애노테이션의 요소는 반환값이 있고 매개변수는 없는 추상 메서드의 형태를 가진다. 

 

위에 선언한 애노테이션은 이렇게 적용을 할 수 있다.

Ex0200 애노테이션의 경우 따로 기본값을 주지 않았기 때문에 애노테이션을 적용하는 쪽에서 그 값을 반드시 지정해줘야 한다. 기본값을 미리 주는 방법은 다음과 같이 default 키워드를 집어넣어도 된다.

애노테이션은 기본적으로 value라는 element를 주어지기 때문에 굳이 이렇게 요소를 선언하지 않고 커스텀 애노테이션을 다른 곳에 적용시킬때 element 이름 없이 바로 값을 집어넣으면 된다.

 

이제 메타 애노테이션을 설명할텐데 메타 애노테이션은 '애노테이션을 위한 애노테이션'이라고 보면 된다. 이 부분을 하나씩 소개하기 이전에 리플렉션의 개념이 필요하다. 먼저 리플렉션 개념을 보고 오도록 하자

https://babgeuleus.tistory.com/104

 

자바 리플렉션(Reflaction)

어노테이션의 개념을 공부하던 중 리플렉션이라는 개념이 어노테이션을 커스텀 할 때 꼭 필요하다는 것을 알게 돼 이번 기회를 통해 정리해본다. 리플렉션 이란? 리플렉(reflect)의 어원 : "반사하

babgeuleus.tistory.com

 

리플렉션 개념을 파악하고 왔다고 생각하고 설명을 이어가본다. 

Ex0300 클래스는 Ex0200 애노테이션과 Deprecated의 built-in 애노테이션을 정의하였다. 

 

이제 리플렉션을 이용해 런타임 중 Ex0300클래스에 어떤 애노테이션이 선언되었는 지 확인해보는 코드이다. 

출력결과

놀랍게도 애노테이션은 Deprecated만 출력되었다. 그 비밀은 retention 이라고 하는 유지 정책과 관련이 있다.

@Retention

애노테이션에는 유지되는 기간을 지정하는 정책이 있다. 유지정책(retention policy)의 종류는 다음과 같다. 참고로 유지정책은 대문자인 이유가 java.lang.annotation.RetentionPolicy라는 enum 타입으로 정의되어있기 때문이다.

  1. SOURCE : 소스 파일에만 존재. 클래스 파일에는 존재하지 않음.
  2. CLASS : 클래스 파일에 존재. 실행시(Runtime)에 사용 불가하며 디폴트 값. 따라서 리플렉션으로 정보를 얻을 수 없다.
  3. RUNTIME : 클래스 파일에 존재. 실행시에 사용 가능하기 때문에 리플렉션을 이용해서 런타임에도 정보를 얻을 수 있다.

아까 예제 실행 했을 때 출력하지 못한 애노테이션은 Retention을 적용하지 않아 기본값인 CLASS 정책이 적용되어서 이다. 

실행결과

@Target

애노테이션과 관련해서 어느 곳에 적용할지 대상을 지정하는 것이다. 여러 개를 지정할 때는 괄호{}을 사용해야 한다.

ANNOTATION_TYPE 애노테이션
CONSTRUCTOR 생성자
FIELD 필드(멤버변수, enum상수)
LOCAL_VARIABLE 지역변수
METHOD 메서드
PACKAGE 패키지
TYPE 타입(클래스,인터페이스,enum)
PARAMETER 매개변수
TYPE_PARAMETER 타입 매개변수(JDK 1.8)
TYPE_USE 타입이 사용되는 모든 곳(JDK1.8)

롬복에서 자주 쓰이는 애노테이션인 Getter을 확인해봤다.

SOURCE로 유지 정책을 결정해서 바이트코드로 변환도 안된다. 타겟은 FIELD와 TYPE으로 지정되어있다.

따라서 이렇게 멤버변수에 지정하거나 

클래스 타입에 지정할 수 있게 된다. 

 

@Documented

애노테이션에 대한 정보가 javadoc으로 작성한 문서에 포함되도록 한다.

 

예를들어 이런식으로 소스코드를 작성하게 되는데 

@author안에는 @Documented라는 메타 애너테이션이 붙어있다. @Override와 @SuppressWarnings를 제외하고 모든 애노테이션에는 해당 @Documented가 붙어있다. 

 

javadoc은 /** ~ */에 소스코드에 대한 주석과 @Documented을 단 애노테이션에 대한 정보를 html 형식으로 생성해주는 도구라고 보면 된다. 예를들어 oracle java document 공식문서가 javadoc 도구로 만든 html이다.

 

인텔리제이에서 javadoc을 생성하는 방법은 다음과 같다. 

JavaDoc 생성 클릭

대충 다음과 같이 설정하고 생성 버튼을 누른다. 

공식문서처럼 순식간에 만들어진다.

 

 

 

 

애노테이션 프로세서

  • Java 5에 처음 등장하고 Java6 부터 활성화
  • 컴파일 단계에서 추가 바이트코드를 생성하는 편리한 기술 
  • 일반적으로 애노테이션에 대한 코드베이스를 검사,수정 또는 생성하는데 사용된다. 본질적으로 애노테이션 프로세서는 javac의 플러그인의 일종이다
  • 애노테이션 프로세서를 적재적소에 잘 사용한다면 개발자의 코드를 단순화 할 수 있다. 

다음은 롬복에서 어노테이션 프로세서를 쓰고 있는 상속관계 아키텍처이다.

어노테이션 프로세서 동작 방식(간략하게)

어노테이션 프로세서는 컴파일 시점에 특정 어노테이션을 찾아 처리하는 역할을 하는데, 이 과정은 여러 라운드(round)로 나누어 진행된다. 각 라운드에서, 프로세서는 이전 라운드에서 생성된 소스파일과 클래스 파일에서 발견된 어노테이션의 하위 집합을 처리하게 된다. 

 

첫번째 라운드 : javac 실행을 위한 초기 입력 단계로 javac 컴파일러 도구는 다음과 같은 방식으로 Processor 인터페이스를 구현한 클래스와 상호 작용한다.

  1. 기존 Processor 객체가 사용되지 않는 경우, javac는 Processor 클래스의 인자 없는 생성자를 호출하여 Processor 인스턴스를 생성
  2. javac는 적절한 ProcessingEnvironment를 가지고 init 메서드 호출
  3. javac는 한 번만 실행되는 getSupportedAnnotationTypes, getSupportedOptions 그리고 getSupportedSourceVersion 메서드 호출
  4. javac는 Processor 객체에 있는 process 메서드 호출한다. 

직접 어노테이션 프로세서를 이용해서 어노테이션을 만들어봤다.

https://babgeuleus.tistory.com/107

 

@GetterSetter을 만들어보자

왜 @GetterSetter을 만들어보려 하나요? 어노테이션 프로세서를 공부하면서 직접 Lombok이 가지고 있는 기능인 @Getter와 @Setter을 만들어보고 싶다는 생각이 들었다.그리고 롬복이 어떤 방식으로 작동

babgeuleus.tistory.com

 

 

 

참고