본문 바로가기
language/java

Meta Annotation

by 죄니안죄니 2026. 6. 16.
반응형

여기서부터는 많은 개발자들이 이름만 알고 정확히는 이해하지 못하는 개념이다.

실제로 Spring을 사용하면서

 
@Service
@Transactional
@RestController
@GetMapping
 

을 매일 사용하지만,

"이 Annotation은 왜 또 Annotation이 붙어 있지?"

라는 의문을 가진 적이 있을 것이다.

이번 글을 이해하면 Spring이 새로운 Annotation을 얼마나 유연하게 확장하는지 알 수 있다.


Meta Annotation

"Annotation에 붙는 Annotation"


Annotation에도 Annotation을 붙일 수 있다.

우리는 지금까지

 
@Service
public class UserService {

}
 

처럼 사용했다.

그런데 실제 Spring 내부 코드를 보면

 
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {

}
 

이렇게 되어 있다.

여기서

 
@Target
@Retention
@Documented
@Component
 

모두 Annotation이다.

즉,

Annotation 위에 또 Annotation이 붙어 있다.

이것을 Meta Annotation이라고 한다.


왜 이런 구조를 만들었을까?

먼저 Meta Annotation이 없다면 어떻게 될까?

Spring이 Bean을 찾는 기준이

 
@Service

@Repository

@Controller
 

세 개뿐이라고 가정해보자.

그러면 Spring 내부는

if (@Service)
    Bean 등록

if (@Repository)
    Bean 등록

if (@Controller)
    Bean 등록
 

처럼 계속 늘어나야 한다.

새로운 Annotation이 생길 때마다

Spring 코드를 수정해야 한다.

굉장히 비효율적이다.


Spring은 더 좋은 방법을 선택했다.

Spring은

"Bean으로 등록하고 싶은 Annotation이라면 @Component를 붙여라."

라는 규칙을 만들었다.

즉,

 
@Service
 

의 내부는

 
@Component
public @interface Service {

}
 

이다.


 
@Repository
 

 
@Component
public @interface Repository {

}
 

이다.


 
@Controller
 

 
@Component
public @interface Controller {

}
 

이다.


그러면 Spring은

@Service 발견

↓

@Component가 붙어있는지 확인

↓

붙어있다

↓

Bean 등록
 

만 수행하면 된다.


그림으로 이해하기

                @Component
                     ▲
                     │
        ┌────────────┼────────────┐
        │            │            │
    @Service   @Repository   @Controller
        │            │            │
        ▼            ▼            ▼
 UserService   UserRepository   UserController
 

Spring은

맨 위의 @Component만 이해하면 된다.


직접 만들어보자

Bean 역할을 하는 Annotation

 
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyComponent {

}
 

MyComponent를 사용하는 Annotation

 
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@MyComponent
public @interface MyService {

}
 

사용

 
@MyService
public class UserService {

}
 

Spring과 비슷한 프로그램이라면

UserService

↓

@MyService 존재

↓

MyService에 @MyComponent 존재

↓

Bean 등록
 

을 수행할 수 있다.


Meta Annotation을 검사하는 과정

Reflection은 Annotation도 조사할 수 있다.

 
Class<?> clazz = UserService.class;

Annotation[] annotations =
        clazz.getAnnotations();
 

여기서

@MyService
 

를 얻는다.


그리고

 
Class<?> annotationType =
        annotation.annotationType();
 

를 호출하면

MyService.class
 

를 얻는다.


다시 Reflection을 사용한다.

 
annotationType.isAnnotationPresent(MyComponent.class);
 

결과

true
 

즉,

UserService

↓

@MyService

↓

MyService.class

↓

@MyComponent 존재

↓

Bean 등록
 

이라는 구조이다.


Spring에서 가장 많이 쓰이는 Meta Annotation


@Component

가장 기본이 되는 Annotation이다.

 
@Component
public class UserService {

}
 

직접 Bean 등록


@Service

 
@Service
public class UserService {

}
 

실제로는

 
@Component
public @interface Service {

}
 

이다.


@Repository

 
@Repository
public class UserRepository {

}
 

역시

 
@Component
public @interface Repository {

}
 

이다.


@Controller

 
@Controller
public class UserController {

}
 

역시

 
@Component
public @interface Controller {

}
 

이다.


그런데 왜 굳이 나눌까?

모두 @Component인데

 
@Component
public class UserService {

}

@Component
public class UserRepository {

}
 

로 쓰면 안 될까?

가능하다.

하지만 의미가 사라진다.


 
@Service
 

를 보면

서비스 계층
 

임을 알 수 있다.


 
@Repository
 

를 보면

데이터 접근 계층
 

임을 알 수 있다.


 
@Controller
 

를 보면

웹 요청을 처리하는 클래스
 

임을 알 수 있다.


즉,

기능은 @Component와 같지만 역할을 표현하기 위해 별도의 Annotation을 제공하는 것이다.


Spring은 Meta Annotation을 적극 활용한다.

예를 들어

 
@RestController
 

를 보자.

내부는 거의 이런 형태이다.

 
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Controller
@ResponseBody
public @interface RestController {

}
 

즉,

@RestController

↓

@Controller

↓

@Component

↓

Bean 등록
 

동시에

@ResponseBody

↓

JSON 응답
 

도 수행한다.


사용자도 만들 수 있다.

실무에서도 자주 사용한다.

예를 들어

모든 API에 공통 정책을 적용하고 싶다면

 
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@RestController
@RequestMapping("/api/v1")
public @interface ApiController {

}
 

사용

 
@ApiController
public class UserController {

}
 

그러면

@RestController

+

@RequestMapping("/api/v1")
 

를 동시에 적용할 수 있다.


전체 흐름

UserService

↓

@MyService

↓

MyService Annotation

↓

@MyComponent 존재

↓

Spring Reflection

↓

Bean 등록
 

면접 포인트

Meta Annotation이란?

Annotation에 붙는 Annotation으로, Annotation의 동작 방식이나 의미를 정의하기 위해 사용된다.

Spring에서 가장 대표적인 Meta Annotation은?

@Service, @Repository, @Controller는 모두 내부적으로 @Component를 Meta Annotation으로 가지고 있으며, Spring은 이를 기반으로 Bean을 등록한다.

Meta Annotation을 사용하는 이유는?

  • 코드 중복 제거
  • 기능 확장 용이
  • 역할 표현 가능
  • 새로운 Annotation을 쉽게 조합 가능

다음 글 추천

이 흐름이라면 다음은 Dynamic Proxy 원리가 가장 자연스럽다.

Reflection → Class 객체 → Annotation → Meta Annotation을 이해한 상태에서 보면,

Spring AOP, @Transactional, @Async가 왜 프록시를 통해 동작하는지

를 한 번에 이해할 수 있다.

반응형

'language > java' 카테고리의 다른 글

JDK Proxy vs CGLIB  (0) 2026.06.16
Dynamic Proxy 원리  (0) 2026.06.16
Annotation 동작 원리  (0) 2026.06.16
Class 객체란?  (0) 2026.06.16
Reflection 원리  (0) 2026.06.16

댓글