소프트웨어 설계 원칙 중 하나로, 프로그램의 제어 흐름을 개발자가 아닌 프레임워크나 컨테이너가 관리하도록 하는 것을 의미합니다. 이는 객체의 생성, 초기화, 생명 주기 관리 등을 개발자가 직접 수행하지 않고, 외부 시스템이 대신 처리함으로써 구현됩니다.
프로그램의 의존성(Dependency)을 외부에 설정해줌으로써, 객체 간의 결합도를 낮추고 유연성과 재사용성을 높이는 방식입니다.
전통적인 방식
객체를 코드 내에서 직접 생성하고, 필요한 의존성을 직접 연결합니다.
이런 방식은 객체 간의 결합도가 높아져 코드 변경 시 관련된 모든 부분을 수정해야 할 수도 있습니다. 즉, 유연성과 테스트 용이성이 떨어집니다.
// 전통적인 방식: 객체를 직접 생성
ServiceA serviceA = new ServiceA();
ServiceB serviceB = new ServiceB(serviceA);
IoC
객체의 생성과 관리를 외부 컨테이너, 프레임워크가 담당하고, 필요한 객체를 주입받아 사용하게 됩니다.
객체 간의 결합도를 낮추고 유연성과 재사용성이 높아집니다.
// IoC 컨테이너가 객체 생성 및 의존성 주입을 담당
@Component
public class ServiceA {
//객체 관리와 주입은 스프링이 처리
}
IoC를 구현하는 주요 방법 중 하나가 의존성 주입(DI)입니다.
객체 간의 의존 관계를 외부 설정 파일(xml)이나 어노테이션을 통해 주입합니다.
객체간 결합도가 낮아지고, 설정 변경만으로 동작을 쉽게 변경 가능합니다.
객체간의 연결을 클래스 내부에서 직접해줄 필요 x
생성자 주입
:의존성을 생성자를 통해 주입
Setter 메서드 주입
:의존성을 Setter메서드로 주입
필드 주입
:어노테이션(@Autowired)을 통해 필드에 직접 주입
// 생성자 주입 예
@Component
public class ExampleA {
private final ExampleB exampleB;
@Autowired
public ExampleA(ExampleB exampleB) {
this.exampleB = exampleB;
}
}
Bean
스프링 IoC 컨테이너에 의해 관리되는 객체를 의미
XML 설정파일
외부 xml 파일에서 Bean을 정의하고 의존성 설정
<bean id="exampleA" class="com.example.ExampleA">
<property name="exampleB" ref="exampleB" />
</bean>
어노테이션 설정
@Component, @Service, @Controller, @Repository 등의 어노테이션을 사용하여 Bean 등록.
@Component
public class ExampleA {
}
Java Config 설정
@Configuration 클래스와 @Bean 메서드를 사용.
@Configuration
public class AppConfig {
@Bean
public ExampleA exampleA() {
return new ExampleA(exampleB());
}
}
싱글톤 Bean
대부분의 Bean은 싱글톤으로 관리되며, 한 번 생성된 Bean은 애플리케이션 내에서 재사용됩니다.
Thread Safety
Bean 자체는 스레드 세이프하지만, 내부 상태는 공유되지 않도록 신중히 관리해야 합니다.