SY 개발일지
article thumbnail

Java 언어로 프로젝트를 하며 다음과 같이 어노테이션을 이용하여 어떠한 상황을 명세하는 경우가 많습니다.

 

특히 스프링을 이용해 프로젝트를 한다면 어노테이션을 통해 Controller나 Service등을 명시하여 빈으로 등록하여 사용합니다. 그렇다면 이러한 어노테이션은 무엇이고, 어떻게 작동하는 걸까요 ?

 

어노테이션이란 ?

자바에서 어노테이션은 사전적 의미로 주석이라는 뜻을 가지고 있습니다. 자바의 어노테이션은 소스코드에 추가하여 사용할 수 있는 일종의 메타데이터입니다. 여기서 메타데이터란, 애플리케이션이 처리해야할 데이터가 아니라 컴파일 과정과 실행 과정에서 코드를 어떻게 처리해야하는지를 알려주기 위한 추가 정보입니다.

 

JDK 1.5 버전 이상, 자바 8버전부터 사용 가능하며, 자바 어노테이션은 클래스 파일에 임베드 되어 컴파일러에 의해 생성된 이후 JVM에 포함되어 동작합니다.

 

기능

자바 어노테이션은 다음과 같은 기능을 합니다.

  • 컴파일러에게 코드 작성 문법 에러를 체크하도록 정보 제공
  • 소프트웨어 개발 환경이 빌드나 배포시 코드를 자동으로 생성할 수 있도록 정보 제공
  • 런타임에 특정 기능을 실행하도록 정보 제공

 

자바 자체에서 기본으로 제공하는 어노테이션들도 많이 있습니다. 또한 필요에 따라 사용자가 직접 커스텀 어노테이션들을 만들어서 사용할 수도 있습니다.

 

형식

어노테이션은 기본적으로 골뱅이'@' 기호를 이용하여 컴파일러에게 어노테이션임을 알립니다. 

@Override
void mySuperMethod() {}

 

어노테이션은 클래스나 메소드에 붙여 사용할 수 있습니다.

@Author(
  name="Benjamin Franklin",
  date="3/27/2003"
)
class MyClass {}
@SuppressWarnings(value="unchecked")
void myMethod() {}

 

어노테이션은 element를 가질 수도 있는데, 이러한 어노테이션은 딱 하나의 element만 있다면 이름을 생략할 수도 있습니다.

@SuppressWarnings("unchecked")
void myMethod() {}

 

또한 아무런 element가 없다면 그냥 @Override 처럼 괄호도 생략 가능합니다.

 

어노테이션은 여러 개가 붙을 수도 있습니다.

@Author(name="Jane Doe")
@EBook
class MyClass {}

 

이러한 어노테이션은 같은 어노테이션을 중첩하여 사용할 수도 있는데, 이러한 어노테이션을 repeating annotation 이라고 합니다.

@Author(name="Jane Doe")
@Author(name="John Smith")
class MyClass {}

Repeating annotation은 자바 8버전 이후부터 사용 가능합니다.

 

예시

어노테이션은 클래스, 필드, 메소드 그리고 다른 프로그램 element들에 대해 선언할 수 있으며, 다음과 같은 상황에서 사용합니다.

  • 클래스 인스턴스 생성 표현식
new @Interned MyObject();
  • 타입 캐스트
myString = (@NonNull String) str;
  • implements 절
class UnmodifiableList<T> implements @Readonly List<@Readonly T> { }
  • 예외 절
void monitorTemperature() throws @Critical TemperatureException { }

 

이러한 형식의 어노테이션을 type annotation 이라고 합니다.

 

어노테이션 종류

자바 빌트인 어노테이션

@Deprecated

더이상 사용하지 않는다는 의미로 사용합니다. 컴파일러는 @Deprecated 어노테이션이 붙은 메소드, 클래스, 필드에서 항상 에러를 발생시킵니다.

