
그림으로 배우는 스프링 6을 기반으로 내용을 정리하며 추가한 내용입니다.
스테레오타입 어노테이션은 Bean으로 관리하고 싶은 구체적인 클래스에 붙이는 어노테이션으로, 스프링에서 사용하는 고유한 용어.
- 스테레오타입 어노테이을 붙이면 스프링이 이를 감지해서 구상 클래스의 생성자를 호출하고 객체를 생성한다. 생성된 객체는 Bean으로 관리해준다.
스테레오 어노테이션은 구상 클래스의 역할에 따라 구분해서 사용한다.
- @Service와 @Component는 부가 기능이 없기 때문에, 코드의 가독성을 위해
Service클래스에는 @Service를,Component클래스에는 @Component를 사용하는 것이 권장된다. 이렇게 하면 소스 코드에서 클래스의 역할을 명확히 알 수 있어 유지보수가 용이하다.- 스테레오 어노테이션을 사용하게 되면 DI컨테이너를 생성하면 각 클래스의 생성자가 DI 컨테이너에 의해 호출되어 객체가 생성됨.
| 스테레오타입 애너테이션 | 설명 |
|---|---|
| @Service | Service 계층의 구성 클래스에 붙이며, 비즈니스 로직을 담는 역할을 한다. 부가 기능은 없다. |
| @Repository | Repository 계층의 구성 클래스에 붙이며, 데이터베이스 접근을 담당한다. 부가 기능으로, 데이터베이스 접근 시 발생하는 예외를 스프링이 제공하는 예외로 자동 변환해주는 기능을 제공한다. |
| @Controller | Controller 계층의 구성 클래스에 붙이며, 웹 요청을 처리하는 역할을 한다. 부가 기능으로 스프링 MVC 기능을 사용할 수 있다. |
| @Component | 역할을 나타내지 않는 범용적인 스테레오타입 애너테이션으로, 특정 계층에 속하지 않는 일반적인 빈(Bean) 정의 시 사용한다. 부가 기능은 없으며, 구성 클래스의 역할이 위의 세 가지(@Service, @Repository, @Controller)에 해당하지 않을 때 주로 사용된다. |
스테레오 어노테이션을 객체를 생성해도 의존 객체 인젝션이 이루어지지 않았기에, DI컨테이너 인젝션을 요청하려면 @Autowired 를 사용해야한다.
@Autowired는 인젝션을 지시하는 어노테이션이다.
| 구분 | 생성자 주입 | @Autowired 필드 주입 |
|---|---|---|
| 장점 | - 불변성 보장 (final 필드 사용 가능) | - 코드가 간결함 |
| - 주입할 의존성을 명확히 보여주어 가독성 향상 | - 간단한 테스트에는 편리 | |
| - 테스트가 용이함 (Mock 주입 가능) | - 수동으로 빈을 주입할 경우 용이 | |
| - 의존성 주입 누락 방지 가능 (컴파일 시점 확인 가능) | ||
| 단점 | - 필드가 많은 경우 코드가 길어질 수 있음 | - 필드 주입은 불변성 보장이 어려움 |
| - 의존성이 많을 경우 생성자 인자가 많아져 가독성 저하 | - 테스트하기 어려움 (Mock 객체 주입이 복잡함) | |
| - DI 컨테이너가 없으면 객체 생성 어려움 | ||
| 권장 여부 | 권장 (스프링 공식 권장 방식) | 권장하지 않음 (테스트와 유지보수의 어려움) |
- 스테레오타입 애너테이션을 클래스에 붙이기: @Service, @Repository 등으로 클래스의 역할을 지정합니다.
- 주입할 대상 클래스 선언: 필요한 클래스를 @Autowired나 생성자 주입( priavet final 생성자명 )으로 주입합니다.
- 스프링이 관리하는 빈으로 등록된 객체 사용: 스프링이 빈으로 관리하는 인스턴스를 자동으로 주입해 사용하게 됩니다.
예시코드
@Service public class MyService { private final MyRepository myRepository; // 생성자 주입 방식 (권장) public MyService(MyRepository myRepository) { this.myRepository = myRepository; } public void performAction() { myRepository.saveData(); } } @Repository public class MyRepository { public void saveData() { // 데이터 저장 로직 } }
스프링에서 컴포넌트 스캔(Component Scan) 은 @Service, @Repository, @Controller, @Component와 같은 스테레오타입 애노테이션이 붙은 클래스들을 자동으로 탐색하고, 이를 빈(Bean) 으로 등록하여 관리하는 중요한 과정입니다.
- 단순히 스테레오타입 애노테이션을 붙이는 것만으로는 해당 클래스가 스프링 빈으로 관리되지 않으며, 반드시 컴포넌트 스캔이 이루어져야 합니다.
![]()
컴포넌트 스캔의 필요성
- 자동화된 의존성 관리
컴포넌트 스캔을 통해 스프링이 자동으로 필요한 빈을 관리하고, 빈들 간의 의존성을 주입합니다. 이로 인해 개발자는 수동으로 객체를 생성하고 주입하는 번거로움을 덜 수 있습니다.- 프로젝트 유지보수성 향상
컴포넌트 스캔을 통해 각 클래스가 빈으로 관리되므로, 코드 가독성이 좋아지고, 클래스의 역할에 따른 명확한 구조가 유지됩니다. 이를 통해 팀 간 협업 시 클래스의 역할과 위치를 쉽게 파악할 수 있습니다.
컴포넌트 스캔을 지시하는 법 -> 패키지의 하위 패키지를 포함하여 DI 컨테이너가 스테레오 어노테이션이 붙어있는 구상 클래스를 찾아줌.
베이스 패키지 지정을 하여 하는 법
#자동으로 지정
package com.example.shopping;
@Configuration
@ComponentScan
public class ShoppingApplication {
public static void main(String[] args) {
#베이스 패키지 지정
@Configuration
@ComponentScan("com.example.shopping")
public class ShoppingApplication {
public static void main(String[] args) {
스프링에서는 애플리케이션이 실행될 때, 의존성 주입(DI)을 관리하는 DI 컨테이너가 생성됩니다. 이 컨테이너는 빈(Bean) 을 관리하고, 애플리케이션 전반에서 필요한 객체의 생명 주기와 의존성을 제어합니다. DI 컨테이너를 생성하는 대표적인 방법 중 하나로 AnnotationConfigApplicationContext 클래스를 사용하며, 이는 Java Config 클래스를 통해 설정된 빈들을 컨테이너에 등록하여 관리합니다.
ApplicationContext context = new AnnotationConfigApplicationContext(ShoppingApplication.class);
AnnotationConfigApplicationContext 클래스
AnnotationConfigApplicationContext 클래스는 애플리케이션에 필요한 빈들을 생성하고 관리하는 DI 컨테이너의 일종입니다.
Java Config 클래스 로드
생성자의 인수로 전달된 Java Config 클래스(ShoppingApplication.class)를 로드하고, 해당 설정을 기반으로 DI 컨테이너를 초기화합니다.
컴포넌트 스캔 및 빈 등록
Config 클래스에 지정된 컴포넌트 스캔 설정을 통해 스테레오타입 애노테이션(@Component, @Service, @Repository, @Controller)이 붙은 클래스를 스캔하고, 이를 빈으로 등록합니다.
빈 관리
DI 컨테이너는 의존성을 가진 객체들 간의 관계를 관리하고, 필요한 곳에 자동으로 주입하여 사용할 수 있도록 합니다. 또한, 빈의 생명 주기(lifecycle)를 관리하여 객체를 재사용 가능하게 합니다.
아래의 3가지 방법이 있어도, 권장은 생성자 인젝션 final 한정자를 붙일 수 있음. 위에서 말했듯이 final 필드 내용을 변경하지 못하게 하기 때문.
@Service
public class TrainingServiceImpl implements TrainingService {
private TrainingRepository trainingRepository;
// 생성자 주입 방식
@Autowired // 생략가능
public TrainingServiceImpl(TrainingRepository trainingRepository) {
this.trainingRepository = trainingRepository;
}
@Service
public class TrainingServiceImpl implements TrainingService {
private TrainingRepository trainingRepository;
@Autowired
public void setTrainingServiceImpl(TrainingRepository trainingRepository) {
this.trainingRepository = trainingRepository;
}
@Service
public class TrainingServiceImpl implements TrainingService {
@Autowired
private final TrainingRepository trainingRepository;
오류를 피하는 방법 : 프로파일을 사용하는 방법을 이용하면 됨
그림출처 : 그림으로보는 스프링6
공부해야할 것 정리 : Bean Lifecycle