의존성 주입(Dependency Injection, DI)은 객체 간의 결합도를 낮추기 위해 사용되는 설계 패턴이다.
핵심은 “객체가 필요한 다른 객체를 스스로 만들지 않고, 외부에서 주입받는다”는 것이다.
반면 인터페이스는 결합을 느슨하게 만들기 위한 설계 원칙이지만,
객체 생성 책임까지 외부로 넘기지 않으면 여전히 강한 결합이 남는다.
이 글에서는 직접 의존 → 인터페이스 기반 → 의존성 주입의 순서로 구조와 차이를 비교한다.
public class Notification
{
private EmailService _email = new EmailService();
public void Alert(string msg)
{
_email.Send(msg);
}
}
Notification이 EmailService를 직접 생성한다.대표 용어: 강결합(Tight Coupling), 전통적 설계(Traditional Design)
public interface IMessageService
{
void Send(string msg);
}
public class EmailService : IMessageService
{
public void Send(string msg) => Console.WriteLine("메일 전송");
}
public class Notification
{
private IMessageService _service;
public Notification()
{
_service = new EmailService(); // 여전히 직접 생성
}
public void Alert(string msg)
{
_service.Send(msg);
}
}
new로 직접 객체를 생성한다.대표 용어: 인터페이스 기반 강결합(Interface-coupled), 부분적 의존 역전(Partial Inversion)
public class Notification
{
private readonly IMessageService _service;
public Notification(IMessageService service)
{
_service = service;
}
public void Alert(string msg)
{
_service.Send(msg);
}
}
대표 용어: 의존성 주입(Dependency Injection), IoC(Inversion of Control)
using Microsoft.Extensions.DependencyInjection;
var services = new ServiceCollection();
services.AddTransient<IMessageService, EmailService>();
services.AddTransient<Notification>();
var provider = services.BuildServiceProvider();
var notification = provider.GetRequiredService<Notification>();
notification.Alert("테스트 메시지");
ServiceCollection은 DI 컨테이너로, 객체 생성과 관계를 자동으로 관리한다.AddTransient, AddScoped, AddSingleton은 객체의 수명(Lifetime)을 정의한다.new를 호출하지 않아도 주입이 자동으로 이루어진다.| 항목 | 직접 의존 | 인터페이스만 사용 | 의존성 주입 |
|---|---|---|---|
| 객체 생성 위치 | 내부 (new) | 내부 (new) | 외부 (컨테이너 / 호출자) |
| 결합도 | 매우 높음 | 중간 | 낮음 |
| 유연성 | 없음 | 교체 시 코드 수정 필요 | 교체 시 등록만 변경 |
| 테스트 용이성 | 낮음 | 낮음 | 높음 |
| 대표 용어 | 강결합(Tight Coupling) | 인터페이스 기반 강결합 | 의존성 주입(DI) |
| 제어 주체 | 클래스 내부 | 클래스 내부 | 외부(IoC Container) |
DI는 IoC의 한 형태이다.
즉, 객체 생성과 의존성 관리의 제어권이 클래스 내부에서 프레임워크로 “역전”되는 것이다.
| 방식 | 제어 주체 | IoC 적용 여부 |
|---|---|---|
| 직접 의존 | 클래스 내부 | ❌ |
| 인터페이스만 사용 | 클래스 내부 | ❌ |
| DI | 외부 컨테이너 | ✅ |