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만 쓸 수 있다
  • SMSSlack 추가하려면?
    • 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