어노테이션은 사전적의미로 주석이라는 뜻을 가지고 있습니다. 하지만 Java에서 Annotation은 어떻게 구현되냐에 따라 소스코드에 영향을 줍니다. 즉, 비즈니스 로직에는 영향을 주지 않지만 해당 타겟의 연결 방법이나 소스코드의 구조를 변경할 수 있습니다. 쉽게 말하면 특정 속성을 어떤 용도로 사용하도록 지시하거나 클래스에 역할을 부여할 수 있는 기능입니다. 어노테이션은 소스코드에 메타데이터를 삽입하는 것이기에 잘 활용하면 가독성이 올라가고 체계적인 소스코드를 구성하는데 도움이 됩니다.
Annotaion은 Java 5 버전부터 등장한 기능입니다. 아래 코드처럼 사용 가능합니다.
@AnnotationExample
public class example {
// ...
}
어노테이션은 Java 자체에서도 제공하지만 특히, Spring Framework에서 많이 사용됩니다. SpringBoot를 사용하면 다양한 어노테이션을 통해 빠르게 코드를 작성하기도 합니다. 어노테이션의 가장 큰 장점은 개발자가 부가적인 사항을 어노테이션으로 선언하여 처리하도록 하고, 본인은 비즈니스 로직 구현에 집중할 수 있도록 도와줍니다. 또한 AOP(AspectOriented Programing)을 편리하게 구현하도록 돕습니다.
어노테이션은 컴파일 시기와 런타임 시기를 전부 처리할 수 있습니다. 자바는 리플렉션 기능이 있는데 이를 통해 자바 클래스 정보를 볼 수 있습니다. 이 리플렉션 기능으로 어노테이션을 효율적으로 활용이 가능합니다. 어노테이션을 메타데이터로 이야기하는 경우가 많습니다. 속성, 클래스 등 다양한 곳에 붙여 쓸 수 있기 때문인데요. 아래와 같이 사용하면, 해당 클래스를 나타내는 정보가 될 수 있으며, 어노테이션 선언만으로도 특정 기능이 실행 되도록 할 수 있습니다.
@Service
public Class TestService {
...
}
위 코드는 SpringBoot에서 Service를 선언하는 어노테이션 입니다. 메타데이터, 태그 느낌이 나시나요? SpringBoot는 해당 어노테이션이 붙은 Class를 Bean에 Service로 등록을 하게 됩니다. 이렇게 어노테이션이 붙어있는 클래스나 속성을 찾아 원하는 로직을 실행하게 됩니다. 우리가 흔히 사용하는 어노테이션 중 하나가 @Override가 있습니다. 이 어노테이션이 붙은 메소드는 상속한 클래스의 함수를 재정의 했다는 의미로 사용됩니다.
@Override
public void test() {
...
}
이렇게 목적에 따라 어노테이션은 활용이 가능합니다.
표준 Annotation
표준 Annotation은 3개를 많이 사용합니다.
- @Override
- @Deprecated
- @SuppressWarnings
@Override
많이 사용하는 Annotation입니다. Override는 메서드를 재정의 할 때 이름을 잘못적는 것을 방지해줍니다.
class Test {
void parentMethod(){}
}
class TestImpl extends Test {
@Override
void parentMethod(){}
}
@Deprecated
앞으로 사용하지 않는 필드나 메서드에 붙여줍니다.
@Deprecated
public int deprecat() {
return null;
}
이런 기능을 사용하는 가장 큰 이유는 하위 호환성 때문입니다. 이전에 개발한 것 중 최신 기능에는 사라졌지만, 이전 버전에 남아있는 경우에 많이 활용하게 된다.
@FunctionalInterface
Java8부터 지원하는 함수형 인터페이스를 지정하는 Annotation 입니다. 이 Annotation은 함수형 인터페이스에 “하나의 추상메서드만 가져야 한다는 제약”을 확인해줍니다. 그리고 함수형 인터페이스라는 것을 알려주는 역할도 합니다.
@FunctionalInterface
interface CustomInterface<T> {
T myCall();
}
CustomInterface<String> customInterface = () -> "Hello Custom";
위와 같이 사용합니다. 함수형 인터페이스라서 람다식으로 표현할 수 있습니다.
@SuppressWarnings
컴파일러의 경고 메세지가 나타나지 않게 합니다. 경고 메시지가 나올 걸 알고 있지만 무시해야 하는 경우 활용합니다.
@SuppressWarnings({"rawtypes","unchecked"})
public class VersionBee implements Serializable {
...
}
위 코드와 같이 사용하며, 확인된 경고는 제거하고 새로운 경고와 섞이지 않도록 할 때 사용합니다. 흔히 보이는 경고를 제거 할 때 매우 좋습니다.
커스텀 어노테이션 만들기
우리가 어노테이션을 깊게 알아야 하는 가장 큰 이유는 커스텀 어노테이션을 만들기 위함입니다. 커스텀으로 어노테이션을 만들면 중복으로 사용할 기능을 간단한 어노테이션 선언으로 쉽게 사용할 수 있습니다.
@Target({ElementType.TYPE})
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomAnnotation {
String value() default "CustomAnnotation";
}
커스텀 어노테이션을 만들 땐 메타 어노테이션을 활용하게 됩니다.
@Target
어노테이션을 정의할 때, 적용 대상을 지정하는데 사용합니다.
TYPE, FIELD, TYPE_USE, LOCAL_VARIABLE, METHOD 등등 다양한 종류가 존재합니다.
@Retention
자바 컴파일러가 어노테이션을 다루는 방법을 설명하며, 특정 시점까지 영향을 미치는지를 결정합니다.
SOURCE, CLASS, RUNTIME과 같은 종류가 있습니다.
@Documented
JavaDoc 생성 시 Document에 포함되도록 합니다.
@Inherited
해당 어노테이션을 하위 클래스에 적용합니다.
사용할 수 있는 좋은 예시는 아래 코드와 같습니다.
@Api
@RestController
@Retention(RetentionPolicy.RunTime)
@Inherited
@Documented
@Target(ElementType.TYPE)
public @interface RestContollerWithSwagger {
String name() default "RestController";
String value();
}
@RestControllerWithSwagger(value = "RestController", name = "RestController")
@RequestMapping("/admin")
public class RestAdminController {
...
}
위 코드는 @Api와 @RestController 어노테이션을 합친 커스텀 어노테이션 입니다. 이렇게 자주 사용하는 Annotation 끼리 합치면서 중복 코드를 줄일 수 도 있습니다.
마무리
Custom Annotation과 같은 것은 우리가 실무에서 많이 활용할 수 있는 기능입니다. 그렇기에 어떻게 만드는지 어떤 상황에서 사용가능한지 알고 있으면 좋겠네요. 알아두면 활용할 곳은 많아보이네요!