IServiceScopeFactory 인터페이스

IServiceScopeFactory 란?

https://learn.microsoft.com/ko-kr/dotnet/api/microsoft.extensions.dependencyinjection.iservicescopefactory?view=net-8.0

IServiceScopeFactory 는 ASP.NET Core의 의존성 주입(DI) 컨테이너에서 새로운 서비스 범위(Scope) 를 만들기 위한 팩토리 인터페이스입니다.

기본적으로 DI 컨테이너는 Singleton / Scoped / Transient 세 가지 생명주기를 제공합니다.

일반적으로 Scoped 서비스는 HTTP 요청(Request)마다 생성되고, 요청이 끝나면 해제됩니다.

하지만 BackgroundService, Singleton 클래스 안에서는 Scoped 서비스를 직접 DI 받을 수 없습니다.

이를 해결하기 위하여 IServiceScopeFactory 를 이용해 임시 Scope를 만들어서 Scoped 서비스를 안전하게 사용합니다.

구분 Singleton Scoped Transient
인스턴스 생성 시점 애플리케이션 시작 시 한 번 HTTP 요청 시작 시 한 번 의존성 주입 요청 시마다
인스턴스 수명 애플리케이션 전체 수명 HTTP 요청 수명 가장 짧은 수명
공유 범위 전체 애플리케이션에서 단일 인스턴스 공유 동일 HTTP 요청 내에서 동일 인스턴스 공유 매번 새로운 인스턴스 생성
사용 사례 - 캐시 서비스 - 설정 관리자 - 로깅 서비스 - DB Context - 사용자 세션 - 트랜잭션 관리 - 경량 서비스 - 상태 없는 헬퍼 - 일회성 작업
주의사항 상태 저장 시 thread-safe 고려 필요 요청 간 상태 공유 불가 빈번한 생성으로 성능 영향 가능성

사용법

1. 기본적인 사용법

csharp
using (var scope = scopeFactory.CreateScope())
{
    var myService = scope.ServiceProvider.GetRequiredService<MyScopedService>();
    myService.DoSomething();
}
// scope.Dispose()가 호출되면서 MyScopedService도 정리됨

2. BackgroundService에서 DbContext 사용하기

csharp
public class MyWorker : BackgroundService
{
    private readonly IServiceScopeFactory _scopeFactory;

    public MyWorker(IServiceScopeFactory scopeFactory)
    {
        _scopeFactory = scopeFactory;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            using var scope = _scopeFactory.CreateScope();
            var db = scope.ServiceProvider.GetRequiredService<MyDbContext>();

            var users = await db.Users.ToListAsync(stoppingToken);
            Console.WriteLine($"현재 사용자 수: {users.Count}");

            await Task.Delay(5000, stoppingToken);
        }
    }
}

// MyDbContext 가 Scoped 로 등록되어 있어도 Worker에서 안전하게 사용 가능.

3. Scoped 서비스 임시 실행

csharp
public class ReportGenerator
{
    private readonly IServiceScopeFactory _scopeFactory;

    public ReportGenerator(IServiceScopeFactory scopeFactory)
    {
        _scopeFactory = scopeFactory;
    }

    public void Generate()
    {
        using var scope = _scopeFactory.CreateScope();
        var service = scope.ServiceProvider.GetRequiredService<ReportService>();

        service.CreateDailyReport();
    }
}

// ReportService 가 Scoped 로 등록되어 있어도 IServiceScopeFactory 를 사용해 특정 시점에만 실행 가능.

4. 여러 Scoped 서비스 조합

csharp
public class CleanupJob
{
    private readonly IServiceScopeFactory _scopeFactory;

    public CleanupJob(IServiceScopeFactory scopeFactory)
    {
        _scopeFactory = scopeFactory;
    }

    public async Task RunAsync()
    {
        using var scope = _scopeFactory.CreateScope();
        var db = scope.ServiceProvider.GetRequiredService<MyDbContext>();
        var emailService = scope.ServiceProvider.GetRequiredService<EmailService>();

        var expiredUsers = db.Users.Where(u => u.IsExpired).ToList();
        db.Users.RemoveRange(expiredUsers);
        await db.SaveChangesAsync();

        await emailService.SendCleanupReport(expiredUsers.Count);
    }
}

정리

  • IServiceScopeFactory 는 Scoped 서비스의 생명주기를 직접 제어할 수 있게 해줍니다.
  • 주로 Singleton 객체나 BackgroundService 에서 Scoped 서비스를 안전하게 사용하기 위해 필요합니다.
  • CreateScope() 로 범위를 열고, 작업이 끝나면 자동으로 Dispose 되어 메모리 누수 없이 관리됩니다.