Dependency Injection 기초
Coddy C# 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 70개 중 46번째.
Dependency Injection(DI)은 클래스가 의존성을 직접 생성하는 대신 외부에서 받는 기법입니다. 이를 통해 코드를 더 유연하고 테스트하기 쉬우며 결합도가 낮게 만들 수 있습니다.
DI 없이 클래스는 자체 dependency를 생성하므로 변경하거나 테스트하기 어렵습니다:
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는 Email, SMS 또는 테스트를 위한 모의 발신자 중 무엇을 사용하는지 알지도 못하고 신경 쓰지도 않습니다. 객체가 생성될 때 dependency가 "injected"됩니다.
IMessageSender emailSender = new EmailSender();
OrderService service = new OrderService(emailSender);
service.PlaceOrder(); // 이메일: 주문 완료이 패턴은 구체적인 구현이 아니라 추상화(인터페이스)에 의존한다는 원칙을 따릅니다. 이 패턴을 사용하면 이를 사용하는 클래스를 수정하지 않고도 구현을 교체할 수 있으므로, 단위 테스트를 수행하고 유연한 아키텍처를 유지하는 데 필수적입니다.
챌린지
쉬움의존성 주입의 강력함을 보여 주는 Logging 시스템을 만들어 봅시다. 로그가 기록되는 방식을 하드코딩하는 대신, 세부 사항을 알 필요 없이 어떤 Logging 대상(콘솔, file 시뮬레이션 또는 그 밖의 무엇이든)과도 작동할 수 있는 유연한 Logger class를 만들게 됩니다.
코드를 세 개의 file에 나누어 구성합니다.
ILogWriter.cs:Loggingnamespace에ILogWriter라는 interface를 Define합니다. 이 interface는 모든 Logging 대상에 대한 계약을 나타내며, 메시지가 어디에 기록되었는지를 설명하는 string을 반환하는 단일 methodWrite(string message)를 가져야 합니다.LogWriters.cs:Loggingnamespace에ILogWriter의 두 가지 구체적인 구현을 Create합니다.ConsoleLogWriter- 해당Writemethod는Console: {message}를 반환합니다.FileLogWriter- constructor를 통해 filename을 받고, 해당Writemethod는File[{filename}]: {message}를 반환합니다.
Logger.cs:Loggingnamespace에 constructor injection을 통해 dependency를 받는Loggerclass를 Create합니다.Logger는 다음을 수행해야 합니다.- constructor를 통해
ILogWriter를 받고 이를 private field에 저장합니다. - injected writer에 delegates하고 그 결과를 반환하는
Log(string message)method를 have합니다.
- constructor를 통해
Program.cs: 서로 다른 두 개의 logger를 Create하여 dependency injection을 보여 줍니다. 하나는ConsoleLogWriter를 사용하고 다른 하나는FileLogWriter를 사용합니다. 어떤 dependency가 injected되었는지에 따라 동일한Loggerclass가 다르게 작동하는 모습을 보여 줍니다.
두 개의 inputs를 받습니다.
- file logger의 filename
- 기록할 message
ConsoleLogWriter를 사용하여 Logger를 Create하고 message를 기록합니다. 그런 다음 (input의 filename을 사용하여) FileLogWriter를 사용하는 또 다른 Logger를 Create하고 동일한 message를 기록합니다. 각 결과를 한 줄씩 Print합니다.
예를 들어 inputs가 app.log와 System started라면 output은 다음과 같아야 합니다.
Console: System started
File[app.log]: System startedLogger class 자체는 전혀 변경되지 않는다는 점에 주목하세요. 어떤 ILogWriter 구현과도 작동합니다. 이것이 dependency injection의 본질입니다. Logger는 abstraction(interface)에 의존하고, 구체적인 구현은 외부에서 "injected"됩니다. 나중에 Logger class를 전혀 수정하지 않고도 DatabaseLogWriter 또는 CloudLogWriter를 쉽게 Add할 수 있습니다!
직접 해보기
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로 또 다른 Logger를 생성하고 (filename 사용) 같은 메시지를 로그하세요
// 결과 출력
}
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
8심화 OOP 개념
상속보다 합성(Composition)제네릭 (클래스 및 메서드)델리게이트와 이벤트어트리뷰트와 리플렉션IDisposable과 using 문Dependency Injection 기초직접 연습해 보세요: 온라인 C# 컴파일러