메타 애노테이션과 합성 애노테이션
메타 애노테이션이 붙은 클래스를 기능적인 면에서는 애노테이션이 붙은 클래스와는 차이가 없습니다. 다만 컴포넌트를 직접 붙이는 것과 컨트롤러나 서비스 같은 컴포넌트를 메타 애노테이션으로 갖고 있는 애노테이션을 붙이는 것과 컴포넌트 스캐너가 바라볼 때는 동일합니다. 하지만 다른 이름을 부여하여 메타 애노테이션을 만들면 Spring Bean으로 등록되는것 뿐만 아니라 @Controller애노테이션이 붙은 Bean은 웹 MVC의 컨트롤러 역할을 담장하는지 혹은 비즈니스 로직을 담당하는 Service로직인지 추가적인 정보를 확인할 수 있습니다. 애노테이션 자체가 달라지기 때문에 여기에 부가적인 효과를 기대할 수 잇습니다.
하지만 여기서 혼동할 수 있는 부분이 있는데 메타 애노테이션을 상속의 개념으로 혼동하면 안됩니다. 애노테이션 자체에는 상속이라는 개념이 존재하지 않습니다. 모든 애노테이션이 메타 애노테이션이 될 수 없습니다. 애노테이션에는 Retention과 Target이라는 정보를 꼭 부여해야합니다. 그 이유는 애노테이션을 활용할 수 있는 위치는 다양한데 Class, Method, Field 등등이 있습니다. 그중 Annotation Type이라는 위치에 사용할 수 있어야지 메타 애노테이션이 될 수 잇습니다.
/*
메타 애노테이션을 통해서 단위테스트라는 점을 애노테이션 명으로
직관적인 설명을 위해 만들 수도 있습니다.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Test
@interface UnitTest { }
public class HelloServiceTest {
/*
JUnit Test 애노테이션을 메타로 가지고 있고 단위테스트라는 점을 애노테이션 명으로
표현하기위해 @UnitTest를 만들었습니다. 메타 애노테이션으로 @Test를 가지고 있기때문
에 JUnit의 테스트 기능을 그대로 사용할 수 있습니다.
*/ @UnitTest
void simpleHelloService() {
HelloService helloService = new SimpleHelloService();
String result = helloService.sayHello("Test");
Assertions.assertThat(result).isEqualTo("Hello Test");
}
@Test
void helloDecorator() {
HelloDecorator helloDecorator = new HelloDecorator(name -> name);
String result = helloDecorator.sayHello("Test");
Assertions.assertThat(result).isEqualTo("*Test*");
}
}
위 코드는 메타 애노테이션을 활용에 대한 예시 입니다. @Test 애노테이션을 메타 애노테이션으로 갖고있는 @UnitTest애노테이션을 구성하는 코드 입니다.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@UnitTest <- 오류 발생
@interface FastUnitTest { }
@UnitTest 메타 애노테이션을 포함하고 있는 빠른 테스트라는 임의의 애노테이션을 만들어 보겠습니다. 단 위 애노테이션을 만들면 IDE에서 오류가 표시되는데 '@UnitTest' not applicable to annotation type라는 오류가 표시됩니다. @UnitTest구성정보를 확인해보면 @Target(ElementType.METHOD)으로 설정되어있습니다. 그러면 @UnitTest가 메타 애노테이션으로 포함하고 있는 @Test 애노테이션 구성정보를 확인해 보겠습니다.
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@API(
status = Status.STABLE,
since = "5.0"
)
@Testable
public @interface Test { }
@Test 애노테이션 구성 정보를 확인해 보면 @Target 구성 정보에 ElementType.ANNOTATION_TYPE도 포함되어 있습니다. 즉 @UnitTest 애노테이션이 메타 애노테이션이 될 수 없었던 이유는 @Target 구성 정보가 ElementType.ANNOTATION_TYPE정보가 존재하지 않았기 때문입니다.
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Test
@interface UnitTest { }
@Target 구성 정보중 ElementType.ANNOTATION_TYPE정보를 추가해주면 정상적으로 @FastUnitTest에 @UnitTest 애노테이션이 메타 애노테이션으로 활용될 수 있게 됩니다.
앞서 메타 애노테이션을 활용하여 @UnitTest같은 애노테이션들을 구성하였습니다. 이러한 메타 애노테이션들을 활용하여 또 하나의 애노테이션을 만드는데 이를 합성 애노테이션이라고도 부르기도 합니다.
애노테이션을 활용하여 개발을 하다 보면 너무 많은 애노테이션을 활용 하다보니 코드에 가독성, 코드가 보기 좋지않게 구성되는경우가 있을 수 있는데 공통적인 애노테이션들을 합성 애노테이션으로 구성하여 보일러 플레이트를 줄일 수 있습니다.
'Spring > 인프런 토비의 Spring Boot' 카테고리의 다른 글
섹션 6-3 [자동 구성 기반 애플리케이션] 빈 오브젝트의 역할과 구분 (0) | 2024.06.14 |
---|---|
섹션 6-2 [자동 구성 기반 애플리케이션] 합성 애노테이션 적용 (0) | 2024.06.14 |
섹션 5-3 [DI와 테스트, 디자인 패턴] DI를 이용한 Decorator, Proxy 패턴 (0) | 2024.06.12 |
섹션 5-2 [DI와 테스트, 디자인 패턴] DI와 단위 테스트 (0) | 2024.06.12 |
섹션 5-1 [DI와 테스트, 디자인 패턴] 테스트 코드를 이용한 테스트 (0) | 2024.06.11 |