다음을 통해 공유


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

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

비고

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

시작하기

시작하려면 DI.Basics라는 새 .NET 콘솔 애플리케이션을 만듭니다. 콘솔 프로젝트를 만드는 가장 일반적인 방법 중 일부는 다음 목록에서 참조됩니다.

프로젝트 파일에서 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.2" />
  </ItemGroup>

</Project>

종속성 주입 기본 사항

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

.NET에서 DI에 대한 추상화는 Microsoft.Extensions.DependencyInjection.Abstractions NuGet 패키지에 정의되어 있습니다.

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

.NET에서 DI는 서비스를 추가하고 이를 IServiceCollection에서 구성하여 관리됩니다. 서비스가 등록되면 메서드를 IServiceProvider 호출하여 인스턴스가 BuildServiceProvider 빌드됩니다. 등록된 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 메서드는 다음 작업을 수행합니다.

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

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

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

        console.WriteLine(farewell);

        return farewell;
    }
}

인터페이스 FarewellService 가 아닌 구체적인 형식을 나타냅니다. 소비자가 액세스할 수 있도록 public로 선언되어야 합니다. internal로 선언된 다른 서비스 구현 형식과 sealed과는 달리, 이 코드는 모든 서비스가 반드시 인터페이스일 필요는 없음을 보여 줍니다. 또한 서비스 구현은 상속을 방지하고 sealed 어셈블리에 대한 액세스를 제한하는 것일 수 internal 있음을 보여 줍니다.

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 구현 팩토리 오버로드를 사용하여 IsEnabledtrue으로 설정된 DefaultConsole 타입을 반환합니다.
    • IGreetingService 형식은 DefaultGreetingService 구현 형식과 함께 추가됩니다.
    • FarewellService이(가) 구체적인 형식으로 추가됩니다.
  • ServiceProvider을(를) ServiceCollection에서 빌드합니다.
  • IGreetingServiceFarewellService 서비스를 해결합니다.
  • 해결된 서비스를 사용하여 이름이 지정된 David사람에게 인사하고 작별 인사를 합니다.

IsEnabled 속성을 false로 업데이트하면 DefaultConsoleGreetSayGoodbye 메서드는 결과 메시지를 콘솔에 기록하는 것을 생략합니다. 이와 같은 변경은 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));

이전의 코드는 ServiceCollectionIConsole 서비스가 등록된 방법과 동일합니다. 이 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 형식을 서비스 및 구현 형식으로 설명합니다. 서비스는 싱글톤 서비스로 등록됩니다.

참고하십시오