다음을 통해 공유


종속성 주입

이 콘텐츠는 ‘.NET MAUI를 사용하는 엔터프라이즈 애플리케이션 패턴’ eBook에서 발췌한 것으로, .NET Docs에서 제공되거나 오프라인으로 읽을 수 있는 다운로드 가능한 무료 PDF로 제공됩니다.

‘.NET MAUI를 사용하는 엔터프라이즈 애플리케이션 패턴’ eBook 표지 썸네일.

일반적으로 클래스 생성자는 개체를 인스턴스화할 때 호출되고 개체에 필요한 모든 값은 생성자에 인수로 전달됩니다. 이것은 생성자 주입이라고 하는 종속성 주입의 예입니다. 개체에 필요한 종속성이 생성자에 주입됩니다.

종속성을 인터페이스 형식으로 지정하면 종속성 주입을 통해 이러한 형식에 종속된 코드에서 구체적인 형식을 분리할 수 있습니다. 일반적으로 인터페이스와 추상 형식 간의 등록 및 매핑 목록을 포함하는 컨테이너와 이러한 형식을 구현하거나 확장하는 구체적인 형식을 사용합니다.

속성 setter 주입메서드 호출 주입 같은 다른 유형의 종속성 주입도 있지만 흔하지는 않습니다. 따라서 이 장에서는 종속성 주입 컨테이너를 사용하여 생성자 주입을 수행하는 데만 집중합니다.

종속성 주입 소개

종속성 주입은 IoC(Inversion of Control) 패턴의 특수 버전으로, 반전과 관련된 고려 사항은 필요한 종속성을 얻는 프로세스입니다. 종속성 주입을 사용하면 다른 클래스가 런타임에 개체에 종속성을 주입합니다. 다음 코드 예제에서는 종속성 주입을 사용할 때 ProfileViewModel 클래스를 구성하는 방법을 보여 줍니다.

private readonly ISettingsService _settingsService;
private readonly IAppEnvironmentService _appEnvironmentService;

public ProfileViewModel(
    IAppEnvironmentService appEnvironmentService,
    IDialogService dialogService, 
    INavigationService navigationService, 
    ISettingsService settingsService)
    : base(dialogService, navigationService, settingsService)
{
    _appEnvironmentService = appEnvironmentService;
    _settingsService = settingsService;

    // Omitted for brevity
}

ProfileViewModel 생성자는 여러 인터페이스 개체 인스턴스를 다른 클래스에 의해 주입된 인수로 받습니다. ProfileViewModel 클래스의 유일한 종속성은 인터페이스 형식에 있습니다. 따라서 ProfileViewModel 클래스에는 인터페이스 개체 인스턴스화를 담당하는 클래스에 대한 지식이 없습니다. 인터페이스 개체를 인스턴스화하고 ProfileViewModel 클래스에 삽입하는 클래스를 종속성 주입 컨테이너라고 합니다.

종속성 주입 컨테이너는 클래스 인스턴스를 인스턴스화하고 컨테이너의 구성에 따라 수명을 관리하는 기능을 제공하여 개체 간의 결합을 줄입니다. 개체를 만드는 동안 컨테이너는 개체에 필요한 모든 종속성을 주입합니다. 해당 종속성이 아직 만들어지지 않은 경우 컨테이너는 먼저 해당 종속성을 만들고 확인합니다.

종속성 주입 컨테이너를 사용하는 경우 다음과 같은 몇 가지 이점이 있습니다.

  • 컨테이너를 사용하면 클래스가 종속성을 찾고 수명을 관리할 필요가 없습니다.
  • 컨테이너를 사용하면 클래스에 영향을 주지 않고 구현된 종속성을 매핑할 수 있습니다.
  • 컨테이너는 종속성을 모의로 만들 수 있도록 하여 테스트를 용이하게 합니다.
  • 컨테이너는 새 클래스를 앱에 쉽게 추가할 수 있도록 하여 유지 관리 가능성을 높입니다.

MVVM을 사용하는 .NET MAUI 앱의 컨텍스트에서 종속성 주입 컨테이너는 일반적으로 뷰 등록 및 확인, 뷰 모델 등록 및 확인, 서비스 등록 및 뷰 모델에 주입하는 데 사용됩니다.

