다음을 통해 공유


빠른 시작: .NET의 종속성 주입 기본 사항

이 빠른 시작 가이드에서는 .NET 콘솔 앱을 작성하고, 수동으로 ServiceCollection 및 해당 ServiceProvider을 생성합니다. DI(종속성 주입)를 사용하여 서비스를 등록하고 해결하는 방법을 알아봅니다. 이 문서에서는 Microsoft.Extensions.DependencyInjection NuGet 패키지를 사용하여 .NET에서 DI의 기본 사항을 보여 줍니다.

비고

이 문서에서는 일반 호스트 기능을 활용하지 않습니다. 보다 포괄적인 가이드는 .NET에서 종속성 주입 사용을 참조하세요.

시작하기

시작하려면 DI.Basics라는 새 .NET 콘솔 애플리케이션을 만듭니다. Visual Studio에서 파일 > 새 > 프로젝트를 선택하거나 .NET CLI를 사용하여 명령어를 입력합니다dotnet new console.

다음으로 프로젝트 파일에서 Microsoft.Extensions.DependencyInjection 에 패키지 참조를 추가합니다. 패키지를 추가한 후 프로젝트가 DI.Basics.csproj 파일의 다음 XML과 유사한지 확인합니다.

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net10.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.5" />
  </ItemGroup>

</Project>

종속성 주입 기본 사항

종속성 주입은 하드 코딩된 종속성을 제거하고 애플리케이션을 보다 유지 관리하고 테스트할 수 있도록 하는 데 사용할 수 있는 디자인 패턴입니다. DI는 클래스와 해당 종속성 간에 IoC(Inversion of Control) 를 달성하기 위한 기술입니다.

Microsoft.Extensions.DependencyInjection.Abstractions NuGet 패키지는 .NET에서 DI에 대한 추상성을 정의합니다.

  • IServiceCollection: 서비스 설명자 컬렉션에 대한 계약을 정의합니다.
  • IServiceProvider: 서비스 개체를 검색하는 메커니즘을 정의합니다.
  • ServiceDescriptor: 서비스 유형, 구현 및 수명을 가진 서비스에 대해 설명합니다.

.NET에서는 서비스를 추가하고 IServiceCollection에서 해당 서비스를 구성하여 DI를 관리합니다. 서비스를 등록한 후 메서드를 BuildServiceProvider 호출하여 인스턴스를 빌드합니다 IServiceProvider . IServiceProvider는 등록된 모든 서비스의 컨테이너 역할을 하며, 이를 사용하여 서비스를 해결합니다.

예제 서비스 만들기

모든 서비스가 동일하게 만들어지는 것은 아닙니다. 일부 서비스에는 서비스 컨테이너가 해당 인스턴스를 받을 때마다(일시적) 새 인스턴스가 필요한 반면, 다른 서비스는 요청(범위) 또는 앱의 전체 수명(싱글톤)에서 공유되어야 합니다. 서비스 수명에 대한 자세한 내용은 서비스 수명을 참조하세요.

마찬가지로 일부 서비스는 구체적인 형식만 노출하고 다른 서비스는 인터페이스와 구현 형식 간의 계약으로 표현됩니다. 이러한 개념을 설명하는 데 도움이 되는 여러 가지 서비스 변형을 만듭니다.

IConsole.cs 새 C# 파일을 만들고 다음 코드를 추가합니다.

public interface IConsole
{
    void WriteLine(string message);
}

이 파일은 IConsole 단일 메서드 WriteLine를 노출하는 인터페이스를 정의합니다. 다음으로 DefaultConsole.cs 새 C# 파일을 만들고 다음 코드를 추가합니다.

internal sealed class DefaultConsole : IConsole
{
    public bool IsEnabled { get; set; } = true;

    void IConsole.WriteLine(string message)
    {
        if (IsEnabled is false)
        {
            return;
        }

        Console.WriteLine(message);
    }
}

앞의 코드는 인터페이스의 기본 구현을 IConsole 나타냅니다. WriteLine 메서드는 IsEnabled 속성을 기반으로 콘솔에 조건부로 씁니다.

팁 (조언)

구현의 명명은 개발 팀이 동의해야 하는 선택 사항입니다. Default 접두사는 인터페이스의 기본 구현을 나타내는 일반적인 규칙이지만 필수는 아닙니다.

다음으로 , IGreetingService.cs 파일을 만들고 다음 C# 코드를 추가합니다.

public interface IGreetingService
{
    string Greet(string name);
}

그런 다음 DefaultGreetingService.cs 새 C# 파일을 추가하고 다음 코드를 추가합니다.

internal sealed class DefaultGreetingService(
    IConsole console) : IGreetingService
{
    public string Greet(string name)
    {
        var greeting = $"Hello, {name}!";

        console.WriteLine(greeting);

        return greeting;
    }
}

앞의 코드는 인터페이스의 기본 구현을 IGreetingService 나타냅니다. 서비스 구현에는 IConsole 기본 생성자 매개 변수가 필요합니다. Greet 메서드는 다음 작업을 수행합니다.

  • greeting을(를) 기반으로 name을(를) 생성합니다.
  • WriteLine 인스턴스에서 IConsole 메서드를 호출합니다.
  • 호출자에게 greeting 반환합니다.

DefaultGreetingService 클래스는 서비스 구현을 통해 상속을 방지할 수 있으며, 어셈블리에 대한 액세스를 제한하기 위해 seal을 사용 internal할 수 있음을 보여 줍니다.

마지막으로 만들 서비스는 FarewellService.cs 파일입니다. 계속하기 전에 다음 C# 코드를 추가합니다.

