DI를 이야기할 때 보통 무엇을 가장 먼저 떠올리시나요? 많은 분이 스프링 같은 DI 컨테이너를 생각합니다. 하지만 이것은 그저 DI를 쉽게 쓰게 도와주는 도구일 뿐이죠. DI 그 자체의 필수 조건은 아닙니다.
이 차이를 이해하는 것이 중요합니다. 도구와 개념을 구분하지 못하면 DI의 진짜 목적을 놓칠 수 있습니다. 오늘은 이 둘의 관계를 명확하게 짚어보겠습니다.
DI의 진짜 의미를 찾아봅시다
DI의 뜻은 아주 쉽습니다. 객체가 필요한 의존성을 스스로 만들지 않는다는 것이죠. 대신 외부에서 주입받아 사용한다는 뜻입니다.
이 방식은 특정 라이브러리가 없어도 충분히 가능합니다. 거창한 프레임워크가 꼭 필요한 것은 아닙니다. 코드를 작성하는 방식만 조금 바꾸면 되니까요.
도구 없이 구현하는 Pure DI
생성자에서 필요한 의존성을 받으면 됩니다. 그리고 상위 계층에서 객체를 조립해 주면 DI는 완성되죠. 이런 방식을 퓨어 DI(Pure DI)라고 부릅니다.

이 방식은 코드가 아주 투명합니다. 이 객체가 무엇에 의존하는지 바로 드러나죠. 객체 생성 흐름도 눈으로 쉽게 따라갈 수 있습니다.
컨테이너는 언제 필요한가요
그렇다면 도구는 언제 써야 할까요? 바로 객체 연결 과정이 너무 많아질 때입니다. 수많은 객체를 손으로 일일이 연결하려면 번거롭죠.
이때 컨테이너가 반복 작업을 대신해 줍니다. 규칙을 등록해 두면 알아서 조립하도록 맡기는 것이죠. 생산성을 크게 높여주는 고마운 도구입니다.
흔히 저지르는 실수와 부작용
하지만 도구를 쓰는 것 자체가 DI라고 착각하면 안 됩니다. 특히 컨테이너를 서비스 로케이터(Service Locator)처럼 쓰는 경우가 많습니다. 객체 내부에서 컨테이너를 호출하는 방식이죠.
“필요한 서비스 주세요”라고 직접 요청하면 겉보기엔 비슷해 보입니다. 하지만 실제로는 결합도가 다시 높아집니다. 의존성이 코드에 명시되지 않고 숨겨지기 때문이죠.
이러면 어떤 객체가 무엇에 의존하는지 파악하기 어렵습니다. 테스트할 때도 컨테이너 전체를 가져와야 해서 불편하죠. DI가 해결하려던 문제를 다시 불러오는 셈입니다.
올바른 설계를 위한 기준점
내가 잘하고 있는지 확인하는 기준은 간단합니다. 컨테이너 없이도 이 설계를 설명할 수 있는지 보세요. 도구를 뺐을 때 코드가 성립하지 않는다면 잘못된 것입니다.
퓨어 DI로도 충분히 설명할 수 있어야 합니다. 컨테이너는 그 위에 얹는 선택적 도구일 뿐이니까요. 없으면 안 되는 것이 아니라 있으면 편한 것입니다.
의존성을 외부로 드러내는 사고방식이 먼저 자리 잡아야 합니다. 그래야 도구도 제 역할을 할 수 있습니다. 그렇지 않으면 컨테이너는 오히려 설계를 방해하는 장치가 됩니다.