Spring IoC (Inversion of Control, 제어의 역전)
- IoC(Inversion of Control)는 객체의 생성과 생명 주기 관리, 의존성 주입 등을 Spring 프레임워크가 개발자를 대신해서 처리하는 개념이다. 자주 사용되는 객체는 Bean으로 등록하여 사용할 수 있고, 개발자는 이러한 객체들의 생명 주기를 관리할 필요가 없다. 이를 통해 개발자는 객체 생성과 관리에 신경 쓸 필요 없이, 비즈니스 로직에 더 집중할 수 있게 된다.
- IoC 컨테이너: 객체의 생성, 초기화, 의존성 주입, 소멸을 관리하는 곳으로, 설정된 Bean을 자동으로 생성하고, 의존 관계를 처리한다. BeanFactory 인터페이스는 각종 객체(Bean)을 관리하는 매커니즘을 제공하고, ApplicationContext 인터페이스는 BeanFactory의 하위 인터페이스로 더 쉬운 AOP 기능과의 통합, 국제화 메시지 리소스 처리 등 엔터프라이즈별 기능이 추가되었고 Spring에서 가장 일반적으로 사용되는 IoC 컨테이너이다.
IoC컨테이너의 동작 과정
- 객체 생성: Spring IoC 컨테이너는 @Configuration, @Component, @Service 등의 애노테이션이나 설정 파일을 통해 Bean 정의를 읽고, 메모리에 로딩하여 인스턴스화한다.
- 의존성 주입: 생성된 Bean에 필요한 의존성을 자동으로 주입한다. 주입 방식은 생성자, 세터, 필드 주입 방식 중 하나이다.
- 초기화: Bean의 초기화 메서드(@PostConstruct)가 실행된다. 이를 통해 Bean이 필요로 하는 초기화 작업이 수행된다.
- 사용: 생성된 Bean은 애플리케이션의 비즈니스 로직에서 자유롭게 사용된다.
- 소멸: 애플리케이션이 종료되거나 Bean이 더 이상 필요하지 않을 때, IoC 컨테이너는 소멸 메서드(@PreDestroy)를 호출해 Bean을 정리한다.
DI (Dependency Injection, 의존성 주입)
- DI(Dependency Injection)는 IoC 개념을 실현하는 기능으로, 의존성 주입을 통해 객체 간의 결합도를 낮추고, 유연한 코드를 작성할 수 있게 한다. DI를 통해 객체는 필요한 의존성을 직접 생성(new)하는 대신, Spring IoC 컨테이너로부터 주입받는다. 이렇게 주입받음으로써 객체 간 강한 결합도를 피하고, 테스트 가능성이 높아지며, 유연한 변경이 가능하게 된다.
DI 방식은 생성자 주입, 세터 주입, 필드 주입으로 구분되며, 일반적으로 생성자 주입이 권장된다. 이러한 방식 중 한가지를 택해 코드를 작성하면, IoC 컨테이너는 런타임 시점에 필요한 객체를 자동으로 주입한다.
DI 방식
1. Setter-based Injection (세터 주입)
세터 메서드를 통해 의존성을 주입한다.
@Service
public class UserService {
private UserRepository userRepository;
@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
2. Constructor-based Injection (생성자 주입)
생성자를 통해 의존성을 주입한다. Spring에서는 @Autowired
가 없어도 자동으로 생성자를 통해 의존성을 주입할 수 있으며, final
키워드를 사용하여 불변성을 보장할 수 있다.
// Case 1. 일반적인 생성자 주입
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
// Case 2. Lombok의 @RequiredArgsConstructor로 간편하게 생성자 주입을 설정
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
}
3. Field-based Injection (필드 주입)
필드에 직접 주입하는 방식으로, Spring에서 권장되지 않으며 현시점 Spring 공식 문서의 의존성 주입 파트에 존재하지 않는다.
대표적인 문제점은 아래와 같다.
- 필드에 final 선언이 불가하여 불변 객체로 관리할 수 없다.
- 필수적인 종속성임에도 제대로 초기화되지 않을 수 있음(컨테이너 밖에서 new 키워드를 통한 객체 생성 시)
- 순환 종속성을 감지하기 어려움
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
}
😎 Constructor-based Injection(생성자 주입) VS Setter-based Injection(세터 주입)
Spring 공식 문서에 따르면, 생성자 주입과 세터 주입은 혼합하여 사용할 수 있으며, 다음과 같은 기준을 따르는 것이 권장된다.
- 필수 의존성에는 생성자 주입을 사용한다.
- 선택적 의존성(예: 환경에 따른 설정, 외부 서비스 사용 여부에 따른 설정 등)에는 세터 주입을 사용한다.
일반적으로는 생성자 주입 방식이 더 권장되는데, 그 이유는 불변성 보장, null-safety 등의 장점을 제공하기 때문이다. 반면, 세터 주입 방식은 객체 생성 후에도 의존성을 재설정할 수 있는 유연성을 제공하지만, null 값을 수동으로 체크해야 하거나 객체의 상태가 변경될 수 있는 단점이 존재한다.
따라서, 필수 의존성을 처리할 때는 생성자 주입을 사용하고, 선택적 의존성이나 유연한 재설정이 필요한 경우에는 세터 주입을 사용하는 것이 적합하다.
Reference
- Spring Framework Reference Doc.
- Baeldung - Why Is Field Injection Not Recommended?
- 도서 - 자바 웹을 다루는 기술(이병승 저)
- [백기선 스프링 프레임워크 핵심기술] IOC Container and Bean
- 스프링 프레임워크 강의 4강 - IoC(Inversion Of Control) 컨테이너
'Spring' 카테고리의 다른 글
Spring의 DispatcherSevlet은 무슨 일을 할까? (1) | 2024.11.05 |
---|---|
SpringBoot프로젝트를 gRPC 서버/클라이언트로 만들어보자!💫 (SpringBoot3 + gRPC) (0) | 2024.10.01 |
Spring Boot 프로젝트 시작 시 Group Id, Artifact Id는 어떻게 설정할까?😯 (0) | 2024.09.21 |
RedisTemplate 객체 주입 문제 해결하기 (0) | 2024.09.12 |
댓글