1. @Annotation : 메타데이터
자바 소스코드를 읽거나 쓰면 "@" 표시가 붙은 단어와 떨어질 수 없다. @Override, @Bean, @Getter, @Component ...
자바 어노테이션이란, 자바 소스코드의 메타데이터이다. 메타데이터란 데이터 자체에 대한 설명으로, 자바 어노테이션의 경우 자바 소스코드 대한 설명을 담고있다고 이해할 수 있다. 프로그램의 주요 비즈니스 코드와는 큰 관련이 없지만, 코드 자체에 대한 설명이나 횡단 관심사의 기능을 추가하고 싶을때 어노테이션을 이용한다.
2. Annotation 사용하는 이유
1) 코드 가독성 향상
어노테이션은 코드 정보 자체를 담고있다는 특징 때문에 코드의 가독성을 높여준다.
상위 인터페이스나 클래스를 상속 / 구현할 때, 오버라이드 하는 메소드에 @Override 어노테이션을 붙인다. 그를 통해 이 메소드가 상위 메소드를 재정의하고 있음을 표현하여 코드를 더 잘 이해할 수 있고, 재정의에 오류가 있을 때 IDE 상에서나 컴파일 타임에 검출 가능하다.
추가하고 싶은 메타데이터가 있다면, 그 정보를 담을 수 있는 어노테이션을 새로 선언해 사용할 수도 있다.
2) 생산성 향상
프로그래밍의 효율성은, 주요 비즈니스 로직에 집중하여 원하는 기능을 빨리 구현했을 때 올라간다. 주요 관심사에서 벗어난 부가 기능들을 하나하나 만들다보면 생산성이 떨어지기 쉽다. 이런 위험을 피하기 위해 비슷한 역할을 하는 코드, 주요 관심사에서 벗어난 코드, 구조의 기본이 되는 코드 등을 어노테이션을 이용해 축약할 수 있다.
많이들 사용하는 Lombok이 그 예시이다. Lombok 라이브러리엔 @Getter, @Setter, @AllArgsConstructor, @toString 등이 존재하는데 이것들은 모두 클래스 구성의 기본이 되는 boilerplate 코드를 작성해줌으로써 개발자의 생산성 향상에 기여한다.
@NonNull을 통해 타입에 대한 null checking이 간단하게 진행되므로 이또한 그 예시라고 할 수 있다.
3) 좋은 성능
위와 같은 장점을 가진 어노테이션을 사용한다고 해서 딱히 프로그램의 성능에 영향을 미치는 것도 아니다. 뒤에 어노테이션의 동작 과정에서 설명하겠지만, 어노테이션은 컴파일 타임에 처리된다. 런타임엔 어노테이션 프로세서에 의해 바이트코드에 추가된 코드를 실행할 뿐이다. 런타임에 추가적인 작업이 필요하지 않으므로, 어노테이션을 사용하는 것이 프로그램의 성능을 떨어뜨리는 것은 아니다.
3. Annotation 동작 과정
어노테이션은 컴파일 타임에 어노테이션 프로세서에 의해 처리된다. javac의 컴파일 타임에, 등록된 어노테이션 프로세서가 소스코드에 존재하는 어노테이션을 스캔해 코드를 추가하는 방식으로 동작한다.
간단한 그림을 보며 추가로 이해해보자.

