의존성 주입 (Dependency Injection)

컴퓨터과학·AI
한 줄 정의: 객체(모듈)가 필요로 하는 다른 객체를 스스로 만들지 않고 외부에서 전달받도록 설계하여, 코드 사이의 결합도를 낮추는 소프트웨어 설계 기법입니다.

쉽게 풀면

카페 바리스타가 커피를 내리려면 원두가 필요합니다. 그런데 바리스타가 매번 직접 원두를 재배하고 수확한다면 커피 한 잔 만드는 데 몇 달이 걸릴 것입니다. 대신 원두 공급 업체가 필요한 원두를 가져다주면, 바리스타는 커피를 내리는 본연의 일에만 집중할 수 있습니다. 의존성 주입도 마찬가지입니다. 코드 안의 한 부품(객체)이 다른 부품을 직접 만들어 쓰는 대신, 외부에서 완성된 부품을 "주입"받아 사용하게 만들어서, 각 부품이 자기 역할에만 집중하고 부품 교체도 쉬워지게 합니다.

왜 중요한가

소프트웨어공학 논문에서 의존성 주입은 단위 테스트 가능성, 유지보수성, 모듈 재사용성을 논할 때 빠지지 않는 개념입니다. 대규모 시스템일수록 모듈 간 결합도가 낮아야 요구사항 변경이나 컴포넌트 교체에 유연하게 대응할 수 있기 때문에, 마이크로서비스 아키텍처나 프레임워크 설계 연구에서 핵심 설계 원리로 다뤄집니다. 또한 자동화된 테스트, 지속적 통합(CI) 파이프라인의 신뢰성을 높이는 실무적 기반으로도 자주 인용됩니다.

논문에서는 이렇게 쓰입니다

"테스트 용이성을 높이기 위해 의존성 주입 패턴을 적용하여, 실제 데이터베이스 연결 없이도 각 모듈을 목(mock) 객체로 손쉽게 교체하며 검증할 수 있도록 시스템을 설계했다."

이 문장은 모듈이 사용할 부품(예: 데이터베이스 연결)을 코드 내부에서 직접 만들지 않고 외부에서 전달받게 만들어서, 테스트할 때는 가짜 부품으로 손쉽게 바꿔 끼울 수 있었다는 뜻입니다.

"제안하는 마이크로서비스 프레임워크는 의존성 주입 컨테이너를 통해 서비스 간 참조를 관리함으로써, 개별 서비스의 배포와 확장을 독립적으로 수행할 수 있게 하였다."

이 문장은 여러 개의 서비스가 서로를 직접 참조하지 않고, 컨테이너라는 중간 관리자가 필요한 서비스를 대신 연결해 주기 때문에 각 서비스를 따로 배포하거나 규모를 늘릴 수 있었다는 뜻입니다.

"안드로이드 애플리케이션의 유지보수성을 개선하기 위해 의존성 주입 프레임워크를 도입하여, 화면 전환 시마다 반복 생성되던 객체를 재사용 가능한 형태로 관리하였다."

이 문장은 모바일 앱 개발에서 여러 화면이 공통으로 쓰는 객체를 매번 새로 만드는 대신 한 곳에서 관리하고 필요할 때 전달받도록 바꾸어, 코드 중복과 메모리 낭비를 줄였다는 뜻입니다.

조금 더 깊게 보면

의존성 주입은 흔히 생성자 주입, 세터(setter) 주입, 인터페이스 주입 세 가지 방식으로 구현되며, 이 중 생성자 주입이 객체 생성 시점에 의존 관계를 확정할 수 있어 널리 권장됩니다. 실제로는 스프링(Spring), 대거(Dagger), Hilt 같은 DI 프레임워크가 이러한 주입과 객체 생명주기 관리를 자동화해 주는 "제어의 역전(Inversion of Control, IoC)" 컨테이너 역할을 합니다. 논문을 읽을 때는 저자가 어떤 주입 방식을 택했는지, 그리고 그것이 테스트 대체(mocking)나 순환 의존성 방지와 어떻게 연결되는지를 함께 살펴보면 이해가 깊어집니다.

주의할 점

의존성 주입은 코드 간 결합도를 낮추는 대표적인 디자인 패턴이지만, 무분별하게 남용하면 어떤 부품이 어디서 어떻게 조립되는지 한눈에 파악하기 어려워지고 설정(configuration)이 복잡해질 수 있습니다. 이는 객체지향 프로그래밍의 4대 원칙 중 캡슐화·추상화를 지키기 위한 수단이지, 그 자체가 목적은 아니라는 점을 기억해야 합니다.

관련 용어