DI(Dependency Injection)
의존성 주입(Dependency Injection)은 외부에서 두 객체 간의 관계를 결정해 주는 디자인 패턴으로, 인터페이스를 사이에 둬서 클래스 레벨에서는 의존관계가 고정되지 않도록 하고 런타임 시에 관계를 동적으로 주입하여 유연성을 확보하고 결합도를 낮출 수 있게 해주는 방법입니다.
의존성이란 한 객체가 다른 객체를 사용할 때 의존성이 있다고 합니다. 예를 들어 다음과 같이 Store 객체가 Pencil 객체를 사용하고 있는 경우에는 우리는 Store 객체가 Pencil 객체에 의존성이 있다고 표현합니다.
public class Store {
private Pencil pencil;
}
그리고 두 객체 간의 관계를 맺어주는 것을 의존성 주입이라고 하며 생성자 주입, 필드 주입, 수정자 주입 등 다양한 주입 방법이 있습니다. Spring 4부터는 생성자 주입을 강력히 권장합니다.
//DI Container
Product product = new Pencil() // Dependency
Store sotre = new Store(product) // Injection
DI(Dependency Injection)가 필요한 이유
public class Store {
private Pencil pencil;
public Store() {
this.pencil = new Pencil();
}
}
위의 예제 코드를 보면 다음과 같은 문제점을 가지고 있습니다.
- 두 클래스가 강하게 결합되어 있습니다.
- 객체들 간의 관계가 아니라 클래스 간의 관계가 맺어져 있습니다.
1. 두 클래스가 강하게 결합되어 있음
Sotre 클래스는 현재 Pencil 클래스와 강하게 결합되어 있다는 문제점을 가지고 있습니다.
두 클래스가 강하게 결합되어 있어서 만약 Store에서 Pencil이 아닌 Food와 같은 다른 상품을 판매하고자 한다면 Store 클래스의 생성자에 변경이 필요합니다.
즉, 유연성이 떨어지는 코드입니다.
각각의 다른 상품들을 판매하기 위해 생성자만 다르고 나머지는 중복되는 Store 클래스들이 파생되는 것은 좋지 못합니다.
이에 대한 해결책으로 상속을 떠올릴 수 있지만, 상속은 제약이 많고 확장성이 떨어지므로 피하는 것이 좋습니다.
2. 객체들 간의 관계가 아니라 클래스 간의 관계가 맺어짐
위의 Store와 Pencil은 객체들 간의 관계가 아니라 클래스 간의 관계가 맺어져 있다는 문제가 있습니다.
올바른 객체지향적 설계라면 객체들 간에 관계가 맺어져야 합니다.
객체들 간에 관계가 맺어졌다면 다른 객체의 구체 클래스를 알지 못하더라도, 인터페이스의 타입으로 사용할 수 있습니다.
결국 위와 같은 문제점이 발생하는 근본적인 이유는 Store에서 불필요하게 어떤 제품을 판매할 지에 대한 관심이 분리되지 않았기 때문입니다. Spring에서는 DI를 적용하여 이러한 문제를 해결하고자 하였습니다.
DI를 통한 문제 해결
위와 같은 문제를 해결하기 위해 다형성이 필요합니다.
여러 제품을 하나로 표현하기 위해서 Product라는 Inteface를 만들어 주도록 합니다.
public interface Product {
}
public class Pencil implements Product {
}
이후 Store와 Pencil이 강하게 결합되어 있는 부분을 제거해주어야 합니다. 이를 제거하기 위해서 다음과 같이 외부에서 상품을 주입(Injection) 받아야 합니다.
public class Store {
private Product product;
public Store(Product product) {
this.product = product;
}
}
위와 같은 이유로 DI 컨테이너가 필요합니다.
Store에서 Product 객체를 주입하기 위해서는 애플리케이션 실행 시점에 필요한 객체를 생성해야 하며, 의존성이 있는 두 객체를 연결하기 위해 한 객체를 다른 객체로 주입시켜야 합니다.
예를 들어 다음과 같이 Pencil이라는 객체를 만들고, 그 객체를 Store로 주입시켜 주는 역할을 위해 DI 컨테이너가 필요합니다.
public class BeanFactory {
public void store() {
Product pencil = new Pencil();
// Bean의 생성
Store sotre = new Store(pencil);
// 의존성 주입
}
}
위와 같은 부분은 스프링 프레임워크가 지원을 해줍니다. 스프링은 특정 위치부터 클래스를 탐색하고, 객체를 만들며 객체들의 관계까지 설명해 줍니다. 이러한 이유로 스프링은 DI컨테이너라고도 불립니다.
그리고 이러한 개념은 제어의 역전(Inversion of Control, IoC)라고도 불립니다. 어떠한 객체를 사용할지에 대한 책임은 프레임워크에게 넘어갔고, 자신은 수동적으로 주입받는 객체를 사용하기 때문입니다.
reference
https://mangkyu.tistory.com/150
'Spring' 카테고리의 다른 글
[Spring] Spring 환경 구성 (0) | 2023.03.31 |
---|---|
[Spring] Spring Framework란?(4) (0) | 2023.03.30 |
[Spring] Spring Framework란?(3) (0) | 2023.03.29 |
[Spring] Spring Framework란?(1) (0) | 2023.03.28 |
[Spring] Framework란? (0) | 2023.03.27 |