닷넷관련/ASP.NET CORE 🍔
ASP.NET Core DI(Dependency Injection - 종속성 주입) : Singleton vs Scoped vs Transient
재우니
2025. 3. 7. 02:13
아래는 ASP.NET Core 8에서 DI(Dependency Injection)를 활용해 Singleton, Scoped, Transient의 차이점을 이해할 수 있도록 난수(Random)를 생성하는 예제를 제공합니다. 이 예제에서는 각 서비스가 생성될 때마다 난수를 할당하고, 각 DI 라이프사이클(생명주기)에 따라 인스턴스가 어떻게 다르게 동작하는지를 보여줍니다.
1. 서비스 인터페이스 및 구현
먼저, 각 라이프사이클별로 사용할 인터페이스와 클래스를 정의합니다.
각 클래스는 생성자에서 new Random().Next(1, 1000)을 호출하여 난수를 생성하고, 이 값을 프로퍼티에 저장합니다.
namespace DIExample
{
// Singleton 인터페이스 및 구현
public interface IRandomSingleton
{
int RandomValue { get; }
}
public class RandomSingletonService : IRandomSingleton
{
public int RandomValue { get; }
public RandomSingletonService()
{
// 애플리케이션 전체에서 단 한 번 생성
RandomValue = new Random().Next(1, 1000);
}
}
// Scoped 인터페이스 및 구현
public interface IRandomScoped
{
int RandomValue { get; }
}
public class RandomScopedService : IRandomScoped
{
public int RandomValue { get; }
public RandomScopedService()
{
// HTTP 요청 당 한 번 생성
RandomValue = new Random().Next(1, 1000);
}
}
// Transient 인터페이스 및 구현
public interface IRandomTransient
{
int RandomValue { get; }
}
public class RandomTransientService : IRandomTransient
{
public int RandomValue { get; }
public RandomTransientService()
{
// 호출 시마다 새로운 인스턴스 생성
RandomValue = new Random().Next(1, 1000);
}
}
}
2. DI 컨테이너에 서비스 등록 (Program.cs)
ASP.NET Core 8의 Minimal API 스타일을 사용하여 DI 컨테이너에 각 서비스를 등록합니다.
- Singleton: AddSingleton을 사용해 애플리케이션 전체에서 단 한 번 인스턴스를 생성합니다.
- Scoped: AddScoped를 사용해 HTTP 요청당 한 번 인스턴스를 생성합니다.
- Transient: AddTransient를 사용해 요청 시마다 새로운 인스턴스를 생성합니다
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Http;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<DIExample.IRandomSingleton, DIExample.RandomSingletonService>();
builder.Services.AddScoped<DIExample.IRandomScoped, DIExample.RandomScopedService>();
builder.Services.AddTransient<DIExample.IRandomTransient, DIExample.RandomTransientService>();
var app = builder.Build();
3. 여러 인스턴스 주입하여 검증 (Program.cs)
이번 예제에서는 각 라이프사이클에 대해 두 개의 인스턴스를 주입받습니다.
- Singleton: singleton1과 singleton2는 동일 인스턴스이므로 동일한 난수 값을 반환해야 합니다.
- Scoped: 같은 HTTP 요청 내에서는 scoped1과 scoped2가 동일 인스턴스를 참조합니다.
- Transient: 매번 새 인스턴스가 생성되므로 transient1과 transient2는 서로 다른 난수 값을 반환합니다.
app.MapGet("/test", (
DIExample.IRandomSingleton singleton1, DIExample.IRandomSingleton singleton2,
DIExample.IRandomScoped scoped1, DIExample.IRandomScoped scoped2,
DIExample.IRandomTransient transient1, DIExample.IRandomTransient transient2) =>
{
return Results.Json(new {
Singleton1 = singleton1.RandomValue,
Singleton2 = singleton2.RandomValue,
Scoped1 = scoped1.RandomValue,
Scoped2 = scoped2.RandomValue,
Transient1 = transient1.RandomValue,
Transient2 = transient2.RandomValue
});
});
app.Run();
4. 실행 결과 및 검증 방법
예를 들어, /test 엔드포인트에 HTTP GET 요청을 보내면 예상되는 JSON 응답은 다음과 같습니다:
{
"Singleton1": 543,
"Singleton2": 543, // 동일한 값: 단일 인스턴스
"Scoped1": 235,
"Scoped2": 235, // 동일한 요청 내에서는 동일 인스턴스 사용
"Transient1": 789,
"Transient2": 123 // 매번 새로운 인스턴스이므로 다른 값
}
- Singleton
- singleton1과 singleton2가 동일한 인스턴스를 주입받으므로 항상 동일한 난수 값이 출력됩니다.
- 애플리케이션이 시작될 때 한 번 생성된 인스턴스입니다.
- 이후 모든 요청에서 동일한 난수 값을 반환합니다.
- Scoped
- 동일 HTTP 요청 내에서는 scoped1과 scoped2가 동일 인스턴스를 공유하여 같은 난수 값을 출력합니다.
- 만약 새로운 HTTP 요청이 발생하면 새로운 인스턴스가 생성되어 다른 값이 할당됩니다.
- 각 HTTP 요청마다 인스턴스가 하나 생성됩니다.
- 동일한 요청 내에서는 같은 값을 반환하지만, 새로운 요청이 들어오면 다른 값이 생성됩니다.
- Transient
- transient1과 transient2는 각각 별도의 인스턴스가 생성되어 서로 다른 난수 값을 반환합니다.
- 요청할 때마다 매번 새로운 인스턴스가 생성됩니다.
- 같은 요청 내에서도 transient1과 transient2는 서로 다른 난수 값을 가집니다.
차이점 요약
- Singleton
- 범위: 애플리케이션 전체
- 특징: 최초 생성 이후 모든 요청 및 서비스에서 동일한 인스턴스 사용
- 사용 예: 상태를 유지해야 하거나, 인스턴스 생성 비용이 큰 경우
- Scoped
- 범위: HTTP 요청 단위
- 특징: 같은 요청 내에서는 인스턴스가 공유되며, 요청이 바뀌면 새로운 인스턴스 생성
- 사용 예: 요청마다 별도의 상태 관리가 필요한 경우
- Transient
- 범위: 매번 새 인스턴스 생성
- 특징: 요청할 때마다 새로운 인스턴스가 주입되어, 동일 요청 내에서도 여러 번 호출 시 값이 다름
- 사용 예: 가벼운 작업이나 상태를 저장하지 않는 서비스에 적합
5. 결론
이 예제를 통해 ASP.NET Core 8의 DI 컨테이너에서 Singleton, Scoped, Transient의 동작 방식을 명확하게 이해할 수 있습니다.
- Singleton은 애플리케이션 전역에서 동일한 인스턴스를 사용하여 메모리 사용과 인스턴스 생성 비용을 절감할 수 있습니다.
- Scoped는 HTTP 요청 단위로 인스턴스를 생성하여, 요청 간에 독립적인 상태를 유지할 때 유용합니다.
- Transient는 매번 새 인스턴스를 제공하므로, 짧은 작업이나 무상태(stateless) 서비스에 적합합니다.
이러한 차이를 이해하고 활용하면, 애플리케이션의 상태 관리와 메모리 사용, 성능 최적화 측면에서 올바른 DI 라이프사이클을 선택할 수 있습니다.
참고 유투브
https://www.youtube.com/watch?v=V-8HlozCTOU&t=329s