코드를 작성하다보니 동일한 어노테이션을 여러 클래스에 반복적으로 작성해야 하는 상황이 발생하였다. 이러한 작업이 상당히 번거롭게 느껴졌고, 이러한 반복 작업을 줄이고자 커스텀 어노테이션을 만들어 적용하게 되었는데, 이에 대해 자세히 알아보도록 하자.
1. 다양한 Java 어노테이션
[ 어노테이션의 종류 ]
어노테이션을 직접 개발하기 위해서는 Java에서 제공하는 어노테이션에 대해 먼저 알아야 한다. Java의 어노테이션은 크게 built-in 어노테이션(Built-in Annotation)과 Meta 어노테이션(Meta Annotation)이 존재한다.
- built-in 어노테이션
- Java 코드에 적용되는 어노테이션
- @Overried, @Deprecated, @SuppressWarnings 등이 존재
- meta 어노테이션
- 다른 어노테이션에 적용되기 위한 어노테이션
- @Retention, @Documneted, @Target, @Inherited, @Repeatable 등이 존재
이 중에서 우리는 meta 어노테이션에 대해 자세히 알아보고자 한다.
[ Meta 어노테이션의 종류 ]
- Retention: 해당 어노테이션의 정보를 어느 범위까지 유지할 것인지를 설정함
- RetentionPolicy.SOURCE: 컴파일 전까지만 유효하며 컴파일 이후에는 사라짐
- RetentionPolicy.CLASS: 컴파일러가 클래스를 참조할 때까지 유효함
- RetentionPolicy.RUNTIME: Reflection을 사용하여 컴파일 이후에도 JVM에 의해 계속 참조가 가능함
- Documented: JavaDoc 생성 시 Document에 포함되도록 함
- Target: 해당 어노테이션이 사용되는 위치를 결정함
- ElementType.PACKAGE : 패키지 선언시
- ElementType.TYPE : 타입 선언시
- ElementType.CONSTRUCTOR : 생성자 선언시
- ElementType.FIELD : 맴버 변수 선언시
- ElementType.METHOD : 메소드 선언시
- ElementType.ANNOTATION_TYPE : 어노테이션 타입 선언시
- ElementType.LOCAL_VARIABLE : 지역 변수 선언시
- ElementType.TYPE_PARAMETER : 매개 변수 타입 선언시
- Inherited: 해당 어노테이션을 하위 클래스에 적용함
- Repeatable: Java8부터 지원하며, 연속적으로 어노테이션을 선언하는 것을 허용함
2. 커스텀 어노테이션 만들기
[ 커스텀 어노테이션 만들기 ]
예를 들어 다음과 같은 RestMemberController를 개발하여 Swagger로 문서화한다고 하자. 여기서 @Api와 @RestController 어노테이션은 다른 RestController들에도 공통적으로 적용되는데, 이를 커스텀 어노테이션으로 만들어 반복 작업을 줄여보도록 하자.
@Api
@RestController
@RequiredArgsConstructor
@RequestMapping("/member")
public class RestMemberController {
private final MemberService memberService;
@ApiOperation("멤버 목록 반환")
@GetMapping("/list")
public ResponseEntity<String> upload() {
return ResponseEntity.ok(memberService.getList());
}
}
1. 어노테이션 생성
어노테이션은 다음과 같이 인터페이스 앞에 @를 붙여서 생성할 수 있다.
public @interface RestControllerWithSwagger {
}
위와 같이 생성된 어노테이션은 다른 곳에서 참조되지만 아무런 기능도 하지 않는다.
2. 메타 어노테이션 추가
앞서 다른 어노테이션에 적용되기 위한 메타 어토네이션에 대해 살펴보았다. 그 중에서 필요한 것들을 적용하면 된다.
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Target(ElementType.TYPE)
public @interface RestControllerWithSwagger {
}
위의 예제에서는 컴파일 이후에도 JVM에 의해 계속 참조가 가능하도록 @Retention(RetentionPolicy.RUNTIME) 을 적용하였으며, 하위 클래스에도 적용가능하도록 @Inherited를 적용하였다. 또한 javadoc에 의해 문서화가 되도록 @Documented를 적용하였으며, 타입 선언 시 어노테이션을 적용할 수 있도록 @Target(ElementType.TYPE)를 적용하였다. 여러 타겟을 원하는 경우에는 @Target({ElementType.TYPE, ElementType.Field}) 와 같이 사용하면 된다.
3. 적용할 어노테이션 추가
추가적으로 적용할 어노테이션이 있으면 추가해준다. 우리는 @Api와 @RestController라는 어노테이션을 공통으로 묶을 것이므로 이를 추가해준다.
@Api
@RestController
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Target(ElementType.TYPE)
public @interface RestControllerWithSwagger {
String name() default "RestController";
String value();
}
4. 변수 추가
어노테이션에 값을 부여하기를 원한다면 변수를 다음과 같이 선언해줄 수 있다.
@Api
@RestController
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Target(ElementType.TYPE)
public @interface RestControllerWithSwagger {
String name() default "RestController";
String value();
}
5. 적용하기
위와 같은 과정으로 어노테이션을 생성하였으면 이제 다음과 같이 적용할 수 있다.
@RestControllerWithSwagger(value = "RestMemberController", name = "RestMemberController")
@RequiredArgsConstructor
@Test1
@RequestMapping("/member")
public class RestMemberController {
private final MemberService memberService;
@ApiOperation("멤버 목록 반환")
@GetMapping("/list")
public ResponseEntity<String> upload() {
return ResponseEntity.ok(memberService.getList());
}
}
출처: https://mangkyu.tistory.com/130 [MangKyu's Diary:티스토리]