class Example {
   // Javadoc comment follows
    /**
     * @deprecated
     * explanation of why it was deprecated
     */
    @Deprecated
    static void deprecatedMethod() { }
}

 

@Override

메소드를 오버라이드하겠다는 의미로 메소드의 선언 앞에 붙여줍니다. 만약 @Override 어노테이션을 붙인 상태에서, 상위 클래스의 메소드를 올바르게 오버라이드하지 않았을 때 컴파일러는 에러를 발생시킵니다.

// mark method as a superclass method
// that has been overridden
@Override 
int overriddenMethod() { }

 

@SuppressWarnings

컴파일러 경고를 출력하지 않도록 설정합니다. @SuppressWarnings 어노테이션은 인자를 받는데, 인자에 따른 의미는 다음과 같습니다.

  • @SuppressWarnings("all"): 모든 경고를 억제
  • @SuppressWarnings("cast"): 타입 캐스트 관련 경고 억제
  • @SuppressWarnings("dep-ann"): 사용하지 말아야할 주석 관련 경고 억제
  • @SuppressWarnings("deprecation"): Deprecated 메소드를 사용한 경우 발생하는 경고 억제
  • @SuppressWarnings("fallthrough"): switch 문에서 break 구문 누락 관련 경고 억제
  • @SuppressWarnings("finally"): finally 블럭 관련 경고 억제
  • @SuppressWarnings("null"): null 관련 경고 억제
  • @SuppressWarnings("rawtypes"): 제네릭을 사용하는 클래스 매개 변수가 특정되지 않았을 때의 경고 억제
  • @SuppressWarnings("unchecked"): 검증되지 않은 연산자 관련 경고 억제
  • @SuppressWarnings("unused"): 사용하지 않는 코드 관련 경고 억제

컴파일러 경고는 경고일 뿐, 경고 상황을 개발자가 알고 있는 경우에는 컴파일 로그가 지저분해지고 진짜 잡아야 할 에러들이 잘 보이지 않을 수 있기 때문에 해당 어노테이션을 사용한다.

// use a deprecated method and tell 
// compiler not to generate a warning
@SuppressWarnings("deprecation")
 void useDeprecatedMethod() {
     // deprecation warning
     // - suppressed
     objectOne.deprecatedMethod();
 }

 

또한 다음과 같이 여러개를 함께 사용할 수도 있습니다.

@SuppressWarnings({"unchecked", "deprecation"})

 

@SafeVarargs

제네릭 같은 가변 인자 매개 변수를 사용할 때 경고 무시

 

@FunctionalInterface

자바에서 람다 함수를 위한 인터페이스를 지정합니다. 함수형 인터페이스에 사용하기 적합하지 않은 경우(메소드가 없거나 두 개 이상이라면)에는 컴파일 오류가 발생합니다.

 

메타 어노테이션

메타 어노테이션은 커스텀 어노테이션을 구성할 때 시점, 위치 등을 지정하기 위한 어노테이션입니다. 

메타 어노테이션에 대해 잘 알고 있어야 정확히 원하는 커스텀 어노테이션을 만들어 사용할 수 있습니다.

 

@Retention

어노테이션의 리텐션 기간을 명명합니다. 다시 말하면, 어노테이션의 메타 정보가 언제 버려질지에 대한 타이밍을 설정합니다.

  • RetentionPolicy.Class
    • 바이트 코드 파일까지 어노테이션 정보 유지
    • 리플렉션을 이용해 어노테이션 정보를 얻을 수 없다.
  • RetentionPolicy.Runtime
    • 바이트 코드 파일까지 어노테이션 정보 유지
    • 리플렉션을 이용해 어노테이션 정보를 얻을 수 있다.
  • RetentionPolicy.Source
    • Compile 이후 삭제

 

@Documented

자바 문서에도 어노테이션 정보가 표현됩니다.

 

@Target