- 어노테이션 프로세서는 컴파일러에게 java 소스코드 혹은 byte code를 받아 프로세싱 라운드를 시작한다.
- 어노테이션 프로세서는 소스코드에 존재하는 어노테이션을 스캔하고 프로세서의 로직에 맞는 코드를 추가하여 결과물(주로 .java)로 내놓는다.
- 다음 어노테이션 프로세서가 동작한다. 다음 프로세서는 이전 프로세서가 결과물로 내놓은 코드를 받아 프로세싱 라운드를 시작한다.
- 같은 과정은 소스코드의 모든 어노테이션이 처리될때까지 연쇄적으로 일어난다.
각 어노테이션에 맞는 어노테이션 프로세서가 순서대로 동작한다는 것이 핵심이다. 어떤 프로세서가 먼저 동작하는지에 따라 코드가 추가되는 순서도 달라지는데, 이때문에 문제가 발생하는 경우도 종종 있는듯하다(롬복을 이용해 생성자를 만들었는데, 생성자가 있어야 동작 가능한 프로세서가 롬복보다 먼저 프로세싱을 시작해버리든가 하는 상황 등)
4. Annotation 종류
1) Built-in 어노테이션
다른 외부 라이브러리를 가져올 필요 없이, Java 자체에 존재하며 사용가능한 어노테이션이다.
- @Override : 상위 클래스를 상속받거나, 인터페이스를 구현했을 때 재정의한 메소드에 붙이는 어노테이션.
- @Deprecated : 다음 버전에서 더이상 지원하지 않을 예정이거나, 더이상 사용을 장려하지 않는 요소에 붙이는 어노테이션.
- @FuntionalInterface : 람다식으로 사용가능한 인터페이스에 붙이는 어노테이션. 보통 한 가지의 메소드를 가지고 있음.
- @SuppressWarning : 컴파일러가 내뱉는 경고 문구를 무시하기 위해 사용하는 어노테이션. "unchecked", "deprecated"를 value 요소로 설정할 수 있다.
2) 메타 어노테이션
어노테이션을 위한 어노테이션이다. 필요에 따라 개발자가 직접 어노테이션을 작성해야하는 경우가 있는데, 이때 어노테이션을 정의하기 위해 사용한다.(메타 데이터를 위한 메타 데이터를 위한 메타데이터)
- @Target : 해당 어노테이션이 붙을 수 있는 범위를 지정한다. ElementType 값을 설정으로 받는다.
- CONSTRUCTOR : 생성자 method
- FIELD : 클래스 멤버변수, enum 상수
- LOCAL_VARIABLE : 지역 변수
- METHOD : 메소드
- PACKAGE : 패키지
- PARAMETER : 매개변수
- @Retention : 해당 어노테이션과 그 기능이 유지되는 기간을 지정한다. RetentionPolicy 값을 설정으로 받는다.
- SOURCE : 소스 파일에서만 사용하고, 클래스파일엔 존재하지 않는다. 거의 주석과 같은 기능
- CLASS : 클래스 파일에만 존재하고, 런타임엔 사용하지 않는다. default
- RUNTIME : 클래스 파일에도 존재하고, 런타임동안 JVM이 사용할 수 있다.
- @Documented : 작성한 어노테이션에 대한 정보가 javadoc에 들어가길 원할 때 사용한다.
- @Inherited : 해당 어노테이션이 붙은 클래스의 자식 클래스에도 같은 어노테이션을 붙일 때 사용한다.
3) custom 어노테이션
필요에 의해 개발자가 직접 정의해서 사용하는 어노테이션이다. 다음과 같이 어노테이션의 이름 앞에 "@interface"를 붙여 이것이 개발자가 직접 정의한 custom annotation임을 알린다.
public @interface MyAnno {
}
어노테이션을 정의할 때 알아야할 기본적인 특징을 몇 가지 살펴보겠다.
- 어노테이션은 내부적으로 Java.lang.annotation.Annotation 클래스를 확장한다. 따라서 다른 클래스를 추가로 extend할 수 없다.
- 어노테이션 안에 0개 이상의 element를 가질 수 있다.
element는 어노테이션의 내부에 가지고있는 상태값이다. element 개수가 각각 0개, 1개, 2개인 어노테이션은 아래와 같이 정의된다.
public @interface NoElement {
}
public @interface OneElement {
public int element();
}
public @interface TwoElements {
public String name();
public int value();
}
이렇게 정의한 custom annotation은 자바 코드에서 자유롭게 사용할 수 있다.
@OneElement(element = 3)
public class Example {
public static void main(String[] args) {
@TwoElements(name = "제 어노테이션입니다",value = 3)
String hi;
}
}
REFERENCE
https://velog.io/@eia51/Annotation-%EC%99%84%EC%A0%84-%EC%A0%95%EB%B3%B5%EA%B8%B0
https://docs.oracle.com/javase/tutorial/java/annotations/index.html
1. @Annotation : 메타데이터
자바 소스코드를 읽거나 쓰면 "@" 표시가 붙은 단어와 떨어질 수 없다. @Override, @Bean, @Getter, @Component ...
자바 어노테이션이란, 자바 소스코드의 메타데이터이다. 메타데이터란 데이터 자체에 대한 설명으로, 자바 어노테이션의 경우 자바 소스코드 대한 설명을 담고있다고 이해할 수 있다. 프로그램의 주요 비즈니스 코드와는 큰 관련이 없지만, 코드 자체에 대한 설명이나 횡단 관심사의 기능을 추가하고 싶을때 어노테이션을 이용한다.
2. Annotation 사용하는 이유
1) 코드 가독성 향상
어노테이션은 코드 정보 자체를 담고있다는 특징 때문에 코드의 가독성을 높여준다.
상위 인터페이스나 클래스를 상속 / 구현할 때, 오버라이드 하는 메소드에 @Override 어노테이션을 붙인다. 그를 통해 이 메소드가 상위 메소드를 재정의하고 있음을 표현하여 코드를 더 잘 이해할 수 있고, 재정의에 오류가 있을 때 IDE 상에서나 컴파일 타임에 검출 가능하다.
추가하고 싶은 메타데이터가 있다면, 그 정보를 담을 수 있는 어노테이션을 새로 선언해 사용할 수도 있다.
2) 생산성 향상
프로그래밍의 효율성은, 주요 비즈니스 로직에 집중하여 원하는 기능을 빨리 구현했을 때 올라간다. 주요 관심사에서 벗어난 부가 기능들을 하나하나 만들다보면 생산성이 떨어지기 쉽다. 이런 위험을 피하기 위해 비슷한 역할을 하는 코드, 주요 관심사에서 벗어난 코드, 구조의 기본이 되는 코드 등을 어노테이션을 이용해 축약할 수 있다.
많이들 사용하는 Lombok이 그 예시이다. Lombok 라이브러리엔 @Getter, @Setter, @AllArgsConstructor, @toString 등이 존재하는데 이것들은 모두 클래스 구성의 기본이 되는 boilerplate 코드를 작성해줌으로써 개발자의 생산성 향상에 기여한다.
@NonNull을 통해 타입에 대한 null checking이 간단하게 진행되므로 이또한 그 예시라고 할 수 있다.
3) 좋은 성능
위와 같은 장점을 가진 어노테이션을 사용한다고 해서 딱히 프로그램의 성능에 영향을 미치는 것도 아니다. 뒤에 어노테이션의 동작 과정에서 설명하겠지만, 어노테이션은 컴파일 타임에 처리된다. 런타임엔 어노테이션 프로세서에 의해 바이트코드에 추가된 코드를 실행할 뿐이다. 런타임에 추가적인 작업이 필요하지 않으므로, 어노테이션을 사용하는 것이 프로그램의 성능을 떨어뜨리는 것은 아니다.
3. Annotation 동작 과정
어노테이션은 컴파일 타임에 어노테이션 프로세서에 의해 처리된다. javac의 컴파일 타임에, 등록된 어노테이션 프로세서가 소스코드에 존재하는 어노테이션을 스캔해 코드를 추가하는 방식으로 동작한다.
간단한 그림을 보며 추가로 이해해보자.

