개요
Microsoft.Extensions.Hosting 네임스페이스는 .NET 애플리케이션의 호스팅 모델(Hosting Model)을 제공하는 핵심 구성 요소입니다.
Host 클래스는 이 모델의 시작점 역할을 하며, 애플리케이션의 생명 주기(lifecycle), 구성(configuration), 의존성 주입(dependency injection, DI), 로깅(logging) 등 다양한 인프라 서비스를 중앙 집중식으로 관리합니다.
이 호스팅 모델은 ASP.NET Core 애플리케이션에서 시작되었지만, 현재는 콘솔 애플리케이션, 백그라운드 서비스, Windows 서비스, WPF 애플리케이션 등 다양한 종류의 .NET 애플리케이션에서 표준적인 방식으로 사용되고 있습니다.
1. Host
클래스의 목적과 역할
Host
클래스는 특정 애플리케이션 유형에 구애받지 않고, 공통적인 인프라 기능을 쉽게 통합하고 관리할 수 있도록 설계되었습니다.
- 생명 주기 관리: 애플리케이션의 시작(
StartAsync
) 및 종료(StopAsync
)를 관리하고, 이 과정에서 필요한 서비스들이 올바른 순서로 초기화되고 정리될 수 있도록 합니다. - 의존성 주입 (DI): 애플리케이션 전반에 걸쳐 강력한 DI 컨테이너를 제공하여, 서비스 간의 느슨한 결합을 촉진하고 테스트 용이성을 향상시킵니다.
- 구성 (Configuration):
appsettings.json
, 환경 변수, 명령줄 인수 등 다양한 소스에서 설정을 로드하고 관리하는 일관된 방법을 제공합니다. - 로깅 (Logging): 다양한 로깅 프로바이더(콘솔, 파일, 데이터베이스 등)를 쉽게 통합하고, 로깅 레벨을 유연하게 제어할 수 있도록 합니다.
- 백그라운드 서비스 (IHostedService): 장시간 실행되는 백그라운드 작업을 호스트의 생명 주기에 통합하여 관리할 수 있도록 합니다. 이는 API를 제공하지 않고 단순히 작업을 수행하는 서비스에 특히 유용합니다.
2. Host
클래스 핵심 인터페이스 및 메서드
Host
클래스 자체보다는, 이와 관련된 IHost
및 IHostBuilder
인터페이스를 이해하는 것이 중요합니다.
(1) IHostBuilder
인터페이스
IHostBuilder
는 호스트를 구성(Configure)하는 데 사용되는 인터페이스입니다.
Host.CreateDefaultBuilder()
메서드가 반환하는 객체가 바로 IHostBuilder
타입입니다.
이 빌더를 통해 다음을 정의할 수 있습니다:
ConfigureAppConfiguration
:
설정 소스(파일, 환경 변수 등)를 추가하거나 커스터마이징합니다.
.ConfigureAppConfiguration((context, config) => { config.AddJsonFile("custom.json", optional: true) .AddEnvironmentVariables() .AddCommandLine(args); })
ConfigureLogging
:
로깅 프로바이더(콘솔, 디버그, 파일 등)를 추가하고 로깅 필터링 규칙을 정의합니다.
.ConfigureLogging((context, logging) => { logging.AddConsole() .AddDebug() .SetMinimumLevel(LogLevel.Information); })
ConfigureServices
:
의존성 주입 컨테이너에 애플리케이션의 서비스들을 등록합니다.AddSingleton
,AddScoped
,AddTransient
등의 메서드를 사용하여 서비스의 생명 주기를 지정할 수 있습니다.
.ConfigureServices((context, services) => { services.AddSingleton<ISingletonService, SingletonService>(); services.AddScoped<IScopedService, ScopedService>(); services.AddTransient<ITransientService, TransientService>(); services.AddHostedService<MyBackgroundService>(); })
UseConsoleLifetime
:
콘솔 애플리케이션의 경우,Ctrl+C
와 같은 콘솔 시그널을 감지하여 호스트를 정상적으로 종료하도록 설정합니다.
(이는Host.CreateDefaultBuilder()
에 의해 기본적으로 포함됩니다.)Build()
:IHostBuilder
에 정의된 모든 구성을 기반으로 최종IHost
인스턴스를 생성합니다.
(2) IHost
인터페이스
IHost
는 빌드된 호스트 인스턴스를 나타내며, 실제로 애플리케이션을 실행하고 관리하는 데 사용됩니다.
주요 메서드는 다음과 같습니다
Services
:
등록된 모든 서비스 인스턴스에 접근할 수 있는IServiceProvider
를 제공합니다.
이를 통해 런타임에 필요한 서비스를 resolve(가져오기)할 수 있습니다.StartAsync(CancellationToken cancellationToken)
:
호스트를 비동기적으로 시작합니다. 이 메서드가 호출되면 등록된 모든IHostedService
인스턴스의StartAsync
메서드가 호출됩니다.StopAsync(CancellationToken cancellationToken)
:
호스트를 비동기적으로 종료합니다. 등록된 모든IHostedService
인스턴스의StopAsync
메서드가 호출되어 리소스를 정리할 기회를 줍니다.RunAsync(CancellationToken cancellationToken = default)
:StartAsync
를 호출하고, 호스트가 종료될 때까지 대기한 후StopAsync
를 호출합니다.
대부분의 콘솔 애플리케이션에서 메인 실행 루프로 사용됩니다.
(3) IHostedService
인터페이스
IHostedService
는 호스트의 생명 주기에 통합되어 백그라운드 작업을 수행하는 서비스들을 정의하는 인터페이스입니다.
StartAsync
와 StopAsync
두 가지 메서드를 구현해야 합니다.
StartAsync(CancellationToken cancellationToken)
:
호스트가 시작될 때 호출됩니다. 장시간 실행되는 작업을 시작하는 데 사용됩니다.StopAsync(CancellationToken cancellationToken)
:
호스트가 종료될 때 호출됩니다.StartAsync
에서 시작된 작업을 정상적으로 중지하고 리소스를 정리하는 데 사용됩니다.
3. Host.CreateDefaultBuilder()
메서드의 이점
이 메서드는 많은 “관습(conventions)”을 제공하여 애플리케이션 초기 설정을 간소화하는 장점이 있습니다.
- 일관성:
모든 .NET 애플리케이션 유형(웹, 콘솔, 백그라운드 서비스)에서 동일한 방식으로 인프라를 설정할 수 있게 하여 코드의 일관성을 유지합니다. - 생산성:
설정, 로깅, DI 등 반복적인 초기화 코드를 작성할 필요 없이 바로 비즈니스 로직에 집중할 수 있도록 합니다. - 확장성:
기본 설정을 사용하면서도ConfigureAppConfiguration
,ConfigureServices
등의 메서드를 통해 언제든지 필요에 따라 커스터마이징하고 확장할 수 있습니다. - 테스트 용이성:
DI 컨테이너를 중심으로 설계되어, 단위 테스트 및 통합 테스트에서 의존성을 쉽게 Mocking하거나 대체할 수 있습니다.
참고: Mocking – 테스트 코드 작성 시 가짜 객체(Mock)를 만들어 실제 객체 대신 사용하는 기법
예시 코드: 콘솔 애플리케이션에서 Host
사용하기
콘솔 애플리케이션에서 Host
클래스를 사용하여 백그라운드 서비스(카운터)를 실행하고, 설정 및 로깅을 사용하는 예제
Microsoft.Extensions.Hosting
NuGet 패키지를 설치
dotnet add package Microsoft.Extensions.Hosting
appsettings.json
파일을 추가하고 내용을 작성합니다.
{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft.Hosting.Lifetime": "Information" } }, "AppConfig": { "IntervalSeconds": 2 } }
Services/MyBackgroundService.cs
(커스텀IHostedService
구현):
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Configuration; // IConfiguration 주입을 위해 추가 using System; using System.Threading; using System.Threading.Tasks; namespace MyConsoleApp.Services { // IHostedService를 구현하여 호스트의 생명 주기에 통합될 백그라운드 서비스를 정의합니다. public class MyBackgroundService : IHostedService, IDisposable { private readonly ILogger<MyBackgroundService> _logger; private readonly IConfiguration _configuration; private Timer? _timer; private int _executionCount = 0; private int _intervalSeconds; public MyBackgroundService(ILogger<MyBackgroundService> logger, IConfiguration configuration) { _logger = logger; _configuration = configuration; // appsettings.json에서 설정 값 읽기 _intervalSeconds = _configuration.GetValue<int>("AppConfig:IntervalSeconds", 5); // 기본값 5초 } // 호스트가 시작될 때 호출됩니다. public Task StartAsync(CancellationToken cancellationToken) { _logger.LogInformation("MyBackgroundService가 시작되었습니다."); // 타이머를 설정하여 지정된 간격마다 DoWork 메서드를 호출합니다. _timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromSeconds(_intervalSeconds)); return Task.CompletedTask; // 비동기 작업 시작을 알리고 즉시 반환 } // 타이머에 의해 주기적으로 호출되는 실제 작업 메서드 private void DoWork(object? state) { var count = Interlocked.Increment(ref _executionCount); _logger.LogInformation( "MyBackgroundService가 {count}번째 작업을 수행 중입니다. 현재 시간: {time}", count, DateTimeOffset.Now); } // 호스트가 종료될 때 호출됩니다. public Task StopAsync(CancellationToken cancellationToken) { _logger.LogInformation("MyBackgroundService가 중지되고 있습니다."); // 타이머를 중지하고 리소스를 해제합니다. // Change(Timeout.Infinite, 0)은 타이머를 비활성화하는 표준 방법입니다. _timer?.Change(Timeout.Infinite, 0); _logger.LogInformation("MyBackgroundService가 중지되었습니다."); return Task.CompletedTask; // 작업 완료를 알립니다. } // 리소스 해제를 위한 Dispose 메서드 (IHostedService는 IDisposable을 구현하는 경우가 많음) public void Dispose() { _timer?.Dispose(); } } }
Program.cs
구현:
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using MyConsoleApp.Services; // MyBackgroundService를 사용하기 위해 추가 using System; using System.Threading.Tasks; namespace MyConsoleApp { public class Program { public static async Task Main(string[] args) { Console.WriteLine("콘솔 애플리케이션 호스트가 시작됩니다. 종료하려면 Ctrl+C를 누르세요."); // 1. IHostBuilder 인스턴스 생성: Host.CreateDefaultBuilder()는 기본적인 구성(config, logging, DI)을 설정합니다. var builder = Host.CreateDefaultBuilder(args) // 2. 서비스 컨테이너 구성: 의존성 주입을 위한 서비스들을 등록합니다. .ConfigureServices((hostContext, services) => { // MyBackgroundService를 IHostedService로 등록합니다. // 호스트가 시작될 때 StartAsync가 호출되고, 종료될 때 StopAsync가 호출됩니다. services.AddHostedService<MyBackgroundService>(); // 여기에 다른 서비스들도 등록할 수 있습니다. 예: // services.AddSingleton<IMyService, MyService>(); // services.AddScoped<IOtherService, OtherService>(); }) // 3. 로깅 구성 (선택 사항: CreateDefaultBuilder가 기본 설정 제공하지만, 커스터마이징 가능) .ConfigureLogging(logging => { logging.ClearProviders(); // 기본 로깅 프로바이더를 지우고 새로 추가하거나 logging.AddConsole(); // 콘솔 로거를 추가합니다. // logging.AddDebug(); // 디버그 로거 추가 }); // 4. 기타 구성 (선택 사항: 파일 경로, 환경 등) // .UseContentRoot(Directory.GetCurrentDirectory()) // .UseEnvironment("Development") // .ConfigureAppConfiguration((hostingContext, config) => { // config.AddJsonFile("custom.json", optional: true); // }); // 5. IHost 인스턴스 빌드: 구성된 빌더를 바탕으로 실제 호스트를 생성합니다. var host = builder.Build(); // 6. 호스트 실행: 등록된 모든 IHostedService를 시작하고 애플리케이션 종료를 대기합니다. // Ctrl+C 시그널을 감지하여 정상적으로 종료됩니다. await host.RunAsync(); Console.WriteLine("콘솔 애플리케이션 호스트가 종료되었습니다."); } } }