IoC(제어의 역전)은 프로그램에서 필요한 객체를 생성하고 메소드를 호출하는 등의 제어권을 외부(Spring)에 넘기는 것을 뜻한다.
Java 같은 기존의 프로그램에서는 사용자, 즉 개발자가 각 객체를 생성하고 흐름의 결정하거나 메소드를 호출하는 작업을 하며 의존관계가 필요한 것들을 일일히 넣어줘 개발자가 모든 흐름을 제어하는 방식이었다.
그러나 개발자가 모든 것을 제어하기 때문에 실수가 일어나기 쉽고, 의존 관계가 복잡한 객체를 변경하게 되면 연쇄되는 파급 효과가 크다는 문제점이 있었다.
스프링은 IoC가 적용된 프로그램으로 객체의 생성 및 생명주기와 객체들 간의 의존성을 IoC 컨테이너에서 관리하며, 개발자는 단지 필요한 비즈니스 로직만 개발하면 프레임워크가 내부에서 정한 흐름대로 프로그램을 구동시킨다.
DI 한국말로는 의존성 주입이라고하며 구성요소 간의 의존 관계가 소스 코드 내부가 아닌 외부의 설정을 통해 정의되는 방식을 뜻한다.
예시
class Programmer {
private Coffee coffee;
public Programmer() {
this.coffee = new Coffee();
}
public startProgramming() {
this.coffee.drink();
...
}
}
Programmer
클래스에서 startProgramming
함수 호출을 위해서는 Coffee
클래스가 필요로 하며 이 때 Programmer
클래스는 Coffee
클래스의 의존성을 가진다고 한다.
의존성을 가진다는 것은 Coffee
클래스 코드가 수정된다면 Programmer
클래스의 코드도 수정해줘야하는 문제 즉, 결합도가 높아지는 것을 의미한다.
만약 위에서 Coffee
클래스를 상속 받은 Americano
클래스를 추가하고자 한다면 DI를 사용하지 않을 때와 할 때는 다음과 같다.
직접 수정
class Coffee {...}
// Coffee 클래스 상속
class Americano extends Coffee {...}
// Programmer.java
class Programmer {
private Coffee coffee;
public Programmer() {
this.coffee = new Americano(); // 직접 수정
}
...
}
이럴 때는 Coffee
클래스를 사용하는 클래스가 100개 다 Americano
를 상속받아야 한다면 일일이 직접 수정을 해줘야하며 이것은 매우 비효율적이다.
DI(의존성 주입) 이용
class Programmer {
private Coffee coffee;
public Programmer(Coffee coffee) {
this.coffee = coffee;
}
public startProgramming() {
this.coffee.drink();
...
}
}
의존하는 클래스를 직접 생성하지 않고 주입해줘서 객체 간의 결합도를 줄이고 코드의 재사용성을 줄일 수 있게 된다.
요약
- DI는 필요한 객체를 직접 생성하지 않고 외부로부터 필요한 객체를 주입 받아 사용하는 것
- DI를 통해 객체간의 의존성(결합도)을 줄이고 코드의 재활요성을 높임
IoC(제어의 역전)으로 인해 객체에 대한 권한이 스프링에 위임되어 스프링 컨테이너가 관리하는 자바 객체를 빈(Bean)이라고 부른다.
스프링 컨테이너는 호출될 때마다 다른 객체를 생성하는 것이 아닌 같은 공유 객체를 생성하여 사용하는 싱글톤 컨테이너이기 때문에 스프링을 통해 주입받은 Bean은 동일한 객체라는 가정하에 개발을 해야한다.
@Configuration & @Bean
: 자바 클래스에 @Configuration을, 메서드에 @Bean을 사용
: 자바 기반 설정방식으로 스프링 부트에서 많이 활용
@Component
: @Component가 부여된 클래스를 @ComponentScan로 정해진 범위에서 탐색해 DI 컨테이너서 Bean을 자동으로 등록하게 만든다
: 애너테이션 기반 설정방식