///
Search
🚩

Annotation

태그
JAVA
메타데이터의 한 형태인 주석은 프로그램 자체의 일부가 아닌 프로그램에 대한 데이터를 제공한다.
메타데이터? 그게 뭘까? 데이터에 대한 데이터다. 도서관에서 사용하는 서지기술용으로 만든 것이 대표적인 예다. 여기서 서지 기술이란 문헌에 관한 것을 그래도 옮겨 쓰는 것이다. 길게 표현하면 특정한 서지 개체가 지닌 속성 요소를 일정한 틀로 표현하는 과정으로서, 특정 자원과 이와 다른 자원 또는 동일 저작의 다른 판과 구별하기 위한 일련의 서지 사항을 체계적으로 기록하는 행위, 또는 이렇게 기록된 일련의 서지사항이다. 이렇게 보면 어렵다. 간단하게 태그 같은 느낌이다. 우리의 학생증을 보면 “이름:”, “학번:” 을 볼 수 있다. 이런 이름을 표현 하기위한 이름이란 태그 같은 느낌의 데이터다. 자바의 Annotation은 다음과 같은 용도를 위하여 사용된다.
컴파일러에 대한 정보 : 컴파일러는 주석을 사용하여 오류를 감지하거나 경고 억제 가능
컴파일 시간 및 배포 시간 처리 : 주석 정보를 처리하여 코드, xml파일 등을 생성 가능
런타임 처리 : 일부 주석은 런타임에 검사 가능
출처 : oracle tutorial

왜 Annotation인가?

기존의 자바 웹 애플리케이션들은 대부분 설정값을 XML파일에 명시하여 관리했다. 변경될 수 있는 데이터들은 코드가 아닌 외부 설정파일에 분리하기 때문에, 재컴파일 시간이 단축되었다. 하지만, 프로그램 작성을 위해 매번 많은 설정을 해야하며, 수 많은 설정파일들을 관리하는 건 힘든 일이었다. 그래서 나온 것이 Annotation이었다. 이를 사용하면 데이터 유효성 검사 조건을 보다 쉽게 파악 가능해지고, 원하는 클래스만 메타데이터를 표현할 수 있었다.

XML vs Annotation의 논쟁

나도 이에 대하여 참 고민을 많이 했다. 과연 XML로 메타 데이터를 관리하는 것이 좋을까? Annotation으로 관리하는 것이 좋을까? 사실 이건 답이 없는 것 같다. 그런데 몇몇 의견을 종합해보고 필자가 내린 결론은 하나의 큰 단일 서비스를 고려해야한다면 XML이 좋다. 하지만 여러 작은 서비스들을 이어 붙여 하나의 서비스를 만들어야한다면 각각의 작은 서비스는 Annotation을 사용하는 편이 좋은 것 같다. 이건 내 코드에 얼마나 많은 범위로 영향이 전파되느냐의 문제라고 생각한다. 예를 들자면 결국 “Annotation의 경우 사용하려면 코드 내에 직접 달린다” 라는 조건이 있다. 이를 다시 생각해보면 개발자의 실수가 발생할 수 있는 큰 서비스의 경우 주석보다는 XML로 관리하는 편이 더 위험한 상황을 예방할 수 있다고 생각한다(어디에서 문제가 생길지 며느리도 모른다). 이런 관점에서 나라면 큰 단일서비스의 메타정보 구성은 XML, 이와 반대로 작은 서비스의 결합은 각 서비스마다 Annotation을 사용하겠다.

Annotation 정의

