1. 한눈에 보는 세 진영의 관계
진영별 정리가 아닌 기능별 어노테이션 간단 정리는 여기서.
- 스프링 (Spring) 어노테이션: 스프링 프레임워크가 독자적으로 만든 어노테이션. 기능이 강력하고 다양하지만, 스프링 프레임워크에 종속된다는 단점이 있음.
- 자바 표준 (Java SE/JSR-250) 어노테이션: 과거 자바 진영(Oracle/Sun)에서 특정 프레임워크에 종속되지 않는 표준을 만들자고 정의한 표준(JSR) 어노테이션.
- 자카르타 (Jakarta EE) 어노테이션: 자바의 소유권이 오라클에서 이클립스 재단으로 넘어가면서, 'Java'라는 상표권 문제로 인해 패키지 이름이 javax에서 jakarta로 변경된 현재의 자바 표준.
💡 쉽게 말해: 과거의 javax.annotation.* (자바 표준)이 현재 jakarta.annotation.* (자카르타 표준)으로 이름만 바뀌어 고스란히 이어져 온 것입니다.
2. 주요 기능별 어노테이션 비교
의존성 주입(DI)과 객체 생명주기 관리에서 이 세 진영은 서로 매칭되는 어노테이션들을 가지고 있음.
| 스프링 (Spring) | 과거 자바 표준 (JSR) | 현재 자카르타 표준 (Jakarta) | |
| 의존성 주입 (DI) | @Autowired | @Inject (JSR-330) @Resource (JSR-250) |
@Inject @Resource |
| 주입 대상 지정 | @Qualifier("name") | @Named("name") | @Named("name") |
| 초기화 메서드 | @PostConstruct (기능 지원) | @PostConstruct | @PostConstruct |
| 소멸 메서드 | @PreDestroy (기능 지원) | @PreDestroy | @PreDestroy |
| 패키지 경로 | org.springframework.beans.. | javax.annotation.. | jakarta.annotation.. |
💡 어노테이션 기능별 상세 정리
1. 의존관계 주입의 핵심: @Autowired
스프링이 관리하는 빈을 다른 빈에 주입할 때 사용하는 가장 대표적인 어노테이션. 수동 설정 파일 내의 @Bean 메서드 내부나, 자동 등록되는 @Component 클래스의 생성자/필드/수정자 어디에나 붙여서 의존관계를 자동으로 주입받을 수 있음.
- 생성자 주입: 스프링에서 가장 권장하는 방식으로, 타입 기반 자동 의존관계 주입. 생성자가 1개만 존재할 때는 @Autowired를 생략할 수도 있음.
- 필드/수정자 주입: 필요에 따라 유연하게 주입 방식을 선택할 수 있도록 도움.
2. 조건부 빈 선택: @Primary, @Qualifier, @Named
동일한 타입의 빈이 여러 개 존재할 때, 스프링은 어떤 빈을 주입해야 할지 몰라 에러(NoUniqueBeanDefinitionException)를 발생시킴. 이 매칭 문제를 해결하는 어노테이션들 역시 수동/자동 구분 없이 공통으로 사용된다.
- @Primary
- 역할: 여러 개의 빈이 충돌할 때, 최우선순위를 부여하는 어노테이션
- 활용: 메인으로 사용할 빈에 @Primary를 붙여두면 별도의 지정 없이도 해당 빈이 먼저 주입된다.
- @Qualifier
- 역할: 빈에 '별칭(구분자)'을 지어주어, 주입 시점에 정확히 지정해서 가져올 수 있도록 함.
- 활용: 빈 등록 시점(@Bean 또는 @Component 위)에 이름을 지정하고, 주입받는 시점(@Autowired와 함께)에 동일한 이름을 적어 매칭.
- @Named (JSR-330)
- 역할: 자바 표준(JSR-330) 어노테이션으로, 스프링의 @Qualifier와 유사하게 특정 빈의 이름을 지정하여 주입할 때 사용한다. (사용을 위해서는 관련 의존성 추가 필요)
3. 빈의 생명주기를 관리하는 @Scope와 @Lazy
빈이 생성되고 관리되는 범위와 시점을 제어하는 어노테이션.
- @Scope
- 역할: 빈의 생존 범위(Scope)를 기본값인 싱글톤(Singleton) 외에 프로토타입(Prototype), Request, Session 등으로 변경한다.
- 활용: 수동 등록 메서드나 자동 등록 클래스 위에 모두 선언 가능.
- @Lazy
- 역할: 스프링 컨테이너가 구동될 때 빈을 바로 생성(로딩)하지 않고, 실제로 해당 빈이 처음 사용되는 시점까지 생성을 미룸.
4. 환경별 맞춤 설정: @Profile
애플리케이션이 실행되는 특정 환경(로컬, 개발, 운영/서비스(prod) 등)에 따라 특정 빈을 등록할지 말지 결정할 때 사용.
- 활용: @Profile("dev")라고 지정해 두면, dev 프로파일이 활성화되었을 때만 수동으로 정의된 @Bean 또는 자동으로 탐색된 @Component가 스프링 컨테이너에 등록.
- 프로파일 활성화: ① 메인 메서드가 있는 곳에서 코드로 직접 프로파일 활성화 지정 (보통 로컬 테스트용), ② 설정 파일( properties 또는 yml)에 추가, ③ 애플리케이션 실행 시점(CLI / IDE)에서 실행 옵션으로 주입하기
5. 초기화와 소멸을 담당하는 @PostConstruct와 @PreDestroy
빈의 생명주기에서 객체가 생성된 직후와 소멸되기 직전에 실행해야 하는 메서드를 정의한다.
- @PostConstruct: 의존관계 주입이 완료된 후, 초기화 작업을 수행할 메서드에 붙임.
- @PreDestroy: 스프링 컨테이너가 종료되기 직전, 자원 해제 등 소멸 콜백을 수행할 메서드에 붙임.
- 참고: 스프링 종속적이지 않은 자바 표준(Jakarta) 어노테이션.
3. 핵심 어노테이션 집중 비교/추가 설명
① @Autowired (스프링) vs @Resource / @Inject (자바/자카르타 표준)
주입하는 기준과 매커니즘에 차이가 있음
- @Autowired: 타입(Type)을 매칭 우선순위로 봄. 똑같은 타입의 빈이 여러 개 있으면 매개변수 이름이나 @Qualifier, @Primary로 해결.
- @Resource: 이름(Name)을 매칭 우선순위로 . 특정 빈 이름을 명확하게 지정해서 가져올 때 유용함.
- @Inject: @Autowired와 거의 유사하게 타입(Type) 중심으로 작동하지만, 자바 표준 규격. (사용하려면 별도의 라이브러리 의존성 추가 필요)
② @PostConstruct와 @PreDestroy
- 스프링 고유 어노테이션이 아니라 원래 자바 표준(자카르타) 어노테이션임.
- 하지만 스프링 컨테이너가 이 표준을 완벽하게 지원하기 때문에, 스프링 환경에서도 빈의 초기화와 소멸 시점에 당연하다는 듯이 이 어노테이션을 사용한다.
- 대부분 자동 구성에 사용된다.
- 이유: 내가 소스코드를 고칠 수 있는 자동 구성 클래스 내부 메서드 위에 붙여서 사용. 외부 라이브러리(내가 코드를 못 고치는 객체)를 수동 빈으로 등록할 때는 이 어노테이션을 붙일 수 없으므로, 수동 구성에서는 @Bean(initMethod = "...", destroyMethod = "...") 속성을 사용하는 것이 정석.
4. 스프링의 강력한 무기: 메타 어노테이션과 @AliasFor
스프링이 자바 표준 진영에 비해 유연하고 강력하다고 평가받는 이유 중 하나는, 어노테이션을 단순히 표시하는 용도를 넘어 재사용하고 확장할 수 있는 메커니즘을 제공하기 때문.
① 메타 어노테이션 (Meta-Annotation)
- 개념: 어노테이션 위에 붙는 어노테이션을 말함. 즉, 새로운 어노테이션을 만들 때 '기반'이 되는 어노테이션.
- 예시: @RestController 소스코드를 열어보면, 내부에 @Controller와 @ResponseBody가 메타 어노테이션으로 붙어 있음. 덕분에 @RestController 하나만 붙여도 두 가지 기능을 동시에 수행할 수 있고, 수많은 스프링 어노테이션이 이 메타 어노테이션 기법으로 겹겹이 쌓여 만들어져 있다.
② @AliasFor (별칭 지정)
- 개념: 스프링 환경에서 어노테이션의 속성(Attribute)을 서로 동의어로 매핑해주거나, 메타 어노테이션의 속성을 재정의(Override)할 때 사용하는 어노테이션.
- 💡 왜 쓸까?:
- 가독성 향상: @RequestMapping(value = "/path") 대신 @RequestMapping("/path")로 쓸 수 있는 이유가 바로 value와 path 속성이 @AliasFor로 묶여있기 때문.
- 속성 상속: 내가 커스텀 어노테이션을 만들 때, 베이스가 되는 메타 어노테이션의 특정 속성을 그대로 밖으로 노출시켜 제어하고 싶을 때 핵심적인 역할을 한.
5. 실무에서는 무엇을 써야 할까? (요약)
- 현재 모던 스프링의 대세는 @Autowired (특히 생성자 주입 + Lombok 조합):
- 이미 스프링 생태계가 공고하기 때문에, 굳이 자바 표준인 @Inject나 @Resource를 고집하기보다 스프링이 제공하는 기능과 생성자 주입 설계를 따르는 것이 가장 깔끔하고 대중적.
- Tomcat 10 / Spring Boot 3.0 이상부터는 무조건 jakarta:
- 과거 소스코드에서 import javax.annotation.PostConstruct;를 보셨다면, 최신 스프링 부트 3.x 환경에서는 import jakarta.annotation.PostConstruct;로 변경해서 사용해야 컴파일 에러가 나지 않음.
📊 스프링 빈 구성 방식별 사용 어노테이션 분류
| 수동 구성 | 자동 구성 | |
| 빈 직접 제어 (@Configuration와 @Bean) |
O | ✘ (@Component 위에는 절대 쓰지 않음) |
| 컴포넌트 스캔 (탐색 & DI) (@Component 계열) |
✘ (@Bean 메서드 안에서는 사용 안함) |
O |
| 주입(DI) 관련 (@Autowired / @Qualifier / @Primary) |
△ (@Bean 쓸 때는 메서드 매개변수로 주입받거나, 메서드를 직접 호출하는 방식으로 의존 관계 명시하는게 직관적 / 메서드 파라미터 충돌날 때 쓰긴 함) |
O (대부분 자동 구성) |
| 환경 및 설정 관련 (@Profile / @Conditional 계열) |
O (@Bean 메서드 위에도 자주 쓰임) |
O (@Component 위에도 자주 쓰임) |
| 설정 관련 (@Scope / @Lazy) | O | O |
| 생명주기 콜백 (@PostConstruct / @PreDestroy) |
△ (아래 설명) | O (대부분 자동 구성) |
💡 @Configuration 클래스에도 이미 자동으로 등록해 둔 빈(Bean)이나 다른 설정 파일에서 생성된 빈을 가져와서 조립해야 할 때 @Autowired를 사용할 수 있음
💡 외부 라이브러리(내가 코드를 못 고치는 객체)를 수동 빈으로 등록할 때는 생명주기 관련 어노테이션을 붙일 수 없음, 수동 구성에서는 @Bean(initMethod = "...", destroyMethod = "...") 속성을 사용하는 것이 정석.
'Tech Stack > Backend' 카테고리의 다른 글
| 🥾 레거시와 Spring Boot 웹 서버 구동 방식 비교 (+ REST API) (0) | 2026.06.25 |
|---|---|
| 🐱순수 Jakarta EE 서블릿 환경에서 톰캣 연동 및 구동 테스트 (0) | 2026.06.24 |
| 🥾Spring Boot 시작하기 (개발 환경 구축) (0) | 2026.06.23 |
| 🍃어노테이션 기반 자동 빈 등록(Component Scan & Autowired) (1) | 2026.06.21 |
| 🥾스프링 부트 소개: 설정의 늪에서 생산성의 혁명으로 (0) | 2026.06.19 |