의존성이 삽입된다는 의미로 IoC를 DI라는 표현으로 사용한다.
Dependency(의존성)
하나의 모듈(클래스)이 바뀌면 다른 모듈까지 변경이 이루어지기 때문에 Dependency는 위험하다. 테스트 가능한 어플을 만들때 의존성이 있으면 유닛테스트 작성이 어렵다.
"A가 B를 의존한다."는 말은 의존 대상 B가 변하면 그것이 A에 영향을 미친다는 것이다.
바리스타를 예시를 들어보자.
바리스타는 커피 레시피에 의존한다. 커피 레시피가 변화하게 되었을 때, 변화된 레시피에 따라서 바리스타는 커피 만드는 방법을 수정해야 한다.
레시피의 변화가 바리스타의 행위에 영향을 미쳤기 때문에 바리스타는 레시피에 의존한다고 할 수 있다.
class Barista {
private CoffeRecipe coffeRecipe;
public Barista() {
CoffeRecipe coffeRecipe = new CoffeRecipe();
}
}
의존관계를 인터페이스로 추상화
Barista 예시를 보면 CoffeRecipe만을 의존할 수 있는 구조로 되어있다.
다양한 CoffeRecipe를 의존받을 수 있게 구현하려면 인터페이스로 추상화해야 한다.
class Barista {
private CoffeRecipe coffeRecipe;
public Barista() {
CoffeRecipe coffeRecipe = new CoffeRecipe();
}
}
interface CoffeRecipe {
newCoffe();
// 이외의 다양한 메소드
}
class CoffeRecipe implements CoffeRecipe {
public Coffe newCoffe() {
return new Coffe();
}
// ...
}
class MilkteaRecipe implements CoffeRecipe {
public Coffe newCoffe() {
return new Coffe();
}
// ...
}
의존관계가 무엇인지, 다양한 의존관계를 위해 인터페이스로 추상화를 알아봤다.
그렇다면 DI(Dependency Injection)은 무엇일까?
지금까지의 구현에서는 Barista 내부적으로 의존 관계인 CoffeRecipe가 어떤 값을 가질지 직접 정하고 있었다. 만약 어떤 CoffeRecipe를 만들지를 손님이 결정하는 상황을 생각해보자.
즉, 바리스타가 의존하고 있는 커피 레시피를 외부(손님)에서 결정하고 주입하는 것이다.
이처럼 그 의존관계를 외부에서 결정하고 주입하는 것이 의존관계 주입이다.
DI는 의존관계를 외부에서 결정하는 것이기 때문에, 클래스 변수를 결정하는 방법들이 곧 DI를 구현하는 방법이다.
런타임 시점의 의존관계를 외부에서 주입하여 DI 구현이 완성된다.
생성자 주입
class Barista {
private CoffeRecipe coffeRecipe;
public Barista(CoffeRecipe coffeRecipe) {
this.coffeRecipe = coffeRecipe;
}
}
class Customer {
private Barista barista = new Barista(new CoffeRecipe());
public void changeMenu() {
Barista = new Barista(new HotCoffeRecipe());
}
}
Setter 주입
class Barista {
private CoffeRecipe coffeRecipe;
public void setCoffeRecipe(CoffeRecipe coffeRecipe) {
this.coffeRecipe = coffeRecipe;
}
}
class Customer {
private Barista barista = new Barista();
public void changeMenu() {
Barista = setCoffeRecipe(new HotCoffeRecipe());
}
}
출처
https://github.com/devham76/tech-interview-study/blob/master/contents/spring.md