- 어노테이션 프로세서는 컴파일러에게 java 소스코드 혹은 byte code를 받아 프로세싱 라운드를 시작한다.
- 어노테이션 프로세서는 소스코드에 존재하는 어노테이션을 스캔하고 프로세서의 로직에 맞는 코드를 추가하여 결과물(주로 .java)로 내놓는다.
- 다음 어노테이션 프로세서가 동작한다. 다음 프로세서는 이전 프로세서가 결과물로 내놓은 코드를 받아 프로세싱 라운드를 시작한다.
- 같은 과정은 소스코드의 모든 어노테이션이 처리될때까지 연쇄적으로 일어난다.
각 어노테이션에 맞는 어노테이션 프로세서가 순서대로 동작한다는 것이 핵심이다. 어떤 프로세서가 먼저 동작하는지에 따라 코드가 추가되는 순서도 달라지는데, 이때문에 문제가 발생하는 경우도 종종 있는듯하다(롬복을 이용해 생성자를 만들었는데, 생성자가 있어야 동작 가능한 프로세서가 롬복보다 먼저 프로세싱을 시작해버리든가 하는 상황 등)
4. Annotation 종류
1) Built-in 어노테이션
다른 외부 라이브러리를 가져올 필요 없이, Java 자체에 존재하며 사용가능한 어노테이션이다.
- @Override : 상위 클래스를 상속받거나, 인터페이스를 구현했을 때 재정의한 메소드에 붙이는 어노테이션.
- @Deprecated : 다음 버전에서 더이상 지원하지 않을 예정이거나, 더이상 사용을 장려하지 않는 요소에 붙이는 어노테이션.
- @FuntionalInterface : 람다식으로 사용가능한 인터페이스에 붙이는 어노테이션. 보통 한 가지의 메소드를 가지고 있음.
- @SuppressWarning : 컴파일러가 내뱉는 경고 문구를 무시하기 위해 사용하는 어노테이션. "unchecked", "deprecated"를 value 요소로 설정할 수 있다.
2) 메타 어노테이션
어노테이션을 위한 어노테이션이다. 필요에 따라 개발자가 직접 어노테이션을 작성해야하는 경우가 있는데, 이때 어노테이션을 정의하기 위해 사용한다.(메타 데이터를 위한 메타 데이터를 위한 메타데이터)
- @Target : 해당 어노테이션이 붙을 수 있는 범위를 지정한다. ElementType 값을 설정으로 받는다.
- CONSTRUCTOR : 생성자 method
- FIELD : 클래스 멤버변수, enum 상수
- LOCAL_VARIABLE : 지역 변수
- METHOD : 메소드
- PACKAGE : 패키지
- PARAMETER : 매개변수
- @Retention : 해당 어노테이션과 그 기능이 유지되는 기간을 지정한다. RetentionPolicy 값을 설정으로 받는다.
- SOURCE : 소스 파일에서만 사용하고, 클래스파일엔 존재하지 않는다. 거의 주석과 같은 기능
- CLASS : 클래스 파일에만 존재하고, 런타임엔 사용하지 않는다. default
- RUNTIME : 클래스 파일에도 존재하고, 런타임동안 JVM이 사용할 수 있다.
- @Documented : 작성한 어노테이션에 대한 정보가 javadoc에 들어가길 원할 때 사용한다.
- @Inherited : 해당 어노테이션이 붙은 클래스의 자식 클래스에도 같은 어노테이션을 붙일 때 사용한다.
3) custom 어노테이션
필요에 의해 개발자가 직접 정의해서 사용하는 어노테이션이다. 다음과 같이 어노테이션의 이름 앞에 "@interface"를 붙여 이것이 개발자가 직접 정의한 custom annotation임을 알린다.
public @interface MyAnno {
}
어노테이션을 정의할 때 알아야할 기본적인 특징을 몇 가지 살펴보겠다.
- 어노테이션은 내부적으로 Java.lang.annotation.Annotation 클래스를 확장한다. 따라서 다른 클래스를 추가로 extend할 수 없다.
- 어노테이션 안에 0개 이상의 element를 가질 수 있다.
element는 어노테이션의 내부에 가지고있는 상태값이다. element 개수가 각각 0개, 1개, 2개인 어노테이션은 아래와 같이 정의된다.
public @interface NoElement {
}
public @interface OneElement {
public int element();
}
public @interface TwoElements {
public String name();
public int value();
}
이렇게 정의한 custom annotation은 자바 코드에서 자유롭게 사용할 수 있다.
@OneElement(element = 3)
public class Example {
public static void main(String[] args) {
@TwoElements(name = "제 어노테이션입니다",value = 3)
String hi;
}
}
REFERENCE
https://velog.io/@eia51/Annotation-%EC%99%84%EC%A0%84-%EC%A0%95%EB%B3%B5%EA%B8%B0
https://docs.oracle.com/javase/tutorial/java/annotations/index.html