C#
C# 기초부터 고급까지 Chapter 2.2. 의존성 주입(DI) – .NET 기본 DI 완전 정복
Juan_
2025. 4. 28. 21:18
728x90
📚 Chapter 2.2: 의존성 주입(DI) – .NET 기본 DI 완전 정복
1️⃣ "의존성(Dependency)"이란 무엇인가?
✅ Dependency(의존성) = "내가 동작하려면 필요한 다른 것"
예를 들면:
- 자동차(고수준)가 엔진(저수준)에 의존한다
- NotificationService(고수준)가 IMessageSender(저수준)에 의존한다
"내가 뭘 해야 하는데, 다른 놈한테 기대야 한다" 이거다!
2️⃣ "문제는" 직접 new로 만들어 버린다는 거야
🔥 나쁜 구조 예시
public class NotificationService
{
private EmailSender _emailSender = new EmailSender(); // 직접 new로 만듬
public void SendNotification(string message)
{
_emailSender.Send(message);
}
}
✅ 문제점:
- NotificationService는 무조건 EmailSender만 쓸 수 있다
- SMS나 Slack 추가하려면?
- NotificationService 코드를 수정해야 한다!!!
(수정하면 버그 난다, 테스트 깨진다, 배포 스트레스 온다)
3️⃣ 의존성 주입(DI)란?
✅ "내가 직접 만드는 게 아니고, 외부에서 필요할 때 넣어주는 거"
이 구조로 바뀐다!
public class NotificationService
{
private readonly IMessageSender _messageSender;
public NotificationService(IMessageSender messageSender)
{
_messageSender = messageSender;
}
public void SendNotification(string message)
{
_messageSender.Send(message);
}
}
- 이제 NotificationService는 구체 구현(EmailSender)이 뭔지 몰라도 됨
- 단지 "메시지를 보낼 수 있는" IMessageSender에만 관심 있음
✅ 추상(인터페이스)에만 의존하게 되는 거야!
4️⃣ 그럼 누가 넣어주는데?
✅ 이걸 담당하는 게 바로 DI 컨테이너다!
(공장에서 필요한 부품을 조립해주는 관리자 같은 존재)
5️⃣ .NET 기본 DI 컨테이너
⚙️ 구성 요소
요소 | 설명 |
---|---|
IServiceCollection | 등록하는 곳 (레시피북) |
IServiceProvider | 실제 객체를 만들어 주는 놈 (요리사) |
✅ .NET은 프로그램 시작할 때 필요한 의존성을 전부 등록해놓고
필요하면 꺼내쓰는 시스템이다!
6️⃣ .NET DI 실전 흐름
1단계. 인터페이스랑 구현체를 만든다
public interface IMessageSender
{
void Send(string message);
}
public class EmailSender : IMessageSender
{
public void Send(string message)
{
Console.WriteLine($"이메일 전송: {message}");
}
}
2단계. 의존성 등록 (ConfigureServices)
builder.Services.AddTransient<IMessageSender, EmailSender>();
builder.Services.AddTransient<NotificationService>();
✅ 의미:
- IMessageSender가 필요하면, EmailSender를 만들어서 줘라
- NotificationService 필요하면, IMessageSender 주입해서 줘라
3단계. 클래스에서는 그냥 받아서 쓴다 (생성자 주입)
public class NotificationService
{
private readonly IMessageSender _sender;
public NotificationService(IMessageSender sender)
{
_sender = sender;
}
public void Alert(string message)
{
_sender.Send(message);
}
}
✅ NotificationService는 IMessageSender만 신경쓴다.
(EmailSender가 뭔지, SMS가 뭔지 모른다. 알 필요도 없다.)
7️⃣ 서비스 수명(Lifetime) 상세 설명
종류 | 의미 | 특징 |
---|---|---|
Transient | 매번 새 객체 생성 | 상태 없는 객체 (가벼움) |
Scoped | 요청(Request)당 하나 | 주로 DBContext 같은 거 |
Singleton | 앱 전체에서 하나만 | 설정정보, 캐시 객체 등 |
🧠 예시
- 로그 찍는 Logger → Singleton
- HTTP 요청 핸들링 → Scoped
- 계산기 같은 서비스 → Transient
8️⃣ DI의 실무 효과 요약
문제 | DI로 해결하는 방법 |
---|---|
코드 수정 폭발 | 인터페이스에만 의존하면 교체 쉬움 |
테스트 힘듦 | Mock 객체로 주입해서 테스트 가능 |
기능 확장 힘듦 | 새로운 구현체 추가만 하면 끝 |
✨ 정리 그림
[NotificationService] ───▶ [IMessageSender] ◀─── [EmailSender]
◀─── [SmsSender]
NotificationService는 IMessageSender만 본다.
EmailSender든, SmsSender든, KakaoSender든 갈아끼우기만 하면 됨!
✅ 최종 요약
핵심 포인트 | 요약 |
---|---|
의존성(Dependency) | 내가 필요한 다른 것 |
문제 | 직접 new하면 코드가 딱 묶여버림 |
해결책 | 외부에서 주입해라 (DI) |
.NET에서 | 서비스 등록하고, 생성자 주입 받으면 끝 |
📢 다음 챕터 예고 🎓
다음 챕터 | 주제 |
---|---|
Chapter 2.3 | async/await 완벽 이해 – Task, ValueTask까지 |
728x90