토비의 스프링(p.114)에서는 다음과 같은 세 가지의 조건을 만족해야 DI라고 표현하고 있습니다.
이 조건을 토대로, DI를 정의하면 다음과 같을 것입니다.
- 특정
빈이 동작하기 위해 필요한의존 관계를 내부적으로 직접 생성하지 않음컴파일 시점에서는인터페이스로 의존하고 있음런타임 시점에는필요한 구현체들을 컨테이너나 팩토리 같은 제 3의 존재가 주입
빈을 생성하기 위한 의존 관계를 내부가 아닌 외부에서 주입받게 되므로, 스프링에서는 이를 제어의 역전(Inversion of Control, IoC)이라고도 표현합니다.
DI를 사용하면 다음과 같은 이점을 얻을 수 있습니다.
SRP)를 지킬 수 있습니다.DI를 받지 않고 내부에서 필요한 객체를 직접 생성하는 경우, 해당 클래스의 비즈니스 로직에 대한 책임 + 필요한 객체를 생성하는 책임 모두를 가지고 있다고 판단할 수 있습니다.빈)을 손쉽게 변경할 수 있습니다.빈)를 주입해주기 때문에, 컴파일 시점의 의존 관계를 그대로 유지한 채로, 테스트의 런타임 시점의 의존 관계를 변경할 수 있습니다.스프링에서는 @Autowired를 통해 의존성을 주입합니다.
이렇게 의존성을 주입하는 방법은 다음과 같이 4가지가 존재합니다.
setter 주입public class MyBean {
private final A a;
@Autowired
public MyBean(A a) {
this.a = a;
}
}
fianl 키워드를 추가할 수 있습니다.null이 되지 않도록 보장할 수 있습니다.public class MyBean {
private A a;
@Autowired
public void setA(A a) {
this.a = a;
}
}
final 키워드를 추가할 수 없습니다.null이 될 수 있습니다.public class MyBean {
@Autowired
private A a;
}
final 키워드를 추가할 수 없습니다.public class MyBean {
private A a;
private B b;
@Autowired
public void init(A a, B b) {
this.a = a;
}
}
Bean을 받아 주입할 수 있습니다.final 키워드를 추가할 수 없습니다.다음과 같은 기준으로 사용할 수 있을 것이라 생각합니다.
Production 환경Test 환경DI 방식은 사용하지 않음생성자 주입을 사용해야 하는 이유 두 가지는 다음과 같습니다.
final 키워드 추가(불변 및 null을 허용하지 않음)이는 다른 방식에 비해 월등히 뛰어난 방식이기 때문에, 다른 방식을 사용할 이유가 없다고 볼 수 있습니다.
다만 Test 환경에서는 필드 주입을 사용할 것이라 명시했는데, 이는 편의성 때문입니다.
A가 B를 사용하는 경우, B가 변경되면 A가 영향을 받게 됩니다.DI가 아닌걸까?DI의 목적을 생각해봐야 할 것 같습니다.DI는 결국, A가 B를 사용할 때, B의 변경에 영향을 최소화하기 위해 사용하는 것입니다.B를 인터페이스로 추상화하고, B의 구현체를 제 3의 존재가 주입해주는 것입니다.B를 추상화하지 않고 B의 구현체를 그대로 사용한다면, 외부에서 주입받는다고 하더라도 A는 B에게 계속 영향을 받게 됩니다.DI를 수행하는 이유가 없어지므로, 엄밀히 따지자면 DI가 아니라고 볼 수 있습니다.빈으로 등록할 모든 클래스에 대해 추상화를 적용하는 것은 비용이 커질 수 있기 때문에, 환경에 따라 적절히 선택해야 할 것입니다.IoC? DI?IoC는 객체의 생성과 구성에 대한 제어 권한을 객체 자체에서 외부로 이동하는 디자인 패턴입니다.외부가 컨테이너인지, 팩토리인지 상관 없이 이를 모두 IoC라고 볼 수 있습니다.토비의 스프링에서 DI 조건 중 하나로 런타임 시점에는 필요한 구현체들을 컨테이너나 팩토리 같은 제 3의 존재가 주입이 있는 것입니다.DI는 이러한 IoC를 구현하기 위한 방법 중 하나입니다.DI는 주로 스프링에서 많이 표현되기 때문에 컨테이너, 빈이라는 단어를 통해 설명이 되었다고 볼 수 있습니다.인터페이스를 사용하지 않으면 DI가 아닌걸까?와 연관이 있는 항목으로 보입니다.A가 B를 사용할 때, B의 변경에 A가 영향을 받으므로 A와 B의 결합도가 강한 상태입니다.DI와 같은 IoC 없이 A가 B를 직접 생성해주는 경우, A에서 필요한 비즈니스 로직과는 별개로 A의 로직을 수행하기 위해 필요한 B를 생성해주는 과정까지 책임을 지니고 있습니다.A의 비즈니스 로직 책임 + B를 생성해주는 책임A의 상태는 B를 생성해주는 책임으로 인해 결합도가 높고(B의 변경에 영향을 받기 쉬워짐), 응집도가 낮습니다.(A의 비즈니스 로직뿐만 아니라 B의 생성 책임까지 지니고 있음)DI의 장점 중의 하나로 결합도를 낮출 수 있으며, 이 결합도를 낮추는 과정이 B의 생성 책임을 외부에게 위임하는 것이므로 응집도를 높일 수 있게 되는 것입니다.null이 되지 않을까? 다른 주입 방법은 null일 수 있다는 것일까?DI가 이루어지기 때문에 null이 되지 않습니다.final 키워드를 붙일 수 있는 이유기도 합니다.일반 메소드 + setter)는 객체를 생성한 이후 의존 관계가 주입됩니다.null일 수 있습니다.setter가 없는 상태에서 필드 주입을 사용하게 되면, 스프링의 DI Container에 의해서만 의존관계 주입이 가능합니다.