Annotation의 경우 java.lang.annotation,Annotationimplements하고 있다. 또한 Annotation은 리플렉션 기술을 이용해서 사용된다.
public @interface Mark { } java/lang/annotation/Annotation { // compiled from: Mark.java } ... 구조 @interface [애너테이션이름] { [타입] [요소] [이름](); // 애너테이션의 요소를 선언한다. ... ... }
Java
복사
Annotation 요소의 규칙
요소의 타입은 기본형, String, enum, annotation, Class만 허용된다.
()안에 매개변수는 선언할 수 없다.
예외를 선언할 수 없다.
요소를 타입 매개변수로 정의할 수 없다.
Annotation의 용도?
1.
컴파일러를 위한 정보를 제공하기 위한 용도 에러 체크, 에러 메시지 억제 등
2.
컴파일 시간 및 배포 시간 처리 개발툴이 Annotation 정보를 처리하여 코드, xml등을 생성하는 일 등
3.
런타임 처리 런타임에 특정 기능을 실행하도록 정보를 제공 (리플렉션 이용)

Annotation 종류

Built-in Annotation

Meta Annotation

Annotation 프로세서

앞서 우리는 어노테이션이 데이터의 데이터 역할을 한다고 알고 있다. 근데 이 뒤에 프로세서가 붙으면 뭘하는 걸까? 일반적인 어노테이션의 역할은 다음과 같다.
컴파일러에 대한 정보 : 컴파일러는 주석을 사용하여 오류를 감지하거나 경고 억제 가능
컴파일 시간 및 배포 시간 처리 : 주석 정보를 처리하여 코드, xml파일 등을 생성 가능
런타임 처리 : 일부 주석은 런타임에 검사 가능
여기에 프로세스가 붙으면 어노테이션에 정의된 일련의 프로세스를 동작하게 하는 것을 의미한다. 좀 말이 어려울 수 있다. 간단하게 동작을 하게 만든다고 생각하면 된다. 대표적으로 @Override가 있으며, Lombok이라는 라이브러리도 있다. 근데 신기하지 않은가? 동작을 만들어 낸다는 것이!

왜쓸까?

왜 쓰는지는 코드를 작성해본 사람은 안다. 작성해야할 코드가 많아지면 개발자의 실수가 많아진다. 하지만 간단한 데이터의 데이터(Annotation)으로 이러한 코드를 작성한다면 마법같은 일이 아닐까? 다음은 왜 쓰는지에 대한 이유이다. 아주 간단한 Getter, Setter를 포함한 Object이다. 서비스 초기에는 name이라는 필드만 필요했기 때문에 이렇게 만들었다. 하지만 다음 코드는 엄청 규모가 커진 이후에 SimpleObject에 포함된 필드를 보여준다. 과연 저 코드에 Getter, Setter를 순수하게 타이핑해서 만든다면 힘들지 않을까?
public class SimpleObject { String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
Java
복사
SimpleObject
public class SimpleObject { String name; int age; int address1; String address2; String name1; String birthday; int birthday1; String lastName; String middleName; String accountNick; int idxNumber; String phoneNumber; String faxNumber; etc... }
Java
복사
SimpleObject가 아닌 SimpleObject
이런 이유로 인하여 어노테이션 프로세스가 주목받고 필요해진 것 이다. 다음은 롬복을 활용하여 Getter, Setter를 만들었다. 나는 어노테이션만 정의했는데 롬복친구가 알아서 다 만들어준 것이다. Annotation Processor만 잘 사용하면 개발자의 코드를 엄청나게 단순화할 수 있다.
import lombok.Getter; import lombok.Setter; @Getter @Setter public class SimpleObject { String name; int age; int address1; String address2; String name1; String birthday; int birthday1; String lastName; String middleName; String accountNick; int idxNumber; String phoneNumber; String faxNumber; }
Java
복사
와우!

장점? 단점?

무슨 기술이건 장단점은 존재한다. 장점은 위의 단순화 말고도 많이 존재한다.
장점?
런타임 비용을 없애준다. 바이트코드를 조작하는 경우 최초 한번 클래스로딩 시에 코드가 생성되고 그 이후로의 사용에는 추가 비용이 없다.
보일러플레이트 코드를 생성해준다. 지루한 코드로부터 벗어난다. 아까의 Getter, Setter와 같이
단점?
해킹이라는 것이다. 소스코드를 어쨋건 외부에서 직접 수정, 접근하는 것이다.
아직까진 여기까지 밖에 못 찾겠다. ㅠㅠ 하지만 단점은 커버가 가능한 수준인 것 같다. Lombok을 직접 만들어보자라고 생각하시고 만드신 분이 있다.

동작 과정(Annotation Processor)

1.
자바 컴파일러가 컴파일을 수행한다. - 자바 컴파일러는 어노테이션 프로세서에 대해 미리 알고 있어야 한다.
2.
실행되지 않은 어노테이션 프로세서들을 수행한다. - 각각의 프로세서는 모두 각자에 역할에 맞는 구현이 되어있어야 한다.
3.
프로세서 내부에서 어노테이션이 달린 Element(변수, 메서드, 클래스 등)들에 대한 처리를 한다. (보통 이곳에서 자바 클래스를 생성한다.)
4.
컴파일러가 모든 어노테이션 프로세스가 실행되었는지 확인하고, 그렇지 않다면 반복해서 위 작업을 수행한다.
이 부분은 조금 더 찾아봐야겠다. 아니면 직접 사용하면서 알 것 같다. 처음에는 롬복동작 과정을 보고 적을려고 했지만 뭔가 모를찝찝함이 남아 있었다. 아래의 북마크는 참고한 자료이다.

Annotation Processor 사용

www.baeldung.com
javaPoet (기가막히네…) 필요하면 꺼내봐야겠다.
간단하게 사용법을 알아보자. 일단은 순서상으로 기억해두면 좋을 것 같다.
1.
Annotation이 필요한가? 필요하다면 Annotation을 만들자
2.
Annotation Processor을 구현하자. 세 가지의 구현 목록이 있다. getSupportedAnnotationTypes() - 이 프로세스가 처리할 어노테이션을 지정해준다. getSupportedSourceVersion() - 소스코드 버전을 몇까지 지원할 것인지 지정해 준다. rocess(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) - 어노테이션 프로세서가 처리할 어노테이션을 사용하고 있는 엘리먼트들을 참조할 수 있다.
엘리먼트란? 소스 코드를 구성하는 요소다.
패키지 엘리먼트
클래스 엘리먼트
메서드 엘리먼트
3.
패키징을 통하여 다른 프로젝트에서 참조하여 사용하면 된다.
골때리는 패키징 구성파일을 만들어주는 @AutoService라는 어노테이션이 있다. 근데 이게 뭘까하다가 찾아보니 이런 기능을 가지고 있었다. 만약 번역기능을 만들어야한다고 가정해보자. 그럼 영어 말고 스페인어도 번역해야한다. 그럼 당연히 하나의 TranslationService Interface를 두고 두가지의 영어번역기, 스페인어번역기를 구현할 것이다. 여기서 문제가 발생한다. 다른 곳에서 이 서비스를 사용하려면 META-INF\services\폴더에 존재해야한다. 근데 기존 영어번역기만 존재할때는 com.service.translationservice.영어번역기 만 있으면 되었지만 스페인어가 추가되고 com.service.translationservice1.스페인어번역기 이렇게 두 가지가 생겨버렸다. 심지어 폴더도 translationservice 에서 translationservice1 으로 1이 붙어버린 폴더가 하나 더 생겨버렸다. 그럼 내가 META-INF\services\ 요기를 찾아서 가서 하나하나 다 수정해줘야 한다. 그럼 실수가 발생하지 않을까? 무조건 발생한다. 이런 걸 막기 위하여 우리의 구글신께선 @AutoService라는 걸 친히 만들어 하사해주신거다. 이걸 통하여 얻을 수 있는 이점은 잘못된 구성파일을 추가하거나, 편집하거나, 노력하는 걸 막아준다. 더 자세한 내용은 아래를 참고하자.

Annotation Processor 활용

직접 만드신 분이 있더라 대단하다…… 난 시간이 없어서 아직 못한것(핑계).. 도 있지만 해볼 생각이다. 전에 토비의 스프링을 공부하며 POI라이브러리를 좀 더 쉽게? 사용할 방법이 없을까? 고민한적이 있었다. 그 덕분에 탄생한 모듈? 아닌 모듈이 있었는데, 그걸 한번 고쳐볼 생각이다. 템플릿 어떻게 만들어? 209p~277p 고쳐봅시다!

Annotation Processor VS Reflection

궁금해서 찾아본 내용이다. 뭐가 좋을까? 라는 이런 터무니없는 생각이다. 근데 충분히 가치가 있었던 내용인 것 같다. 이유는 Reflection의 경우 런타임에 실행되고, Annotation Processing의 경우 컴파일 타임에 코드를 만든다. 그런 면에서는 아무래도 Annotation Processing이 좀 더 끌린다. 런타임 중에 일어나는 일은 사실 내가 예측하기 힘든 범위라서 더 그런 것 같다. 어쨋든 실행이 되어야 검사가 되는 반면 검사로직이 만들어진 것은 다르니깐. 가시성면에서 좀 차이가 나는 것 같다.