의존성 주입
객체의 종속성을 객체 내부에서 직접 생성하는 것이 아니라 외부에서 제공하는 방식
public class EmailService
{
public void SendEmail(string to, string subject, string body)
{
// 이메일 전송 로직
}
}
public class UserService
{
private EmailService _emailService = new EmailService();
public void RegisterUser(string email)
{
// 사용자 등록 로직
_emailService.SendEmail(email, "Welcome!", "Welcome to our service!");
}
}
위 코드에서 `UserService` 클래스는 `EmailService` 클래스에 의존하고 있다.
즉, A 클래스가 B 클래스의 인스턴스를 직접 생성하고 사용한다면 의존성이 강하다고 할 수 있다.
위 코드를 의존성 주입 방식으로 변경하면 아래와 같다.
public class UserService
{
private readonly IEmailService _emailService;
public UserService(IEmailService emailService)
{
_emailService = emailService;
}
public void RegisterUser(string email)
{
// 사용자 등록 로직
_emailService.SendEmail(email, "Welcome!", "Welcome to our service!");
}
}
`UserService` 클래스가 직접 `EmailService`의 인스턴스를 생성하지 않고, 생성자를 통해 주입 받았다.
제어의 역전(Inversion of Control)
기존의 모든 제어를 클라이언트 코드가 가지도록 구현하던 것에서, 외부 프레임워크의 흐름 제어를 받도록 하는 것
인스턴스의 생성 등은 프레임워크가 담당하며, 우리는 프레임워크에서 요구하는 인터페이스를 구현하면 되기 때문에 더욱 유연하고 모듈화된 소프트웨어를 만들 수 있다.
IoC 컨테이너
IoC를 구현하는 프레임워크로 객체 관리, 객체 생성, 의존성 관리 등을 수행함
- `void Register<TInterface, TConcrete>()`: TInterface와 이를 구현하는 TConcrete 클래스 간의 매핑을 등록한다. 이후에 TInterface에 의존하는 인스턴스가 요청될 경우, TConcrete 타입의 개체를 인스턴스에 주입하게 된다.
- `void Register<TClass>()`: TClass를 등록
- `T GetInstance<T>()`: 의존성이 해결된 T타입의 인스턴스를 가져온다. 클래스 타입인 경우 해당 클래스의 인스턴스, 인터페이스 타입인 경우 그 인터페이스를 구현하는 클래스의 인스턴스가 반환된다.
1) .NET의 IoC 컨테이너
Microsoft.Extensions.DependencyInjection 네임스페이스에 포함

- `IServiceCollection`: 서비스 등록을 위한 인터페이스
- `IServiceProvider`: 등록된 서비스의 인스턴스를 제공하는 팩토리
- `ServiceDescripter`: 서비스 등록 정보를 담는 클래스
2) Prism의 IoC 컨테이너
Prism.DryIoc 또는 Prism.Unity Nuget 사용

`IContainerRegistry`: 서비스를 컨테이너에 등록하는 인터페이스
- 관련 메서드: Register, RegisterSingleton, RegisterScoped 등
public partial class App
{
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
// transient
containerRegistry.Register<IDateTimeService, DateTimeService>();
containerRegistry.Register<IMessageService, MessageService>();
// singleton
containerRegistry.RegisterSingleton<IViewModelBase, ViewModelBase>();
}
protected override Window CreateShell()
{
return Container.Resolve<MainWindow>();
}
}
`IContainerProvider`: 의존성 관리 기능을 사용하는 클래스에서 컨테이너를 사용할 수 있도록 함
- 관련 메서드: Resolve, ResolveAll, IsRegistered
public class ViewModelBase : BindableBase, IViewModelBase
{
/// <summary>
/// IoC Container
/// </summary>
protected IContainerProvider Container { get; }
/// <summary>
/// DateTime Service
/// </summary>
protected IDateTimeService DateTimeService { get; }
/// <summary>
/// Message Service
/// </summary>
protected IMessageService MessageService { get; }
public ViewModelBase()
{
}
public ViewModelBase(IContainerProvider containerProvider)
{
Container = containerProvider;
DateTimeService = Container.Resolve<IDateTimeService>();
MessageService = Container.Resolve<IMessageService>();
}
}
의존성 주입이 필요한 이유
| 결합도 감소 |
|
| 유연성 및 확장성 |
|
| 재사용성 |
|
💡 참고
'.NET > WPF' 카테고리의 다른 글
| [WPF] InvokeCommandAction 사용 시 바인딩 업데이트 타이밍 문제 (0) | 2024.11.26 |
|---|---|
| [WPF] 의존성 주입 예제 (2) - Prism.DryIoc 사용 (0) | 2024.11.24 |
| [WPF] 의존성 주입 예제 (1) - Microsoft.Extensions.DependencyInjection 사용 (0) | 2024.11.18 |
| [WPF] 이벤트 라우팅, 버블링(Bubbling), 터널링(Tunneling) (1) | 2024.10.19 |
댓글