Spring을 배우기 전에는 보통 객체를 직접 생성해서 사용했다
예를 들어 서비스 클래스에서 필요한 저장소 객체를 직접 new로 생성하는 방식이다
public class ScheduleService {
private ScheduleRepository scheduleRepository = new ScheduleRepository();
}
이 방식은 처음에는 단순해 보이지만 문제가 있다
서비스가 저장소를 직접 생성하면 두 클래스가 강하게 연결되어 높은 결합도를 가지게된다
나중에 저장소 구현이 바뀌거나 테스트를 위해 다른 객체로 교체하고 싶어도 코드를 직접 수정해야 한다
즉, 객체 생성과 사용의 책임이 한 클래스 안에 같이 들어 있어서 유연성이 떨어진다
Spring은 이런 문제를 해결하기 위해 IoC(Inversion of Control) 와 DI(Dependency Injection) 를 사용한다
이 두 개념은 Spring 프레임워크의 핵심이라고 볼 수 있다
IoC는 말 그대로 제어의 흐름이 뒤집힌 것이다
기존에는 개발자가 객체를 직접 생성하고, 필요한 시점에 직접 호출하고, 생명주기를 관리했다
하지만 Spring에서는 이 제어권을 개발자가 아니라 Spring 컨테이너가 가져간다
즉, 객체를 언제 만들지, 어떤 객체를 사용할지, 어떤 객체를 서로 연결할지에 대한 제어권이
개발자 코드에서 Spring으로 넘어간다
핵심 의미
정리하면 IoC는 다음 의미를 가진다
public class ScheduleService {
private ScheduleRepository scheduleRepository = new ScheduleRepository();
}
이 경우 ScheduleService가 ScheduleRepository를 직접 생성한다
즉, ScheduleService 제어권을 가지고 있다
ex)
IoC가 적용된 경우
public class ScheduleService {
private final ScheduleRepository scheduleRepository;
public ScheduleService(ScheduleRepository scheduleRepository) {
this.scheduleRepository = scheduleRepository;
}
}
여기서는 ScheduleService가 ScheduleRepository를 직접 생성하지 않는다.
단지 나는 ScheduleRepository가 필요하다! 라고 표현한다
실제로 객체를 만들어서 넣어주는 일은 Spring이 한다
이것이 바로 제어의 역전이다
개념
DI는 필요한 객체(의존 객체)를 직접 생성하지 않고 외부에서 주입받는 것이다
여기서 의존성(Dependency)이란 어떤 클래스가 동작하기 위해 반드시 필요한 다른 객체를 뜻한다
예를 들어 ScheduleService가 ScheduleRepository 없이는 제대로 동작할 수 없다면
ScheduleService는 ScheduleRepository에 의존한다고 말한다
Spring에서는 이 의존 객체를 외부(Spring 컨테이너)가 만들어서 넣어준다
이 과정을 의존성 주입이라고 한다
IoC와 DI의 관계
이 둘은 자주 같이 나오지만 완전히 같은 말은 아니다
IoC: 제어권을 Spring에게 넘긴다는 큰 개념
DI: 그 제어권을 실제로 구현하는 대표적인 방법
즉, DI는 IoC를 실현하는 방법 중 하나라고 이해하면 된다

이미지출저:https://isoomni.tistory.com/entry/TISPRING-IOC-DI-%EC%A0%95%EC%9D%98-%EC%9E%A5%EC%A0%90
Spring에서 의존성을 주입하는 방식은 대표적으로 3가지가 있다
가장 많이사용하고 권장되는 방식이다
@Service
public class ScheduleService {
private final ScheduleRepository scheduleRepository;
public ScheduleService(ScheduleRepository scheduleRepository) {
this.scheduleRepository = scheduleRepository;
}
}
장점
Spring에서는 생성자가 하나만 있으면 @Autowired를 생략해도 자동 주입된다
@Autowired
private UserRepository userRepository;
간단해 보이지만 권장되지 않는다
@Autowired를 필드에 직접 선언하여 의존성을 주입받는 방식
대표적으로 final 키워드 사용이 불가능하기 때문에 변수에 할당된 객체를 아래의 코드 예처럼 null 혹은 다른 객체로 바꿀 수 있어 안전하지 않다
private UserRepository userRepository;
@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
선택적으로 바뀔 수 있는 의존성에는 사용할 수 있지만
필수 의존성은 보통 생성자 주입을 더 많이 사용한다
IoC 개념을 실제로 수행하는 주체가 바로 IoC 컨테이너이다
IoC 컨테이너는 객체를 생성하고, 저장하고, 관리하고, 의존성을 주입해주는 Spring의 핵심 구성요소이다
Spring에서 가장 대표적인 IoC 컨테이너는 ApplicationContext이다
역할
IoC 컨테이너는 다음과 같은 일을 한다
개발자가 new 하지 않아도 컨테이너가 객체를 생성한다
생성한 객체를 내부에 보관한다
어떤 Bean이 어떤 Bean을 필요로 하는지 파악해서 연결해준다
객체 생성부터 초기화, 사용, 소멸까지 관리한다
이벤트 처리, 메시지 처리, 환경 설정, AOP 기반 기능 등도 지원한다
Bean은 Spring 컨테이너가 생성하고 관리하는 객체이다
단순히 클래스라고 해서 Bean이 되는 것이 아니라
Spring이 인식해서 등록하고 관리할 때 그 객체를 Bean이라고 부른다
Bean은 그냥 객체보다 한 단계 더 특별한 존재다
Spring 생태계 안에서 공식적으로 관리받는 객체라고 볼 수 있다
객체와 빈의 차이
| 구분 | 일반 객체 | Bean |
|---|---|---|
| 생성 | 내가 new | 스프링 |
| 관리 | 내가 | 스프링 |
| 생명주기 | 없음 | 있음 |
| DI 가능 | ❌ | ✅ |
정리