ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Java] 표준 어노테이션
    백엔드/Java 2023. 2. 21. 08:00
    반응형

     

    자바 코드를 보다가 내부 코드가 궁금해져서 확인해봤는데, 이런 내용의 코드가 있었다.

     

    @Test는 어떤 동작을 수행하길래 이런 어노테이션들이 붙어 있는 것일까? 그래서 찾아보니 자바의 정석을 통해 어노테이션을 정리하는 사람들이 많았다.

     

    표준 어노테이션은 크게 두가지로 나뉜다. 하나는 어노테이션을 정의하는데 사용하는 메타 어노테이션, 하나는 일반 어노테이션이다. 이 글에서는 메타 어노테이션부터 설명한다.

     

     

    1. 메타 어노테이션


    오라클 공식 홈페이지 튜토리얼을 확인해보면 메타 어노테이션은 다른 어노테이션에 사용되는 어노테이션이라고 나와있다. 어노테이션을 정의하는 어노테이션이라고 생각하면 될 것 같다.

     


    [@Retension]

    이 어노테이션이 들어있으면 어디까지 어노테이션 내용을 저장하는지 대한 내용을 지정할 수 있다.

    여기는 자바 소스 코드가 어떻게 JVM까지 가게 되는지 간단한 과정이라도 알아야 한다.


    1. RetentionPolicy.SOURCE (.java 까지)

    이 값이 붙어 있으면 소스 코드에서만 존재할 수 있다. 즉, 해당 소스 코드를 컴파일할 때, 컴파일러에 의해 해당 어노테이션은 삭제 된다. 그래서 컴퓨터 입장에서는 .java에만 존재하는 내용이고, .class 파일로 변환될 때에는 해당 내용이 없어져서 보인다.

    내용이 삭제되니 정보 전달 목적으로 사용하기도 하고, 컴파일러 내부에서 처리할 코드에 대한 추가 정보를 제공할 수 있다. 대표적인 예시는 @Getter, @Setter로 컴파일이 될 때, 바이트 코드에 getter, setter 메서드를 모두 생성해두고 사라지게 된다.

     

     

    2. RetentionPolicy.CLASS (.class 까지)

    컴파일러에 의해 .class 파일까지 기록을 하지만, 런타임에서는 사용할 수 없다. 대신 컴파일을 할 때에는 남아있기 때문에 컴파일러에게 추가 정보를 제공할 수 있다.

    대표적인 어노테이션은 Lombok의 @Nonnull이 있다. Nonnull은 값이 null이 나오면 에러가 발생하는 어노테이션이다. 이것도 컴파일이 될 때 null이 아님을 확인하는 코드를 넣게 된다. 이것만 보면 getter랑 다를바가 없어 보이지만, CLASS인 이유는 프로그램이 실행되는 도중에 null값을 전달 받게 된다면 IDE에서 어디가 null값이 발생하는 것인지 알려주는 기능 같은걸 제공할 수 있다고 한다.

     

     

    3. Retention.RUNTIME (프로그램 종료 전까지)

    대부분의 어노테이션은 RUNTIME을 사용한다. 

    대표적으로 @Autowired가 있는데, 의존성 주입을 위해 bean으로 등록된 객체를 스캔하여 주입하는 역할을 한다. 적절한 객체를 찾아야하기 때문에 컴포넌트 스캔을 하는데, RetentionPolicy.RUNTIME으로 설정해야 프로그램이 실행되는 도중에 해당 어노테이션을 참조하며 작동할 수 있게 되므로 그렇다. 프레임워크 어노테이션처럼 프로그램 실행 도중에 처리해야하는 어노테이션은 RUNTIME으로 설정해야 작동한다고 한다.


    [@Documented]

    @Documented가 들어간 어노테이션을 사용하게 된다면 JavaDoc을 생성할 때, 어노테이션도 문서에서 보여준다.

     

    package ladder.domain;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Target;
    
    @Target(ElementType.FIELD)
    public @interface abcdefg {
    }

    @abcdefg에는 @Documented 어노테이션이 없는 상태이다.

     

    package ladder.domain;
    
    public class Employee {
        @abcdefg
        public String name;
    }

    이 상태로 Tools - Generate JavaDoc...을 해보자

     

    이제 abcdefg에 @Documented를 다시 해서 JavaDoc을 생성해보면..

    이렇게 어노테이션까지 나오게 된다.


    [@Target]

    어노테이션을 어디에 쓸지 결정하는 어노테이션이다.

     

    - ANNOTATION_TYPE (어노테이션)

    - CONSTRUCTOR (생성자)

    - FILED (멤버 변수, Enum 변수)

    - LOCAL_VARIABLE (지역 변수)

    - METHOD (메서드)

    - PACKAGE (패키지)

    - PARAMETER (매개변수)

    - TYPE (타입 - 클래스, 인터페이스, Enum)

    - TYPE_PARAMETER (타입 파라미터)

    - TYPE_USE (타입이 사용되는 모든 곳)

     

    여기서 타입이란 int, class, enum, interface 이런걸 뜻한다.

     

    @Test를 확인해보면 ANNOTATION_TYPE과 METHOD에만 가능하다고 나와있다. 클래스 위에 @Test를 붙여보거나, 메서드 파라미터에 어노테이션을 붙여보면 안된다는 것을 확인할 수 있다.


    [@Inherited]

    어노테이션이 자동으로 상속이 된다.

    만약 @유전병이라는게 있고, Parent 클래스에 붙어 있는 상태라면 Parent를 상속하는 Children도 @유전병을 가지고 있는 것으로 판단한다.


    [@Repeatable]

    이 어노테이션은 한 곳에서 여러 번 사용할 수 있다는 것이다.

    만약 @Stopwatch가 @Repeatable을 가지고 있는 상태라면 

     

    @Stopwatch("05:00")
    @Stopwatch("01:00")
    @Stopwatch("00:30")
    public class time {
    };

     

    이렇게 여러 번 사용할 수 있다.


     

     

    2. 일반 어노테이션


    오라클 공식 홈페이지 튜토리얼에서는 java.lang에 미리 정의된 어노테이션이라고 나와있다.

     


    [@Deprecated]

    더 이상 사용하지 않는 기능이 있을 때, 해당 어노테이션을 붙인다.

    만약 @Deprecated가 붙여진 것을 사용할 경우, 컴파일을 할 때 경고 메시지가 나오게 된다. 사용하지 말라는 권고이기 때문에 경고 메시지만 나올 뿐이지 프로그램은 잘 돌아간다.

    @Retention(RetentionPolicy.RUNTIME)인 이유는 프레임워크가 업데이트되며 사용하지 않는 것에 이 어노테이션을 붙일 수도 있기 때문이다. 


    [@Override]

    @Target(ElemenType.METHOD)로 메서드에만 사용할 수 있는 어노테이션이다.

    다들 알다시피 메서드를 오버라이딩하는데 쓰인다.

    @Retention(RetentionPolicy.SOURCE)이기 때문에 컴파일을 할 때, 조상에 이런 메서드가 있는지 확인하고 없으면 에러를 던진다. 에러가 없으면 저 어노테이션의 목적은 끝났으니 클래스 파일에는 없다.


    [@SuppressWarning]

    컴파일을 할 때, 컴파일러가 특정 경고 메시지를 출력하지 않도록 설정하는 것이다. 예를 들어서 @Deprecated는 컴파일에 경고 메시지를 나오게 하므로 @SuppressWarning을 통해 해당 경고 메시지가 나오지 않게 만들 수도 있다.

    이것 역시 컴파일 이후에는 쓸모가 없기 때문에 @Retention(RetentionPolicy.SOURCE)를 가지고 있다.


    [@SafeVarargs]

    코드를 작성한 사람이 이 메서드는 타입 안정성을 보장한다는 뜻으로 달아두는 어노테이션이다.

    주로 제네릭스(Generics)를 사용하는 곳에서 많이 쓰이는 편이다. 제네릭스는 어떤 타입이 들어올지 모르기 때문에 @SafeVarags를 적용하면 타입 안정성이 있다는 뜻으로 타입 안정성이 보장되지 않은 unchecked cast는 경고 메시지가 나오지 않는다. 하지만 varargs 경고 메시지는 억제할 수 없기 때문에 @SafeVarargs를 사용한다면 @SuppessWarning("varargs")도 같이 사용해야 한다.


    [@FunctionalInterface]

    1개의 추상 메서드를 가지는 함수형 인터페이스를 만들었을 때 적용한다. 이 어노테이션을 적용하면 컴파일 과정에서 정말 함수형 인터페이스가 맞는지 검사를 해주게 된다.


     

     

     

     

    3. @Test


    - 어노테이션과 메서드에 @Test를 붙일 수 있다.

    - 프로그램이 종료되기 전까지 해당 어노테이션을 참조할 수 있게 된다.

    - JavaDoc으로 변환하면 @Test가 붙어서 나온다.

    - @API는 여기서 다루지 않았지만, 만약 사용자가 이걸 사용하고 싶을 때 어떤 정보를 알고 있어야 하는지 보여주는 어노테이션이라고 한다. 자바 5.0부터 STABLE 상태로 계속 사용해도 되는 어노테이션이다.

    - @Testable은 IDE나 툴 제공 업체에서 테스트 가능하다는 것으로 서비스 기능 추가에 사용하는 것이다. IDE가 소스 코드만 분석하여 테스트 코드임을 알 수 있게 만들어 주는게 대표적 예시이다.


     

     

     

     

    간단하게 정리할 수 있을 줄 알았는데, 생각보다 내용이 많아서 정리하기 힘들었다.

     

    반응형

    댓글

Designed by Tistory.