생성할 어노테이션이 적용될 수 있는 위치를 나열합니다.

  • ElementType.TYPE: 클래스, 인터페이스, 열거 타입
  • ElementType.ANNOTATION_TYPE: 어노테이션
  • ElementType.FILED: 필드
  • ElementType.CONSTRUCTOR: 생성자
  • ElementType.METHOD: 메소드
  • ElementType.LOCAL_VARIABLE: 로컬 변수
  • ElementType.PACKAGE: 패키지

 

@Inherited

자식 클래스가 어노테이션을 상속받을 수 있습니다.

 

@Repeatable

반복적으로 어노테이션ㅇ르 선언할 수 있습니다.

 

커스텀 어노테이션

커스텀 어노테이션은 메타 어노테이션을 사용하여 다음과 같은 구조를 갖습니다. 어노테이션의 필드 타입은 enum, String이나 기본 자료형, 기본 자료형의 배열만 사용 가능합니다.

@Target({ElementType.[적용대상]})
@Retention(RetentionPolicy.[정보가 유지되는 대상])
public @interface [어노테이션 명] {
    public 타입 elementName() [default 값]
    ..
}

 

즉, 예를 들어 다음과 같이 생성할 수 있습니다.

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value() default "MyAnnotation: default value"
}

 

위 어노테이션은 메소드에 붙일 수 있고, runtime 에 적용됩니다. 새로 만든 커스텀 어노테이션은 @어노테이션명으로 메소드 앞에 붙여서 사용가능합니다.

class MyObject {
    @MyAnnotation
    public void testMethod1() {
        System.out.println("This is testMethod1");
    }
    
    @MyAnnotation(value = "My new Annotation")
    public void testMethod2() {
        System.out.println("This is testMethod2");
    }
}

이렇게 만든 어노테이션은 자바의 리플렉션을 활용하여 특정 목적으로 사용 가능합니다. 그 전까지 어노테이션은 그저 문자열 정보를 가지고 있을 뿐입니다.

public class MyMain {
    public static void main(String[] args) {
        Method[] methodList = MyObject.class.getMethods();
        
        for (Method method : methodList) {
            if (method.isAnnotationPresent(MyAnnotation.class)) {
                MyAnnotation annotation = method.getDeclaredAnnotation(MyAnnotation.class);
                String value = annotation.value();
                System.out.println(method.getName() + ": " + value);
            }
        }
    }
}

다음과 같이 리플렉션을 통해 어노테이션의 값을 가져와 결과가 잘 나오는 모습을 확인할 수 있습니다.

 

어노테이션 정보 가져오기

다음의 메소드들을 이용하여 어노테이션의 정보를 가져와 사용할 수 있습니다.

리턴타입 메소드명(매개변수)
boolean isAnnotationPresent(Class<? Extends Annotation> annotationClass)
지정한 어노테이션이 적용되었는지 여부. Class에서 호출했을 경우 상위 클래스에 적용된 경우도 true를 리턴한다.
Annotation getAnnotation(Class<T> annotationClass)
지정한 어노테이션이 적용되어 있으면 어노테이션을 리턴하고 그렇지 않으면 null을 리턴한다.
Class 에서 호출했을 경우 상위 클래스에 적용된 경우에도 어노테이션을 리턴한다.
Annotation[] getAnnotations()
적용된 모든 어노테이션을 리턴한다. Class에서 호출했을 경우 상위 클래스에 적용된 어노테이션도 모두 포함한다.
적용된 어노테이션이 없을 경우 길이가 0인 배열을 리턴한다.
Annotation[] getDeclaredAnnotations()
직접 적용된 모든 어노테이션을 리턴한다. Class에서 호출했을 경우 상위 클래스에 적용된 어노테이션은 포함되지 않는다.

 

 

참고

https://hbase.tistory.com/169

https://docs.oracle.com/javase/tutorial/java/annotations/

https://velog.io/@potato_song/Java-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98-%EC%BB%A4%EC%8A%A4%ED%85%80-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98-%EB%A7%8C%EB%93%A4%EA%B8%B0

 

 

profile

SY 개발일지

@SY 키키

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!