의존성 주입 (Dependency Injection) 패턴
의존성 주입(이하 DI) 패턴은 현재 가장 널리 사용되고 있는 패턴 중 하나라고 할 수 있다.
보통은 Spring이나 NestJS 등의 백엔드 프레임워크를 통해서 접해보았을 것이다.
기본 원리
DI는 기본적으로 느슨한 결합 구조를 지향하는 패턴이다.
만약 백엔드에서 Contoller-Service 레이어의 구조를 기반으로 시스템을 구성한다면 그냥 이렇게 할 수도 있다.

이러한 방법 자체에는 사실 별다른 문제는 없다.
다만 Service가 컨트롤러 코드 내에 강하게 결합되어 있다는 것이 꽤나 불편한 상황으로 작용할 수는 있다.
만약 저 health라는 컨트롤러에 대해서 단위테스트를 하고 싶다면 어떻게 해야할까?
보통은 사이드이펙트를 유발하는 모든 구성요소를 Fake Object로 교체해서 돌리는 것이 바람직한데, 저 코드는 그런 대처가 용이하지는 않다.
이런 문제를 좀 개선하고자 나온게 DI 패턴이다.
인터페이스 등을 활용해서 느슨하게 연결하고, 세부적인 구현체는 상황에 따라서 그때그때 집어넣어서 사용할 수 있게 하는 것이다.
DI에서는 그를 위해서 레이어가 좀 추가된다.
Controller가 interface 등을 통해서 Service를 추상화된 형태로 DI에서 가져와 사용하게 되는 것이다.
그리고 Service는 각각 DI Context에 등록해서 DI에 의해 load되게 된다.
컨트롤러가 꼭 인터페이스를 통해서만 DI를 해야하는건 아니지만, 인터페이스를 쓰지 않으면 DI의 많은 장점이 사라진다.
저기서 DI될 수 있는 Service 단위들을 Spring에서는 Bean이라고 부르고, Nest.js에서는 provider 등으로 부르곤 한다.
그리고 Dependency Manager는 Factory라는 이름으로 불린다.
장단점
장점
-
인터페이스를 통한 추상화로, 상황에 따른 구현체의 교체가 편리하다.
-
1번 사유과 연관되어, 단위테스트 등을 수행할 때도 Fake 구현체와의 교체가 수월하다.
단점
-
코드 자체가 장황하고 복잡해진다.
-
보통 데코레이터와 리플렉션 등을 활용한 메타프로그래밍을 통해 구현되어, IDE 자동화나 가독성을 저해하는 부분이 있다.