[DI] 의존성 주입(Dependency Injection) 의 개념과 방법 및 장단점

sana·2022년 5월 28일
2

Dependency Inejction

목록 보기
1/2

의존성 주입의 개념

의존성 주입에 대한 비유

우선 이해를 돕기 위해 다음 상황을 상상해보자.

갑자기 케이크가 먹고 싶다. 하지만 집에서 직접 만들면 주방이 지저분해지고 난리나니 배달을 시키기로 결심했다. 그런데 우리동네 빵집은 사장님이 직접 베이킹을 해서 배달까지 하기 때문에 옷에 밀가루가 묻은 더러운 상태이다. 그래서 나는 배달원이 우리 집에 들어오는 것이 아니라, 그저 잘 만든 케이크만 대문 앞에 놓아주기를 바란다. 즉, 어질러지는 상황 없이, 배달원과 최소한의 접촉만을 하면서 케이크를 받고 싶다.

여기서 우리는 의존성 주입에 사용되는 개념(혹은 객체)들을 발견할 수 있다.
우선 나는 케이크가 필요하기 때문에 이것을 배달원에게 요청한다. 즉, 나는 클라이언트이며 케이크는 서비스, 배달원은 주입자이다. 배달원은 나에게 케이크를 배달해줌으로써 의존성을 주입하는 것이다. 여기서 케이크는 초코케이크일수도 있고, 딸기케이크일 수도 있는데 이것을 결정하는 것은 배달원이다.

결국 의존성 주입의 핵심은 객체들 간의 상호작용이 최소한이었으면 좋겠다는 것이다. 즉, 내가 무엇이 필요하면 직접 만드는 것이 아니라 전달받도록 요청하게 된다.

의존성 주입이란?

의존성 주입의 기본적인 의미는 ‘외부'에서 클라이언트에게 서비스를 제공(주입)하는 것이다.
다시 말해, 객체가 필요로 하는 어떤 것을 외부에서 전달해주는 것으로 볼 수 있다.

하지만 더 넓게는 이러한 의존성 주입을 가능하게 하는 디자인, 설계 패턴까지도 의미한다. 작은 의미의 의존성 주입이 단일 클래스와 관련되어 있다면 의존성 주입 아키텍처 패턴은 이론과 객체들을 어떻게 구성할지에 대한 지침까지도 포함한다고 볼 수 있다. 여러 프레임워크들이 전체 애플리케이션 단위에서 느슨하게 결합된 코드를 개발하는 것을 가능하도록 지원해준다.

의존성 주입의 참여 객체

앞서 말한 의존성 주입에 활용되는 개념을 다시 정리해보자.

클라이언트-서버
Class AClass B의 코드를 참조할 때, A는 B에 의존한다고 말한다. 이 때 Class AClass 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

https://en.wikipedia.org/wiki/Dependency_injection#:~:text=In%20software%20engineering%2C%20dependency%20injection,leading%20to%20loosely%20coupled%20programs.

https://hue-dev.site/springframework/2021/05/03/Dependency-Injection-%EC%9D%B4-%EB%AD%90%EC%97%90%EC%9A%94.html

1개의 댓글

comment-user-thumbnail
2023년 5월 13일

진짜 덕분에 이해했습니다... 감사합니다!

답글 달기