스프링
의존성 주입 DI
j9972
2024. 1. 15. 22:16
728x90
DI - 의존성 주입
DI는 외부에서 두 객체 간의 관계를 결정해주는 디자인 패턴으로, 인터페이스를 사이에 둬서 의존관계가 고정되지 않도록 런타임 시전에 동적으로 관계를 주입하여 결합도를 낮출 수 있다.
의존성 주입이라는 말을 예시를 통해서 정확히 무엇인지 알아보겠다.
A가 B를 의존한다 → 의존대상 B가 변하면 그것이 A에게 영향을 미친다.
(요리사)가 (레시피)에 의존한다
“피자가게의 요리사는 피자의 레시피에 의존”한다.
피자 레시피가 변하면 요리사는 피자를 만드는 방법을 수정해야 한다.
왜냐하면 레시피의 변화가 요리사의 행위에 영향을 끼치기에 “요리사는 레시피에 의존한다” 라고 할 수 있다.
왜 필요한가?
DI를 적용하지 않는다면, 객체들 간의 관계가 아니라 클래스 간의 관계가 맺어질 뿐만 아니라 두개의 클래스가 강하게 결합된다
public class Store {
private Pencil pencil;
public Store() {
this.pencil = new Pencil();
}
}
위와 같이 클래스들이 강하게 결합되어 있어서 유연성이 떨어지고, 객체들간의 관계가 아닌 클래스 간의 관계가 아니라 객체간의 관계가 맺어져 있지 않아서 어떤 제품을 판매할지에 대한 관심이 분리되어 있지 않는데, 이를 해결하고자 DI가 필요한 것이다.
DI 사용 및 미사용 예시
DI 사용하지 않은 경우
public class PizzaRecipe { ... }
public class Chef {
private PizzaRecipe recipe = new PizzaRecipe();
public makeingPizza() {
this.recipe.cooking()
}
}
DI 사용하는 경우
public class PizzaRecipe { ... }
public class PotatoPizza extends PizzaRecipe { ... }
public class Chef {
private PizzaRecipe recipe;
public Chef(PizzaRecipe recipe) {
this.recipe = recipe
}
public makeingPizza() {
this.recipe.cooking()
}
}
여기서 보면 Chef 객체에 PizzaRecipe 라는 객체를 주입해주는 것을 알 수 있다.
이러면서 PotatoPizza를 cooking하냐 다른 pizza를 cooking 하냐에 따라서 다른 객체를 넘겨주어서 코드를 재사용 할 수 있게 되는 것이다.
→ 다형성이 필요하다
DI의 장점
- Unit Test가 용이해진다. ⇒ 의존성이 낮아지니깐
- 코드의 재활용성이 높아진다. ⇒ 하나의 객체에 의존하지 않으니깐
- 객체 간의 의존성(종속성)을 줄이거나 없앨 수 있다.
- 객체 간의 결합도를 낮추면서 유연한 코드를 작성할 수 있다.
DI 적용 방법
- 생성자 주입
- 객체 생성과 동시에 의존관계 주입을 하며, 가장 권장되는 방법이다.
- 생성자의 호출 시점에 1회 호출되는 것을 보장한다.
- setter 프로퍼티 주입
- 다른 사람이 데이터 변경이 가능해진다 ( setter 때문 )
- 필드 주입
- 말 그대로 피르뎅 바로 의존관계를 주입하는데, 외부에서 변경이 불가능하다
생성자 주입이 권장되는 이유
- 객체의 불변성 확보
- 이를 통해서 캐싱 기능과 GC 성능이 향상될 수 있다.
- 테스트 코드의 작성
- final 키워드 작성 및 Lombok의 결합
- 생성자 주입만 객체 생성 동시에 의존성이 이뤄지기에 final 변수를 할당할 수 있다.
- 다른 2가지 방법은 의존성 주입 함수 호출을 통해 의존성이 주입되기에 final 변수할당이 불가능하다
- 순환 참조 방지
- 순환 참조 : 서로 다른 빈들이 참조를 맞물리면서 스프링에서 어떤 빈을 먼저 만들어야 하는지 결정할 수 없는 상황