객체가 스스로 의존성을 챙기는 상황을 먼저 떠올려 봅시다. 객체가 사용할 대상을 직접 생성하면 두 객체는 강하게 묶입니다. 겉보기에는 편해 보일지도 모릅니다. 하지만 시간이 흐를수록 이 결합은 큰 부담으로 다가옵니다.
의존 대상의 내용이 조금만 바뀌어도 사용하는 쪽까지 고쳐야 합니다. 비슷한 기능을 다른 방식으로 바꾸려 해도 쉽지 않습니다. 지금은 잘 돌아가도 나중에는 건드리기 힘든 코드가 됩니다. 이 부분에서 많은 프로그래머가 답답함을 느끼죠.
DI를 통해 책임을 분리하는 방법
의존성 주입(Dependency Injection)은 이 문제를 해결하는 좋은 방법입니다. 객체가 무엇을 쓴다는 사실은 그대로 둡니다. 대신 그것을 만들고 가져오는 책임을 객체에서 떼어내는 것이죠. 이제 객체는 스스로 의존성을 만들지 않습니다.
외부에서 필요한 것을 건네받아 사용하게 됩니다. 이 변화로 전체적인 설계는 훨씬 간결해집니다. 객체는 자기 역할에만 집중하게 되죠. 생성과 선택이라는 부수적인 짐을 내려놓을 수 있습니다.

유연한 변경과 쉬워지는 테스트
이렇게 구성이 바뀌면 코드의 성격도 달라집니다. 우선 변경에 아주 강해집니다. 특정 기능을 다른 것으로 바꾸고 싶을 때가 있습니다. 이때 객체 내부를 뜯어고칠 필요가 없습니다.
그저 주입하는 대상만 교체하면 해결됩니다. 또한 테스트 과정이 훨씬 수월해집니다. 실제 환경의 무거운 객체 대신 가벼운 대역을 넣을 수 있습니다. 테스트는 빠르고 예측 가능한 상태가 됩니다.
미래를 대비하는 현실적인 선택
테스트를 위해 코드를 억지로 비틀 필요도 줄어듭니다. 자연스럽게 기능을 확장하기 좋은 코드로 이어집니다. 기존 코드는 거의 건드리지 않아도 됩니다. 새로운 기능을 추가하는 일이 훨씬 가벼워지죠.
중요한 점은 DI가 모든 걸 해결해 주는 마법은 아니라는 겁니다. DI의 가치는 의존성을 밖으로 드러낸다는 데 있습니다. 어떤 객체가 무엇에 의존하는지 명확해집니다. 관계를 조정할 수 있는 지점이 생기는 것이죠.
변화를 흡수하는 유연한 설계
덕분에 시스템은 투명해지고 변경 범위도 예측할 수 있습니다. DI가 필요한 이유는 명확합니다. 소프트웨어는 반드시 변하고 그 변화는 의존성에서 시작됩니다. 직접 의존성을 쥐고 있으면 변화는 파괴가 됩니다.
반대로 외부에서 주입받으면 변화는 교체와 확장이 됩니다. DI는 코드를 멋지게 꾸미려는 기술이 아닙니다. 미래의 변경을 감당할 수 있는 틀을 만드는 현실적인 선택입니다.