Java나 Spring을 다룰 때, 아래의 코드들과 같이 @(;AT) 로 시작하는 무언가를 본 적이 있을 것이다.
// java
static class myObject {
int value;
String name;
// ...
@Override
protected Object clone() throws CloneNotSupportedException {
// TODO Auto-generated method stub
return super.clone();
}
@Override
public boolean equals(Object obj) {
// TODO Auto-generated method stub
return super.equals(obj);
}
}
// Spring Boot
@Controller
public class HelloController {
@GetMapping("hello")
public String hello(Model model) {
model.addAttribute("data", "spring!!");
return "hello";
}
}
이를 Java의 Annotation 이라고 한다.
최근 Java와 Spring Boot를 공부하면서 이를 많이 보았지만 프로그램에서 어떤 식으로 동작하는지 궁금증이 생겼다. 그래서 이에 대해 좀 더 자세히 알아보고자 한다.
Annotation은 JAVA 5부터 추가된 기능이다. annotation은 '주석'이라는 뜻을 가진 단어이다.
그처럼 프로그래밍 언어 자체에는 영향을 미치지 않지만
class나 interface, method, field 등 소스 코드에 추가적인 정보(메타데이터)를 제공하기 위해 사용된다. (이는, Javadoc과 유사한 개념이라 할 수 있다.)
이러한 annotation을 통해 소스 코드의 가독성을 높이고, 유지보수를 용이하게 하며, 개발 과정을 효율화할 수 있다.
이전에 사용된 파일 관리 방법에 대해 알아보자.
annotation이 없을 때, 개발자는 코드에 특정 메타데이터나 설정 정보를 전달하기 위해 xml 파일과 같은 외부 설정 파일을 사용하여 관리하였다.

하지만 위의 방식이라면 코드와 설정 파일의 일관성을 유지하기 어렵다.
설정 파일이 아닌 주석을 통해 정보를 제공하여도, 컴파일러나 런타임 시스템이 인식을 할 수 없는 문제가 발생하여 이 또한 좋은 방법은 아니었다.
설정 정보와 코드의 일관성을 유지하는 방법 등을 쉽게 할 수 있는 방법으로 나온 것이 Annotation(@)이라 볼 수 있다.
Oracle에서 설명하는 용도는 다음과 같다.

