Microsoft .NET/ASP.Net & ASP.Net Core

[.NET] BackgroundService로 주기 작업 만들기, 종료 처리까지

전자기린 2026. 11. 10. 00:45

ASP.NET Core에서 주기적으로 도는 작업(정리, 동기화, 알림)을 만드는 가장 단순한 방법인 BackgroundService 정리입니다.

절차

  1. 최소 구현
  2. DI 주입 주의
  3. 정상 종료 처리
  4. 운영에서 확인할 것

 

1. 최소 구현

public class CleanupWorker : BackgroundService
{
    private readonly ILogger<CleanupWorker> _logger;
    public CleanupWorker(ILogger<CleanupWorker> logger) => _logger = logger;

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            _logger.LogInformation("정리 작업 실행 {Time}", DateTimeOffset.Now);
            await Task.Delay(TimeSpan.FromMinutes(10), stoppingToken);
        }
    }
}
builder.Services.AddHostedService<CleanupWorker>();

ExecuteAsync가 반환되면 작업이 끝난 것으로 보므로, 반복은 while로 직접 돌립니다.

2. DI 주입 주의

BackgroundService는 Singleton입니다. AppDbContext 같은 Scoped 서비스를 생성자로 받으면 시작할 때 예외가 납니다.

using var scope = _scopeFactory.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();

3. 정상 종료 처리

앱이 내려갈 때 stoppingToken이 취소됩니다. 모든 await에 토큰을 넘기고, 오래 걸리는 루프는 중간에 취소를 확인합니다.

foreach (var item in items)
{
    stoppingToken.ThrowIfCancellationRequested();
    await ProcessAsync(item, stoppingToken);
}

종료 대기 시간은 기본 5초입니다. 더 필요하면 호스트 옵션으로 늘립니다.

builder.Services.Configure<HostOptions>(o =>
    o.ShutdownTimeout = TimeSpan.FromSeconds(30));

4. 운영에서 확인할 것

  1. 인스턴스를 여러 개 띄우면 같은 작업이 중복 실행됩니다 — 분산 락이나 별도 워커로 분리
  2. IIS 뒤에서는 앱 풀 재활용으로 프로세스가 내려갑니다 — 계속 돌려야 하면 Windows 서비스나 systemd로 등록
  3. 실행 로그를 남겨 실제로 도는지 확인

정리

  1. ExecuteAsync 안에서 while 루프
  2. Scoped 의존성은 IServiceScopeFactory로
  3. 종료 토큰을 모든 await에 전달