Menu
Coddy logo textTech

Dependency Injection 기초

Coddy C# 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 70개 중 46번째.

의존성 주입 (Dependency Injection, DI)은 클래스가 스스로 의존성을 생성하는 대신 외부로부터 의존성을 전달받는 기법입니다. 이를 통해 코드는 더 유연해지고, 테스트 가능해지며, 결합도가 낮아집니다.

DI 없이 클래스가 스스로 의존성을 생성하면, 변경이나 테스트가 어려워집니다:

public class OrderService
{
    private EmailSender sender = new EmailSender();  // 강하게 결합됨
    
    public void PlaceOrder() => sender.Send("Order placed");
}

DI를 사용하면 의존성이 생성자를 통해 전달됩니다:

public interface IMessageSender
{
    void Send(string message);
}

public class EmailSender : IMessageSender
{
    public void Send(string message) => Console.WriteLine("Email: " + message);
}

public class OrderService
{
    private IMessageSender sender;
    
    public OrderService(IMessageSender sender)
    {
        this.sender = sender;
    }
    
    public void PlaceOrder() => sender.Send("Order placed");
}

이제 OrderService는 테스트를 위해 이메일, SMS 또는 모의(mock) 발송자를 사용하는지 여부를 알 필요도 없고 신경 쓰지도 않습니다. 객체가 생성될 때 의존성이 "주입"됩니다:

IMessageSender emailSender = new EmailSender();
OrderService service = new OrderService(emailSender);
service.PlaceOrder();  // 이메일: 주문이 완료되었습니다

이 패턴은 구체적인 구현보다는 추상화(인터페이스)에 의존한다는 원칙을 따릅니다. 이는 해당 구현을 사용하는 클래스를 수정하지 않고도 구현을 교체할 수 있게 해주며, 이는 단위 테스트와 유연한 아키텍처를 유지하는 데 필수적입니다.

challenge icon

챌린지

쉬움

의존성 주입(Dependency Injection)의 강력함을 보여주는 로깅 시스템을 구축해 봅시다. 로그가 작성되는 방식을 하드코딩하는 대신, 구체적인 내용을 알지 못해도 콘솔, 파일 시뮬레이션 등 모든 로깅 대상과 함께 작동할 수 있는 유연한 Logger 클래스를 만들 것입니다.

코드는 세 개의 파일로 구성됩니다:

  • ILogWriter.cs: Logging 네임스페이스에 ILogWriter라는 인터페이스를 정의합니다. 이 인터페이스는 모든 로깅 대상에 대한 계약을 나타내며, 메시지가 작성된 위치를 설명하는 문자열을 반환하는 단일 메서드 Write(string message)를 가져야 합니다.
  • LogWriters.cs: Logging 네임스페이스에 ILogWriter의 두 가지 구체적인 구현체를 만듭니다:
    • ConsoleLogWriter - Write 메서드는 "Console: " + message를 반환합니다.
    • FileLogWriter - 생성자를 통해 파일 이름을 전달받으며, Write 메서드는 "File[" + filename + "]: " + message를 반환합니다.
  • Logger.cs: 생성자 주입을 통해 의존성을 전달받는 Logger 클래스를 Logging 네임스페이스에 만듭니다. Logger는 다음을 수행해야 합니다:
    • 생성자를 통해 ILogWriter를 받아 프라이빗 필드에 저장합니다.
    • 주입된 writer에게 작업을 위임하고 그 결과를 반환하는 Log(string message) 메서드를 가집니다.
  • Program.cs: ConsoleLogWriter를 사용하는 로거와 FileLogWriter를 사용하는 로거, 두 개의 서로 다른 로거를 생성하여 의존성 주입을 시연합니다. 주입된 의존성에 따라 동일한 Logger 클래스가 어떻게 다르게 동작하는지 보여줍니다.

두 개의 입력을 받게 됩니다:

  • 파일 로거에 사용할 파일 이름
  • 로그로 남길 메시지

ConsoleLogWriter를 사용하여 Logger를 생성하고 메시지를 로그로 남깁니다. 그런 다음 (입력받은 파일 이름을 사용하여) FileLogWriter를 사용하는 또 다른 Logger를 생성하고 동일한 메시지를 로그로 남깁니다. 각 결과를 별도의 줄에 출력하세요.

예를 들어, 입력이 app.logSystem started라면 출력은 다음과 같아야 합니다:

Console: System started
File[app.log]: System started

Logger 클래스가 전혀 변경되지 않는다는 점에 주목하세요. 이 클래스는 어떤 ILogWriter 구현체와도 작동합니다. 이것이 의존성 주입의 본질입니다. Logger는 추상화(인터페이스)에 의존하고, 구체적인 구현은 외부에서 "주입"됩니다. 나중에 Logger 클래스를 전혀 수정하지 않고도 DatabaseLogWriterCloudLogWriter를 쉽게 추가할 수 있습니다!

치트 시트

의존성 주입(Dependency Injection, DI)은 클래스가 스스로 의존성을 생성하는 대신 외부로부터 의존성을 전달받는 기술로, 코드를 더 유연하고 테스트 가능하며 결합도를 낮게 만듭니다.

DI가 없는 경우 (강한 결합):

public class OrderService
{
    private EmailSender sender = new EmailSender();  // 강한 결합
    
    public void PlaceOrder() => sender.Send("Order placed");
}

DI를 사용하는 경우 (느슨한 결합):

public interface IMessageSender
{
    void Send(string message);
}

public class EmailSender : IMessageSender
{
    public void Send(string message) => Console.WriteLine("Email: " + message);
}

public class OrderService
{
    private IMessageSender sender;
    
    public OrderService(IMessageSender sender)
    {
        this.sender = sender;
    }
    
    public void PlaceOrder() => sender.Send("Order placed");
}

의존성 주입하기:

IMessageSender emailSender = new EmailSender();
OrderService service = new OrderService(emailSender);
service.PlaceOrder();  // Email: Order placed

DI는 구체적인 구현이 아닌 추상화(인터페이스)에 의존한다는 원칙을 따르며, 이를 통해 사용하는 클래스를 수정하지 않고도 구현체를 교체할 수 있게 합니다.

직접 해보기

using System;
using Logging;

class Program
{
    public static void Main(string[] args)
    {
        // 입력 읽기
        string filename = Console.ReadLine();
        string message = Console.ReadLine();

        // TODO: ConsoleLogWriter를 사용하는 Logger를 생성하고 메시지를 기록합니다
        // 결과 출력

        // TODO: FileLogWriter를 사용하는 (filename을 이용한) 또 다른 Logger를 생성하고 동일한 메시지를 기록합니다
        // 결과 출력
    }
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