IoC / DI 핵심 개념
스프링의 가장 중요한 기반 기술은 IoC(Inversion of Control) 과 DI(Dependency Injection) 이다. 애플리케이션의 객체 생성·초기화·연결을 개발자가 아닌 스프링 컨테이너가 관리한다는 데 의미가 있다.
1. IoC(Inversion of Control)
IoC는 말 그대로 제어의 역전이다.
원래 객체 생성과 의존성 연결은 개발자 코드가 직접 수행하는 것이 일반적이지만,
스프링에서는 이 제어 흐름을 모두 컨테이너에게 위임한다.
IoC의 핵심은 다음 두 구조로 구현된다.
- BeanFactory (가장 기본적인 IoC 컨테이너)
- ApplicationContext (BeanFactory 기능 + 다양한 부가 기능)
애플리케이션 구동 시, 스프링은 BeanDefinition을 읽고 Bean을 생성하여 컨테이너에 등록한 뒤 관리한다.
2. BeanFactory vs ApplicationContext
스프링 컨테이너는 크게 아래 두 종류로 나뉜다.
BeanFactory
- 스프링 IoC의 최소 기능 제공
- 지연 초기화(Lazy init) 중심
- 거의 직접 사용할 일이 없다
ApplicationContext
- 실무와 스프링부트에서 사용하는 핵심 컨테이너
- BeanFactory 확장판
- 메시지 소스, 이벤트 발행, AOP, 환경 정보 등 다양한 기능 포함
스프링 부트에서는 환경(web/non-web)에 따라
ApplicationContext의 여러 구현체(예: AnnotationConfigServletWebServerApplicationContext)를 사용하지만,
개발자는 보통 이를 단순히 ApplicationContext로 취급하면 된다.
3. DI(Dependency Injection)
DI는 객체 간 의존성을 생성자나 메서드를 통해 주입하는 패턴이다.
스프링에서는 아래 3가지 스타일을 지원한다.
- 생성자 주입(권장)
- 필드 주입(비권장)
- Setter 주입(옵션 의존성에 활용)
4. DI 방식별 비교
4.1 생성자 주입 (가장 권장)
@Service
public class OrderService {
private final UserRepository userRepository;
public OrderService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}장점:
- 불변(immutability) 보장
- 테스트 용이
- 의존성 누락을 컴파일 타임에 방지
- Lombok
@RequiredArgsConstructor와 궁합이 좋음
4.2 필드 주입 (권장 X)
@Service
public class OrderService {
@Autowired
private UserRepository userRepository;
}단점:
- DI 프레임워크 없이는 테스트 불가능
- 의존성이 숨겨져 구조 파악 어려움
- 순환 참조 문제를 더 쉽게 유발
4.3 Setter 주입 (옵션 의존성에 적합)
@Service
public class OrderService {
private UserRepository userRepository;
@Autowired
public void setUserRepository(UserRepository repository) {
this.userRepository = repository;
}
}장점:
- 런타임 조건에 따라 의존성 갈아끼우기 가능
- 선택적/optional 한 의존성에 맞음
5. @ComponentScan — 자동 Bean 등록
스프링은 classpath 상의 특정 패키지를 스캔하여 자동으로 Bean을 등록한다.
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}@SpringBootApplication 은 내부적으로 다음을 포함한다.
@ComponentScan→ 자동 스캔@Configuration→ 설정 파일@EnableAutoConfiguration
스캔 대상 애노테이션:
@Component@Service@Repository@Controller@RestController
6. @Bean — 수동 Bean 등록
자동 스캔이 아닌, 개발자가 명시적으로 Bean을 등록하는 방식.
@Configuration
public class AppConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}위와 같이 등록하면 빈 이름은 기본적으로 메서드명이 된다.
7. 컴포넌트 스캔 흐름 정리
1. @ComponentScan 선언
2. 지정된 패키지를 classpath scanning
3. @Component 계열 애노테이션이 붙은 클래스 탐색
4. BeanDefinition 생성
5. ApplicationContext 초기화 시점에 Bean 생성8. 실무 기준 핵심 정리
- DI는 생성자 주입이 기본 원칙이다.
- ApplicationContext가 IoC 컨테이너의 표준 구현이다.
- 컴포넌트 스캔 + 수동 Bean(@Bean) 조합으로 유연한 구조를 설계한다.
- 필드 주입은 테스트 어려움과 구조 불명확성 때문에 피해야 한다.
- IoC/DI는 스프링 전체 생태계의 기반이며, 이후 AOP, 트랜잭션 등도 모두 이 위에서 동작한다.
Last updated on