.NET에서 사용할 수 있는 많은 종속성 주입 컨테이너가 있습니다. eShop 다중 플랫폼 앱은 앱에서 보기, 보기 모델 및 서비스 클래스의 인스턴스화를 관리하는 데 사용합니다 Microsoft.Extensions.DependencyInjection . Microsoft.Extensions.DependencyInjection에서는 느슨하게 결합된 앱을 쉽게 빌드할 수 있으며, 형식 매핑 및 개체 인스턴스를 등록하고, 개체를 확인하고, 개체 수명을 관리하고, 종속 개체를 확인하는 개체의 생성자에 주입하는 메서드를 포함하여 종속성 주입 컨테이너에서 일반적으로 발견되는 모든 기능을 제공합니다. Microsoft.Extensions.DependencyInjection에 대한 자세한 내용은 .NET에서 종속성 주입을 참조하세요.

.NET MAUI에서 MauiProgram 클래스는 CreateMauiApp 메서드를 호출하여 MauiAppBuilder 개체를 만듭니다. MauiAppBuilder 개체에는 뷰, 뷰 모델 및 종속성 주입을 위한 서비스와 같은 구성 요소를 등록할 수 있는 위치를 제공하는 IServiceCollection 형식의 Services 속성이 있습니다. Services 속성에 등록된 모든 구성 요소는 MauiAppBuilder.Build 메서드가 호출될 때 종속성 주입 컨테이너에 제공됩니다.

런타임 시 컨테이너는 요청된 개체에 대해 서비스를 인스턴스화하기 위해 요청되는 서비스의 구현을 알고 있어야 합니다. eShop 다중 플랫폼 앱에서는 개체를 IAppEnvironmentService인스턴스화하기 전에 , IDialogServiceISettingsServiceINavigationService인터페이스를 ProfileViewModel 확인해야 합니다. 여기에는 컨테이너가 다음 작업을 수행하는 경우가 포함됩니다.

  • 인터페이스를 구현하는 개체를 인스턴스화하는 방법을 결정합니다. 이것을 등록이라고 합니다.
  • 필요한 인터페이스 및 ProfileViewModel 개체를 구현하는 개체를 인스턴스화합니다. 이것을 ‘확인’이라고 합니다.

결국 앱은 ProfileViewModel 개체를 사용하여 완료되며 가비지 수집에 사용할 수 있게 됩니다. 이 시점에서 가비지 수집기는 다른 클래스가 동일한 인스턴스를 공유하지 않는 경우 단기 인터페이스 구현을 삭제해야 합니다.

등록

종속성을 개체에 주입하려면 먼저 종속성 유형을 컨테이너에 등록해야 합니다. 형식을 등록하려면 컨테이너에 인터페이스와 인터페이스를 구현하는 구체적인 형식을 전달해야 합니다.

코드를 통해 컨테이너에 형식 및 개체를 등록하는 방법에는 다음 두 가지가 있습니다.

  • 컨테이너에 형식 또는 매핑을 등록합니다. 이것을 임시 등록이라고 합니다. 필요한 경우 컨테이너는 지정된 형식의 인스턴스를 빌드합니다.
  • 컨테이너의 기존 개체를 싱글톤으로 등록합니다. 필요한 경우 컨테이너는 기존 개체에 대한 참조를 반환합니다.

참고

종속성 주입 컨테이너가 항상 적합한 것은 아닙니다. 종속성 주입은 작은 앱에 적절하거나 유용하지 않을 수 있는 추가 복잡성 및 요구 사항을 발생합니다. 클래스에 종속성이 없거나 다른 형식에 대한 종속성이 없는 경우 컨테이너에 배치하는 것은 적절하지 않을 수 있습니다. 또한 형식에 필수적인 단일 종속성 집합이 클래스에 있고 변경될 가능성이 없으면 컨테이너에 배치하는 것이 부적절할 수 있습니다.

종속성 주입이 필요한 형식의 등록은 앱의 단일 메서드에서 수행해야 합니다. 이 메서드는 해당 클래스 간의 종속성을 인식하도록 앱의 수명 주기 초기에 호출되어야 합니다. eShop 다중 플랫폼 앱은 이 메서드를 MauiProgram.CreateMauiApp 수행합니다. 다음 코드 예제에서는 eShop 다중 플랫폼 앱이 클래스에서 MauiProgram 선언하는 CreateMauiApp 방법을 보여 줍니다.

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
        => MauiApp.CreateBuilder()
            .UseMauiApp<App>()
            // Omitted for brevity            
            .RegisterAppServices()
            .RegisterViewModels()
            .RegisterViews()
            .Build();
}

