Spring 으로 개발을 할 때 @Bean
과 @Component
의 차이를 명확하게 알고 사용한 게 아니었다는 걸 알고
정리를 하고자 오랜만에 글을 작성합니다.
먼저 @Bean
과 @Component
의 차이를 다루고,
@Component
에 대한 Spring 공식문서를 읽으며 정리하고 마무리하겠습니다.
두 애노테이션 모두 Spring Context에 해당 객체를 빈(Bean)으로 등록하기 위해 사용한다는 점은 공통점입니다.
차이점은 1. 용도와 2. 제한이 있습니다.
1. 용도
개발자가 통제하지 못하는 외부 라이브러리를 가지고 와서 빈으로 등록하려고 할 때, @Bean
을 사용할 수 있습니다.
반면, 개발자가 직접 구현한 클래스를 빈으로 등록하려 할 때, @Component
를 사용할 수 있습니다.
결국,
둘다 스프링의 의존성 주입 (Dependency Injection)를 이용하는 방식입니다.
@Bean
은 (코드 수정이 불가능한 클래스에 대해) setter나 builder 등을 통해서 사용자가 프로퍼티를 변경하여 생성한 클래스의 인스턴스를 빈으로 등록할 때 사용합니다.
@Component
는 (일반적으로 개발자가 작성한 코드에 대해) 스프링이 로드될 때,
일반적으로 개발자가 컨트롤이 불가능한 외부 라이브러리 사용시 @Bean
사용, 개발자가 직접 컨트롤이 가능한 내부 클래스는 @Compoenent
사용 하여 스프링이 로드되는 시점에, 클래스의 인스턴스를 생성한 후에 빈으로서 등록합니다.
2. 제한
두 애노테이션의 구현 코드를 보겠습니다.
각 애노테이션은 선언할 수 있는 타입이 정해져있기 때문에 애노테이션이 달 수 있는 곳이 제한되어있습니다.
Bean 애노테이션은 ElementType.METHOD 👉 메서드에 달 수 있습니다.
@Bean
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {
}
Component 애노테이션은 ELementType.TYPE 👉 클래스에 달 수 있습니다.@Component
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {
}
만약, @Component
를 메서드에 달면, 컴파일 에러가 발생합니다.
간단히 요약하면 위의 이야기이고, Spring 실습 docs를 살펴보며 더 구체적으로 이야기해보려합니다.
Baeldung - Spring Component annotation 문서
1) Spring ApplicationContext
Spring ApplicationContext를 먼저 이해해야 @ComponentScan
을 이해할 수 있습니다.
Spring ApplicationContext는 Spring이 자동으로 관리하고 배포하는 것으로 식별된 객체의 인스턴스를 보유하는 곳입니다.
그리고 이 인스턴스들을 빈(Bean)이라고 합니다.
빈 관리와 의존성 주입(Dependency Injection) 기회는 Spring의 몇몇 주요 특징입니다.
제어의 역전(IoC, Inversion of Control) 원리를 사용하면, Spring은 우리의 애플리케이션으로부터 빈 인스턴스를 모으고, 적절한 시점에 그것들을 사용합니다.
우리는 따로 세팅하고 이 객체들을 인스턴스화하지 않아도, 스프링에 대한 빈 의존성을 볼 수 있습니다.
@Autowired
와 같은 애노테이션을 사용해서 Spring에 의해 관리되는 빈을 우리의 애프리케이션에 삽입하는 기능은 스프링에서 매우 강력하고 확장성있는 코드를 만들도록 해줍니다.
그래서 우리는 관리해주길 원하는 빈에 대해 스프링에게 어떻게 말해야 할까요?
우리는 우리의 클래스 위에 stereotype 애노테이션을 사용함으로써, '자동 빈 감지'라는 스프링의 이점을 사용해야합니다.
2) @Component
@Component
애노테이션은 우리가 커스텀한 빈을 Spring이 자동으로 감지(detect)하도록 해주는 애노테이션입니다.
그래서 우리는명시적으로 작성하지 않아도, 스프링이 아래와 같은 것을 해줍니다.
@Component
애노테이션이 붙은 클래스들에 대해 애플리케이션에서 스캔합니다.- 그것들을 인스턴스화하고 지정된 족성성을 주입합니다.
- 그것들을 필요로 하는 어디든 주입합니다.
그러나, 대부분의 개발자들은 더욱 구체화된(specialized) stereotype 애노테이션을 사용하여 해당 기능을 제공받길 선호합니다.
3) Spring의 Stereotype 애노테이션들
Spring은 @Controller
, @Service
, @Repository
와 같이 구체화된(Specialized) stereotype 애노테이션을 제공합니다.
위 애노테이션들의 구현 코드를 보면, 메타 애노테이션으로 @Component
도 함께 구성되어있기 때문에
결국 @Component
와 같은 역할을 제공하고 똑같이 행동합니다.
@Service
애노테이션을 예로 보면 @Component
가 붙은 것을 확인할 수 있죠!
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
}
해당 애노테이션들은 @Component 의 별칭이라고 봐도 좋을 것 같습니다.
4) @ComponentScan
애노테이션은 단지 Bean들을 다른 객체(도메인과 같은)와 구분하는 목적으로 사용되지만,
Spring은 @ComponentScan
으로 빈들을 ApplicationContext로 모으기 위해 사용합니다.
(사실 @ComponentScan
으로 인해 빈이 발견되지 않는다면 @Component
는아무것도 할 수 없습니다.)
프로젝트의 루트 클래스에 붙이는 @SpringBootApplication
은 @ComponentScan
을 포함하는 주석입니다. 그렇기 때문에 기본적으로 Spring은 우리가 정의한 모든 @Component
를 스캔합니다.
그러나, 해당 애노테이션이 루트 클래스에 두지 않거나 외부 소스를 스캔하려는 경우,
우리가 @Component 로 지정한 패키지가 클래스 경로에 존재한다면, @ComponentScan
에 추가적으로 지정할 수 있습니다.
// 1. 외부에 위치한 클래스에 @Component 애노테이션을 붙입니다.
package com.baeldung.component.scannedscope;
@Component
public class ScannedScopeExample {
}
// 2. Application의 메인 클래스에 @ComponentScan 애노테이션에 추가로 탐색하고 싶은 패키지를 지정합니다.
package com.baeldung.component.inscope;
@SpringBootApplication
@ComponentScan({"com.baeldung.component.inscope", "com.baeldung.component.scannedscope"})
public class ComponentApplication {
//public static void main(String[] args) {...}
}
5) @Component의 제한
그리고 @Component
를 사용하지 못하는 경우도 있습니다.
- 프로젝트 외부 패키지에 @Component 애노테이션을 추가하는 경우
- 외부 소스(third-party source)에서 제공되는 소스 코드에 접근할 수 없는 경우
- 실행 중인 환경에 따라 (조건에 따라) 하나의 빈 구현(implementation)을 다른 구현보다 사용하길 원하는 경우
대부분의 경우 자동 감지가 충분히 일어나지만, 그렇지 않은경우 @Bean
을 사용해야 합니다.
6) @Component vs @Bean
@Bean
또한 스프링이 런타임에 빈들을 모으는데 사용하는 애노테이션입니다.
하지만, 클래스 레벨에서 사용되지 않고, 메서드에 추가해서 Spring이 메서드의 결과를 스프링 빈으로서 저장하게 됩니다.
이제 두 애노테이션의 차이를 표로 다시 정리를 해보면 아래와 같습니다.
@Component | @Bean |
---|---|
클래스 레벨 애노테이션 | 메서드 레벨 애노테이션 |
클래스의 소스코드가 수정할 수 있을 때만 @Component 가능 | 어디서든 사용할 수 있지만, 좀 더 장황합니다. |
Spring의 자동 감지와 호환됩니다. | 수동으로 클래스를 인스턴스화하여 반환하는 것이 필요합니다. |
클래스를 정의하는 것과 인스턴스화하는 것이 분리됩니다. (사실 그래서 서드파티 코드를 Spring의 빈으로 만들 수 있는 이유이죠!) | |
빈이 사용할 수 있는 여러 인스턴스 옵션을 결정하는 논리를 도입할 수 있음을 의미합니다. (해당 클래스 내 설정 메서드로 커스텀할 수 있게됩니다.) |
도움이 되었다면 좋아요 부탁드려요💙
피드백은 환영입니다!
'백엔드 개발하며 작성한 > Spring' 카테고리의 다른 글
[Spring Batch] 1. 스프링 배치 공부 순서와 간단 소개 (0) | 2023.06.11 |
---|---|
Spring Framework와 Spring Boot (0) | 2022.08.01 |
@RequestBody 붙였어?? (0) | 2022.07.13 |
Spring Boot에서 Connection Pool 이용하기 (HikariCP) (0) | 2022.04.10 |
Filter와 OncePerRequestFilter (0) | 2022.02.22 |