Java에서 기본적으로 제공하는 Annotation이다.
주로 컴파일러에게 특별한 지시를 내리거나 경고를 표시하는 데 사용된다.
아래는 Built-in Annotation의 예이다.
@Override
@Deprecated
@SafeVarArgs
public class Example {
@SafeVarargs
public final <T> void printItems(T... items) {
for (T item : items) {
System.out.println(item);
}
}
public static void main(String[] args) {
SafeVarargsExample example = new SafeVarargsExample();
example.printItems("Hello", "World");
}
}
@SuppressWarnings
@SuppressWarnings("unchecked")@FunctionalInterface
@FunctionalInterface
public interface MyFunctionalInterface {
void myMethod();
// default 또는 static 메서드는 여러 개 정의할 수 있음
default void anotherMethod() {
System.out.println("Another method");
}
}
public class FunctionalInterfaceExample {
public static void main(String[] args) {
MyFunctionalInterface func = () -> System.out.println("Lambda implementation");
func.myMethod();
}
}
Annotation을 정의할 때 사용하는 annotation이다.
동작 방식을 설정하거나 제어하는 데 사용되며 Java에는 여러 meta annotation이 존재한다.
@Retention@Override 이 SOURCE 레벨로 유지되는 annotation의 예시이다.@Deprecated와 @SuppressWarnings가 그 예시이다.Java의 리플렉션(Reflection)
프로그램이 실행 중에 클래스, 인터페이스, 메소드, 필드 등을 동적으로 분석하고 조작할 수 있는 기능을 의미한다. 런타임에 객체의 정보를 알 수 있고 메소드를 호출하거나 필드에 접근할 수 있다.
이는 클래스와 구성 요소에 대한 메타데이터가 런타임 동안 유지되며 이는 자바의 클래스 파일에 포함되어 있다. 그리고 JVM이 이를 읽고 관리한다.
리플렉션 API는 이 정보를 활용하여 런타임에 클래스의 구조를 탐색하고 조작할 수 있게 해준다.
Annotation의 작동 원리가 이 리플렉션에 있다고 할 수 있다.
@Target
annotation이 적용될 수 있는 요소(대상)을 지정하는 데 사용된다.
이를 통해 annotation이 적용될 위치를 제한할 수 있다.
| ElementType 유형 | 설명 |
|---|---|
| ANNOTATION_TYPE | 다른 annotation에 적용 가능 |
| CONSTRUCTOR | 생성자에 적용 가능 |
| FIELD | 필드(멤버 변수)에 적용 가능 |
| LOCAL_VARIABLE | 로컬 변수에 적용 가능 |
| METHOD | 메소드에 적용 가능 |
| PACKAGE | 패키지에 적용 가능 |
| PARAMETER | 메소드 매개변수에 적용 가능 |
| TYPE | 클래스, 인터페이스(annotation 포함), 열거형에 적용 가능 |
@Inherited
annotation이 자식 클래스에 상속될 수 있음을 나타낸다.
기본적으로 annotation은 상속되지 않지만, @Inherited를 사용하면 부모 클래스에 정의된 annotation이 자식 클래스에서 상속된다.
@Documented
해당 annotation이 Javadoc과 같은 도구로 생성된 문서에 포함되어야 함을 나타낸다.
즉, annotation이 API 문서에 노출되어야 할 때 사용한다.
@Repeatable
같은 annotation을 한 요소에 여러 번 적용할 수 있게 해준다.
이를 사용하려면, annotation 타입을 중복 적용할 수 있도록 annotation 컨테이너를 정의해야 한다.
import java.lang.annotation.Repeatable;
@Repeatable(MyAnnotations.class)
public @interface MyAnnotation {
String value();
}
public @interface MyAnnotations {
MyAnnotation[] value();
}
// 사용 예시
@MyAnnotation("First")
@MyAnnotation("Second")
public class MyClass {
}
Java에서 개발자가 특정한 목적을 위해 직접 정의한 Annotation
만드는 방법은 아래에 후술
먼저 어떤 annotation이든 가질 수 있는 타입은 3(+2)가지이다.
Marker Annotations
아무런 element를 가지지 않고 단순히 표시하기 위한 annotation이다.
대표적인 예시로 @Override가 있다.
Single value Annotations
하나의 element만 가지고 있으며, 값을 명시하여 데이터를 전달하기 위해 사용된다.
@TestAnnotation("Testing")
Full Annotations
둘 이상의 element를 갖는 annotation. 데이터를 key-value 형태로 전달하게 된다.
@TestAnnotation(owner="Peter", value="Class myObject")
이후 아래의 2가지는 Java 8 부터 제공하는 annotation 타입이다.
이전 버전의 Java에서는 선언에만 Annotation을 쓸 수 있었다.
@NotNull String str1 = "hello";
MyObject object = new @Interned MyObject();
@Schedule(dayOfMonth="last")
@Schedule(dayOfWeek="Fri", hour="23")
public void doPeriodicCleanUp() {
// ...
}
@interface 키워드를 사용하여 인터페이스처럼 정의된다.public @interface CustomAnnotation {
String value();
int number() default 0; // 기본값 지정 가능
}
위와 같이 value, number라는 두 개의 요소를 가진 'CustomAnnotation'이라는 Annotation을 정의했다.
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface CustomAnnotation {
String value();
int number() default 0;
}
public class MyClass {
@CustomAnnotation(value = "Example usage", number = 5)
public void myMethod() {
// Method implementation
}
}
import java.lang.reflect.Method;
public class AnnotationProcessor {
public static void main(String[] args) {
try {
// Method method = MyClass.class.getMethod("myMethod");
Method method = MyClass.getClass().getDeclaredMethod("myMethod");
if (method.isAnnotationPresent(CustomAnnotation.class)) {
CustomAnnotation annotation = method.getAnnotation(CustomAnnotation.class);
System.out.println("Value: " + annotation.value());
System.out.println("Number: " + annotation.number());
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
위의 정보들을 활용하여 Spring Boot에서 많이 사용되는 Annotation 중 하나인 @RestController를 살펴보았다.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
/**
* The value may indicate a suggestion for a logical component name,
* to be turned into a Spring bean in case of an autodetected component.
* @return the suggested component name, if any (or empty String otherwise)
* @since 4.0.1
*/
@AliasFor(annotation = Controller.class)
String value() default "";
}
Spring Boot의 @RestController는 Restful 서비스를 개발할 때 사용하는 Annotation이다.
이 Annotation을 보면, @Retention(RetentionPolicy.RUNTIME) 으로 설정되어 있어
런타임까지 이 Annotation이 유지되어, 리플렉션을 통해 annotation의 정보를 읽을 수 있게 설정되어 있다.
@Target(ElementType.TYPE) 으로 클래스, 인터페이스, 열거형에만 사용할 수 있게 설정되었다.

@Documented Annotation이 있어 Javadoc과 같은 도구를 이용하여 해당 API에 대한 설명이 있어야 함을 명시하였고,
실제 구현되어 있는 곳에서도 Javadoc을 통해 API 문서에 노출되어 있다.
그리고 @RestController 에는 @Controller와 @ResponseBody가 같이 명시되어 있어,
@RestController는 이 둘의 기능을 모두 제공함을 알 수 있다.
@Controller
요청을 처리하고 모델 데이터를 View에 전달하는 역할.
@ResponseBody
메소드의 반환 값을 View 이름으로 사용하지 않고, HTTP 응답 본문에 직접 쓰도록 지정한다.
즉, 메소드의 반환된 객체는 자동으로 JSON이나 XML 형식으로 변환되어 HTTP response으로 클라이언트에게 전달된다.
위와 같이 Spring Boot에서 기본적으로 제공하는 Annotation 등이 어떤 역할을 하는지 파악할 수 있다.