MauiApp.CreateBuilder 메서드는 종속성을 등록하는 데 사용할 수 있는 MauiAppBuilder 개체를 만듭니다. eShop 다중 플랫폼 앱의 많은 종속성을 등록해야 하므로 확장 메서드RegisterAppServicesRegisterViewModels는 구성되고 RegisterViews 유지 관리 가능한 등록 워크플로를 제공하기 위해 만들어졌습니다. 다음 코드에서는 RegisterViewModels 메서드를 보여 줍니다.

public static MauiAppBuilder RegisterViewModels(this MauiAppBuilder mauiAppBuilder)
{
    mauiAppBuilder.Services.AddSingleton<ViewModels.MainViewModel>();
    mauiAppBuilder.Services.AddSingleton<ViewModels.LoginViewModel>();
    mauiAppBuilder.Services.AddSingleton<ViewModels.BasketViewModel>();
    mauiAppBuilder.Services.AddSingleton<ViewModels.CatalogViewModel>();
    mauiAppBuilder.Services.AddSingleton<ViewModels.ProfileViewModel>();

    mauiAppBuilder.Services.AddTransient<ViewModels.CheckoutViewModel>();
    mauiAppBuilder.Services.AddTransient<ViewModels.OrderDetailViewModel>();
    mauiAppBuilder.Services.AddTransient<ViewModels.SettingsViewModel>();
    mauiAppBuilder.Services.AddTransient<ViewModels.CampaignViewModel>();
    mauiAppBuilder.Services.AddTransient<ViewModels.CampaignDetailsViewModel>();

    return mauiAppBuilder;
}

이 메서드는 MauiAppBuilder 인스턴스를 수신하며, Services 속성을 사용하여 뷰 모델을 등록할 수 있습니다. 애플리케이션의 요구 사항에 따라 수명이 다른 서비스를 추가해야 할 수 있습니다. 다음 표에서는 이러한 다양한 등록 수명을 선택할 수 있는 시기에 대한 정보를 제공합니다.

메서드 설명
AddSingleton<T> 애플리케이션의 수명 동안 유지되는 개체의 단일 인스턴스를 만듭니다.
AddTransient<T> 확인 중에 요청될 때 개체의 새 인스턴스를 만듭니다. 임시 개체에는 미리 정의된 수명이 없지만 일반적으로 호스트의 수명을 따릅니다.

참고

뷰 모델은 인터페이스에서 상속되지 않으므로 AddSingleton<T>AddTransient<T> 메서드에 제공된 구체적인 형식만 필요합니다.

CatalogViewModel은 애플리케이션의 루트 근처에서 사용되며 항상 사용할 수 있어야 하므로 AddSingleton<T>에 등록하는 것이 좋습니다. CheckoutViewModelOrderDetailViewModel 등의 다른 뷰 모델은 상황에 따라 탐색되거나 나중에 애플리케이션에서 사용됩니다. 항상 사용되지는 않을 수 있는 구성 요소가 있다고 가정해 보겠습니다. 이 경우 메모리 또는 계산 집약적이거나 Just-In-Time 데이터가 필요한 경우 AddTransient<T> 등록에 더 적합할 수 있습니다.

서비스를 추가하는 또 다른 일반적인 방법은 AddSingleton<TService, TImplementation>AddTransient<TService, TImplementation> 메서드를 사용하는 것입니다. 이러한 메서드는 인터페이스 정의와 구체적인 구현이라는 두 가지 입력 형식을 사용합니다. 이 형식의 등록은 인터페이스를 기준으로 서비스를 구현하는 경우에 가장 적합합니다. 아래 코드 예제에서는 SettingsService 구현을 사용하여 ISettingsService 인터페이스를 등록합니다.

public static MauiAppBuilder RegisterAppServices(this MauiAppBuilder mauiAppBuilder)
{
    mauiAppBuilder.Services.AddSingleton<ISettingsService, SettingsService>();
    // Omitted for brevity...
}