public class FarewellService(IConsole console)
{
    public string SayGoodbye(string name)
    {
        var farewell = $"Goodbye, {name}!";

        console.WriteLine(farewell);

        return farewell;
    }
}

인터페이스 FarewellService 가 아닌 구체적인 형식을 나타냅니다. 소비자가 액세스할 수 있도록, public로 선언해야 합니다. 다른 서비스 구현 유형을 internalsealed로 선언하는 것과 달리, 이 코드는 모든 서비스가 반드시 인터페이스여야 하는 것은 아님을 보여줍니다.

Program 클래스 업데이트

Program.cs 파일을 열고 기존 코드를 다음 C# 코드로 바꿉다.

using Microsoft.Extensions.DependencyInjection;

// 1. Create the service collection.
var services = new ServiceCollection();

// 2. Register (add and configure) the services.
services.AddSingleton<IConsole>(
    implementationFactory: static _ => new DefaultConsole
    {
        IsEnabled = true
    });
services.AddSingleton<IGreetingService, DefaultGreetingService>();
services.AddSingleton<FarewellService>();

// 3. Build the service provider from the service collection.
var serviceProvider = services.BuildServiceProvider();

// 4. Resolve the services that you need.
var greetingService = serviceProvider.GetRequiredService<IGreetingService>();
var farewellService = serviceProvider.GetRequiredService<FarewellService>();

// 5. Use the services
var greeting = greetingService.Greet("David");
var farewell = farewellService.SayGoodbye("David");

앞의 코드는 다음 방법을 보여 줍니다.

  • ServiceCollection 인스턴스를 만듭니다.
  • 이곳에서 서비스를 등록하고 구성합니다.ServiceCollection
    • IConsole 구현 팩터리 오버로드를 사용하여 서비스를 제공합니다. DefaultConsole 형식을 반환하고, IsEnabled 속성은 true로 설정합니다.
    • IGreetingService 서비스의 해당 구현 유형은 DefaultGreetingService입니다.
    • FarewellService 서비스는 구체적인 형식으로 제공됩니다.
  • ServiceProvider을(를) ServiceCollection에서 빌드합니다.
  • IGreetingServiceFarewellService 서비스를 해결합니다.
  • 해결된 서비스를 사용하여 이름이 지정된 David사람에게 인사하고 작별 인사를 합니다.

IsEnabled DefaultConsolefalse 속성을 업데이트하면 GreetSayGoodbye 메서드가 결과 메시지를 콘솔에 기록하는 단계를 생략합니다. 이 변경은 IConsole 서비스가 IGreetingServiceFarewellService 서비스에 앱의 동작에 영향을 주는 의존성으로 주입됨을 보여줍니다.

이러한 모든 서비스는 싱글톤으로 등록됩니다. 이 샘플의 경우 일시적 또는 범위가 지정된 서비스로 등록하는 경우 동일하게 작동합니다.

중요합니다

이 모순된 예제에서는 서비스 수명이 중요하지 않습니다. 실제 애플리케이션에서 각 서비스의 수명을 신중하게 고려합니다.

샘플 앱 실행

샘플 앱을 실행하려면 Visual Studio 또는 Visual Studio Code에서 F5 키를 누르거나 터미널에서 명령을 실행 dotnet run 합니다. 앱이 완료되면 다음과 같은 출력이 표시됩니다.

Hello, David!
Goodbye, David!

서비스 설명자

서비스를 ServiceCollection 추가하는 데 가장 일반적으로 사용되는 API는 다음과 같은 수명 명명된 제네릭 확장 메서드입니다.

  • AddSingleton<TService>
  • AddTransient<TService>
  • AddScoped<TService>

이러한 메서드는 ServiceDescriptor 인스턴스를 생성하고 그것을 ServiceCollection에 추가하는 편리한 메서드입니다. 서비스 ServiceDescriptor 유형, 구현 유형 및 수명을 가진 서비스를 설명하는 간단한 클래스입니다. 구현 팩터리 및 인스턴스를 설명할 수도 있습니다.

ServiceCollection에 등록한 각 서비스에 대해, 인스턴스 ServiceDescriptor를 사용하여 Add 메서드를 직접 호출할 수도 있습니다. 다음 예제를 고려하세요.

services.Add(ServiceDescriptor.Describe(
    serviceType: typeof(IConsole),
    implementationFactory: static _ => new DefaultConsole
    {
        IsEnabled = true
    },
    lifetime: ServiceLifetime.Singleton));

이전의 코드는 IConsoleServiceCollection 서비스가 등록된 방법과 동일합니다. 이 메서드는 Add 메서드가 IConsole 서비스를 설명하는 ServiceDescriptor 인스턴스를 추가합니다. 정적 메서드 ServiceDescriptor.Describe는 다양한 ServiceDescriptor 생성자에 위임합니다. 서비스에 해당하는 코드를 고려합니다.IGreetingService

services.Add(ServiceDescriptor.Describe(
    serviceType: typeof(IGreetingService),
    implementationType: typeof(DefaultGreetingService),
    lifetime: ServiceLifetime.Singleton));

앞의 IGreetingService 코드는 서비스 유형, 구현 유형 및 수명을 사용하여 서비스를 설명합니다. 마지막으로 서비스에 해당하는 코드를 고려합니다.FarewellService

services.Add(ServiceDescriptor.Describe(
    serviceType: typeof(FarewellService),
    implementationType: typeof(FarewellService),
    lifetime: ServiceLifetime.Singleton));

앞의 코드는 구체적인 FarewellService 형식을 서비스 및 구현 형식으로 설명합니다. 서비스는 싱글톤 서비스로 등록됩니다.

참고하십시오