우선 이해를 돕기 위해 다음 상황을 상상해보자.
갑자기 케이크가 먹고 싶다. 하지만 집에서 직접 만들면 주방이 지저분해지고 난리나니 배달을 시키기로 결심했다. 그런데 우리동네 빵집은 사장님이 직접 베이킹을 해서 배달까지 하기 때문에 옷에 밀가루가 묻은 더러운 상태이다. 그래서 나는 배달원이 우리 집에 들어오는 것이 아니라, 그저 잘 만든 케이크만 대문 앞에 놓아주기를 바란다. 즉, 어질러지는 상황 없이, 배달원과 최소한의 접촉만을 하면서 케이크를 받고 싶다.
여기서 우리는 의존성 주입에 사용되는 개념(혹은 객체)들을 발견할 수 있다.
우선 나는 케이크가 필요하기 때문에 이것을 배달원에게 요청한다. 즉, 나는 클라이언트이며 케이크는 서비스, 배달원은 주입자이다. 배달원은 나에게 케이크를 배달해줌으로써 의존성을 주입하는 것이다. 여기서 케이크는 초코케이크일수도 있고, 딸기케이크일 수도 있는데 이것을 결정하는 것은 배달원이다.
결국 의존성 주입의 핵심은 객체들 간의 상호작용이 최소한이었으면 좋겠다는 것이다. 즉, 내가 무엇이 필요하면 직접 만드는 것이 아니라 전달받도록 요청하게 된다.
의존성 주입의 기본적인 의미는 ‘외부'에서 클라이언트에게 서비스를 제공(주입)하는 것이다.
다시 말해, 객체가 필요로 하는 어떤 것을 외부에서 전달해주는 것으로 볼 수 있다.
하지만 더 넓게는 이러한 의존성 주입을 가능하게 하는 디자인, 설계 패턴까지도 의미한다. 작은 의미의 의존성 주입이 단일 클래스와 관련되어 있다면 의존성 주입 아키텍처 패턴은 이론과 객체들을 어떻게 구성할지에 대한 지침까지도 포함한다고 볼 수 있다. 여러 프레임워크들이 전체 애플리케이션 단위에서 느슨하게 결합된 코드를 개발하는 것을 가능하도록 지원해준다.
앞서 말한 의존성 주입에 활용되는 개념을 다시 정리해보자.
클라이언트-서버
Class A
가 Class B
의 코드를 참조할 때, A는 B에 의존한다고 말한다. 이 때 Class A
는 Class B
의 '클라이언트'이고, B는 A의 '서비스'라고 할 수 있다.
주입자
주입자는 서비스를 생성해서 클라이언트로 주입해주는 책임을 갖는다.
인터페이스
클라이언트의 서비스가 어떻게 상호작용할지 그 방법을 정의하는 인터페이스 클래스이다.
의존성을 주입하는 방법에는 생성자 주입, 세터 주입, 인터페이스 주입, 필드 주입 등 다양한 방법이 있다.
하지만 이 글에서는 가장 중요한 생성자 주입만을 다루도록 하겠다.
class Drink {
private Coffee coffee;
public Drink() {
this.coffee = new Coffee();
}
}
위와 같이 클라이언트(Drink) 클래스 내부에서 서비스(Coffee)를 직접 생성하는 경우 의존성 주입이 이루어지지 않았다고 볼 수 있다.
클라이언트는 서비스에 전적으로 의존하고, 서비스는 클라이언트에 타이트하게 결합되어 있기 때문에 다른 커피를 마시고 싶을 경우 Drink 내부의 코드도 변경해야 한다.
class Drink {
private Coffee coffee;
public Drink(Coffee coffee) {
this.coffee = coffee;
}
}
class Coffee {}
class Espresso extends Coffee{}
class Latte extends Coffee {}
반면, 위와 같이 생성자를 통해 의존성 주입을 한 경우 클라이언트(Drink)는 필요할 때 서비스(Coffee)를 외부에서 넘겨받는다.
따라서 Coffee 클래스를 상속받은 Espresso, Latte 클래스도 Drink에 쉽게 전달될 수 있다.
(에 대한 나의 생각)
유지보수의 필요가 없고 간단한 프로그램을 만들 경우 의존성 주입은 오히려 번거롭고 불필요한 선택일 수 있다.
또한, 개발자나 팀이 숙련되지 않은 경우 의존성 주입 프레임워크를 이해하기 어려울 수 있어 오히려 개발에 소요되는 시간이 늘어날 수 있다.
하지만 일반적인 상용 애플리케이션을 만들고 지속해서 유지보수를 해야 할 경우 의존성 주입은 필수적이라고 생각한다.
애플리케이션이 복잡해질수록 의존성 주입은 빛을 발하고 장기적으로 생산성 향상에 도움이 될 것이다.
따라서 프로젝트를 시작하기 전에 적합한 의존성 주입 프레임워크를 선택하는 것이 중요할 것이다.
[참고자료]
https://betterprogramming.pub/what-is-dependency-injection-b2671b1ea90a
진짜 덕분에 이해했습니다... 감사합니다!