JAVA

[ JAVA ] - 커스텀 어노테이션 만드는 법

algml0703 2023. 5. 6. 08:08
반응형

어노테이션 (:annotation)

annotation은 사전적으로는 주석의 의미를 가지며, 프로그래밍 관점에서 어노테이션은 코드에 대한 부가 정보를 제공하는 메타 데이터이라고 할 수 있다. 어노테이션을 통하여 코드에 대한 설명과 컴파일러 명령 등을 표현할 수 있다. 이를 통해 개발자는 더욱 편리하고 가독성 높은 개발이 가능한다. 예를 들어 자바에서 흔하게 사용되는 @Override 어노테이션이 붙은 메서드는 상위 클래스나 인터페이스로부터 상속 받은 메서드를 오버라이딩하고 있음을 컴파일러에게 미리 알리는 역할을 수행하여 하위클래스에서 오버라이딩 하려 할 때 메서드 명 등을 잘못하여 정의한 경우에도 컴파일러가 알아서 경고메시지를 표시하여 줌으로써 개발자는 에러를 방지할 수 있다. 또한 @Deprecated의 경우는 해당 메서드나 클래스가 더 이상 사용되지 않음을 말해준다. 어노테이션은 앞의 설명에서 보았듯이 '@' 기호를 사용하며, 필요에 따라 직접 커스튬하여 만들 수도 있다.


어노테이션의 역할

1) 코드에 대한 문서화 기능을 제공한다.

2. 컴파일러에게 정보를 제공하여 준다.

3) 런타임 시 동작을 제어한다.

4) 코드 생성을 위한 정보를 제공한다.


어노테이션의 종류

1) 메타 어노테이션

메타 어노테이션이란 어노테이션 정의시에 사용되는 어노테이션으로 어노테이션을 위한 어노테이션이라고 할 수 있다. 메타 어노테이션에는 @Retention, @Target, @Documented, @Inherited가 존재한다.

@Retention
어노테이션이 적용이 유지되는 범위를 지정한다. 
@Target
어노테이션이 적용되는 대상을 지정한다.
@Documented
어노테이션 정보가 javadoc 문서에 포함되도록 한다.
@Inherited
어노테이션이 하위 클래스에 상속되도록 한다.

2) JDK 표준 어노테이션

자바에서 기본적으로 제공하는 어노테이션으로, @Override, @Deprecated, @SuppressWarnings 등이 있다.

@Override
컴파일러에게 해당 메서드가 상위 클래스나, 인터페이스로부터 상속받은 메서드를 오버라이딩한 메서드라는 것을 알린다.
@Deprecated
앞으로 사용되지 않을 것임을 알린다. jdk사라지의 버전이 바뀔 때마다 새로 생기거나 사라지는 클래스나 메서드가 있는데, 이때에 앞으로 사용되지 않을 것임이 예상되는 경우에는 해당 어노테이션을 사용하여 미리 해당 버전의 jdk를 사용하는 개발자들에게 정보를 제공할 수 있다.
@SuppressWarnings
컴파일러의 특정 경고 메시지가 나타나지 않도록 해준다.

3) 커스텀 어노테이션

사용자가 메타 어노테이션을 이용하여 직접 구현한 어노테이션을 의미한다.


custom annotation은 기본적으로 아래와 같은 단계를 통해 만들 수 있다.

1. @interface를 사용하여 인터페이스를 정의한다. 

 위의 어노테이션의 경우 런타임 때까지 어노테이션 정보가 사용되고, 필드에 적용하여 사용할 수 있도록 한 것이다. 또한 해당 어노테이션 명은 Id가 된다.

@Retention의 인자로는 RetentionPolicy의 값이 올 수 있다.
- RetentionPolicy.SOURCE 
코드 내에서만 해당 어노테이션이 유효하게 적용되며, 클래스 파일에는 존재하지 않아 컴파일 이후 시점부터는 적용되지 않는다.
RetentionPolicy.CLASS 
컴파일 된 클래스 파일에 포함되어 유효하게 적용되며, 런타임 이후 시점 즉 실행시에는 적용되지 않는다.
RetentionPolicy.RUNTIME
 런타임 때까지 해당 어노테이션이 유효하게 적용된다. 해당 인자를 줄 경우에 실행시에 리플렉션을 통해 어노테이션 정보를 읽어올 수 있다.

* 리플렉션이란(Reflection)?
 리플렉션이란 런타임 과정에서 클래스, 메서드, 변수의 정보를 가져올 수 있도록 해주는 api를 의미한다.

@Target의 인자로는 ElementType이 올 수 있다. 여러 개를 인자로 줄 때에는 {} 안에 값을 넣어준다.
- ANNOTATION_TYPE : 어노테이션에 적용
- CONSTRUCTOR : 생성자에 적용
- FIELD : 필드(멤버변수, enum 상수)에 적용
- LOCAL_VARIABLE : 지역 변수에 적용
- METHOD : 메서드에 적용
- PACKAGE : 패키지에 적용
- PARAMETER : 매개변수에 적용
- TYPE : 클래스, 인터페이스, enum에 적용

2. 어노테이션 사용

커스텀 구현된 어노테이션 사용시에는 위와 같이 사용하게 된다. 

만일 구현한 어노테이션에 요소가 하나에 불과하고, 해당 요소명이 value인 경우에는 어노테이션 적용시 요소명을 생략하고, 해당 어노테이션의 인자값 자체에 값을 넣어주기만 해도 된다.

Ex) uuid를 생성하여 주는 커스텀 어노테이션 구현

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface UUIDGenerator {
}

@Interface를 사용하여 아무런 요소도 갖지 않고, UUIDGenerator 명을 가진 어노테이션을 만들어준다. 해당 어노테이션은 실행시에도 유효하며, 필드를 대상으로 하는 어노테이션이다.

public class UUIDGeneratorProcessor {
    public static void process(Object object) {
        Class<?> clazz = object.getClass(); // 해당 클래스 정보를 가져온다.
        Field[] fields = clazz.getDeclaredFields(); // 해당 클래스에 선언된 필드 정보를 가져옴
        for (Field field : fields) { 
            if (field.isAnnotationPresent(UUIDGenerator.class)) {
            // 해당 필드에 UUIDGenerator 어노테이션이 존재한다면
                field.setAccessible(true);
               	// 해당 필드에 접근할 수 있도록 하여 준다.
                try {
                    field.set(object, UUID.randomUUID().toString());
                    // 해당 필드에 uuid 값을 할당한다.
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

자바의 리플렉션 api를 사용하여 해당 어노테이션이 붙은 필드의 경우는 uuid 값을 가지도록 하는 클래스를 구현하였다.

public class TestClass {
    @UUIDGenerator
    private String id;

    public String getId() {
        return id;
    }
}

해당 어노테이션을 위와 같이 사용하는 클래스를 구현하였다.

public class main {
    public static void main(String[] args) {
        TestClass test = new TestClass();
        UUIDGeneratorProcessor.process(test);
        System.out.println(test.getId());
    }
}

실행 시 정상적으로 uuid를 가지고 있음을 확인할 수 있다.

출처

자바의 정석

반응형