모든 서비스가 등록되면 MauiAppBuilder.Build 메서드를 호출하여 MauiApp을 만들고 종속성 주입 컨테이너를 등록된 모든 서비스로 채워야 합니다.

Important

메서드가 Build 호출되면 종속성 주입 컨테이너는 변경할 수 없으며 더 이상 업데이트하거나 수정할 수 없습니다. 호출 Build하기 전에 애플리케이션 내에서 필요한 모든 서비스가 등록되었는지 확인합니다.

해결

형식을 등록한 후에는 종속성으로 확인하거나 주입할 수 있습니다. 형식이 확인되고 컨테이너가 새 인스턴스를 만들어야 하는 경우 모든 종속성이 인스턴스에 주입됩니다.

일반적으로 형식이 확인되면 다음 세 가지 중 하나가 발생합니다.

  1. 형식이 등록되지 않은 경우 컨테이너는 예외를 throw합니다.
  2. 형식이 싱글톤으로 등록된 경우 컨테이너는 싱글톤 인스턴스를 반환합니다. 이번에 해당 형식을 처음 호출하는 경우라면 컨테이너가 필요한 경우 해당 형식을 만들고 이에 대한 참조를 유지 관리합니다.
  3. 형식이 임시로 등록된 경우 컨테이너는 새 인스턴스를 반환하고 해당 인스턴스에 대한 참조를 유지하지 않습니다.

.NET MAUI는 필요에 따라 등록된 구성 요소를 해결하는 다양한 방법을 제공합니다. 종속성 주입 컨테이너에 대한 액세스 권한을 얻는 가장 직접적인 방법은 Handler.MauiContext.Services를 사용하는 Element를 통한 것입니다. 관련 예시는 아래와 같습니다.

var settingsService = this.Handler.MauiContext.Services.GetServices<ISettingsService>();

Element 내에서 또는 Element의 생성자 외부에서 서비스를 확인해야 하는 경우에 유용할 수 있습니다.

주의

ElementHandler 속성이 null일 수 있으므로 이러한 상황을 처리해야 할 수 있습니다. 자세한 내용은 Microsoft 설명서 센터에서 처리기 수명 주기를 참조하세요.

.NET MAUI에 대해 Shell 컨트롤을 사용하는 경우 탐색 중에 종속성 주입 컨테이너를 암시적으로 호출하여 개체를 만듭니다. Shell 컨트롤을 설정할 때 Routing.RegisterRoute 메서드는 아래 예제와 같이 경로를 View에 연결합니다.

Routing.RegisterRoute("Filter", typeof(FiltersView));

Shell 탐색 중에는 FiltersView 등록을 찾으며, 찾은 경우 해당 뷰를 만들고 모든 종속성을 생성자에 주입합니다. 아래 코드 예제와 같이 CatalogViewModelFiltersView에 주입됩니다.

namespace eShop.Views;

public partial class FiltersView : ContentPage
{
    public FiltersView(CatalogViewModel viewModel)
    {
        BindingContext = viewModel;

        InitializeComponent();
    }
}

종속성 주입 컨테이너는 뷰 모델 인스턴스를 만드는 데 적합합니다. 뷰 모델에 종속성이 있는 경우 필요한 서비스의 생성 및 주입을 처리합니다. MauiProgram 클래스의 CreateMauiApp 메서드 간에 있을 수 있는 모든 종속성 및 뷰 모델을 등록해야 합니다.

요약

종속성 주입을 사용하면 이러한 형식에 종속된 코드에서 구체적인 형식을 분리할 수 있습니다. 일반적으로 인터페이스와 추상 형식 간의 등록 및 매핑 목록을 포함하는 컨테이너와 이러한 형식을 구현하거나 확장하는 구체적인 형식을 사용합니다.

Microsoft.Extensions.DependencyInjection에서는 느슨하게 결합된 앱을 쉽게 빌드할 수 있으며, 형식 매핑 및 개체 인스턴스를 등록하고, 개체를 확인하고, 개체 수명을 관리하고, 종속 개체를 확인하는 개체의 생성자에 주입하는 메서드를 포함하여 종속성 주입 컨테이너에서 일반적으로 발견되는 모든 기능을 제공합니다.