이 게시물은 원래 rollgate.io/blog/feature-flags-aspnet-core에 게시되었습니다.
모든 .NET 팀은 결국 같은 벽에 부딪힙니다: 스테이징에서 기능이 준비되어 있지만, 생산으로 밀어 넣으려면 모든 사용자에게 한 번에 스위치를 켜야 합니다. 무언가 깨지면 - 잘못된 가정, 경계 사례, 성능 하락 - 당신의 유일한 회복은 롤백 및 재배포입니다.
특징 플래그는 배포를 릴리스로부터 분리하여 이를 해결합니다. 코드를 플래그 뒤에 배포하고 대시보드를 통해 누가 이를 볼지 제어하면 파이프라인을 건드릴 필요 없습니다. 1%의 사용자로 시작하여 메트릭스를 모니터링하고 100%로 확장합니다. 오류가 어느 단계에서든 폭발하면 초당 플래그를 비활성화합니다.
빠른 시작: .NET 8의 특징 플래그
NuGet에서 Rollgate SDK를 설치합니다:
dotnet add package Rollgate.SDK
애플리케이션 시작 시 클라이언트 초기화:
using Rollgate.SDK;
var client = new RollgateClient(new RollgateConfig
{
ApiKey = Environment.GetEnvironmentVariable("ROLLGATE_API_KEY") ?? "",
});
await client.InitializeAsync();
if (client.IsEnabled("new-checkout", false))
{
Console.WriteLine("New checkout enabled");
}
client.Dispose();
InitializeAsync() 이후, 각 IsEnabled 호출은 메모리 내 사전에서 읽으며, 단일 자릿수 마이크로초 오버헤드.
의존성 주입에 등록 중
ASP.NET Core에서 클라이언트를 단일 인스턴스로 등록하고 작은 IFeatureFlags 추상화를 추가하여 컨트롤러가 테스트 가능하게 합니다:
// Program.cs
builder.Services.AddSingleton<RollgateClient>(sp =>
{
var client = new RollgateClient(new RollgateConfig
{
ApiKey = builder.Configuration["Rollgate:ApiKey"] ?? "",
RefreshInterval = TimeSpan.FromSeconds(30),
});
// Tutorial simplicity. In production, prefer IHostedService.
client.InitializeAsync().GetAwaiter().GetResult();
return client;
});
builder.Services.AddSingleton<IFeatureFlags, RollgateFeatureFlags>();
public interface IFeatureFlags
{
bool IsEnabled(string flagKey, bool defaultValue = false);
}
public sealed class RollgateFeatureFlags : IFeatureFlags
{
private readonly RollgateClient _client;
public RollgateFeatureFlags(RollgateClient client) => _client = client;
public bool IsEnabled(string key, bool def = false) => _client.IsEnabled(key, def);
}
ASP.NET Core 컨트롤러의 기능 플래그
IFeatureFlags을 직접 SDK 타입 대신 주입하세요:
[ApiController]
[Route("api/[controller]")]
public class CheckoutController : ControllerBase
{
private readonly IFeatureFlags _flags;
public CheckoutController(IFeatureFlags flags) => _flags = flags;
[HttpPost]
public async Task<IActionResult> CreateOrder([FromBody] OrderRequest request)
{
return _flags.IsEnabled("checkout-v2", false)
? Ok(await ProcessV2Async(request))
: Ok(await ProcessV1Async(request));
}
}
사용자 식별 — 세션당 한 번, 요청당 한 번이 아님
RollgateClient.IdentifyAsync HTTP 요청을 발행하고 플래그를 새로 고침합니다. 모든 요청마다 호출하지 마십시오 — 그렇게 하면 각 엔드포인트당 네트워크 라운드 트립이 추가되고 메모리 내 평가 모델을 파괴합니다.
적절한 장소는 사용자마다 한 번씩 실행되는 액션 필터로, 그 다음 단락을 건너뜁니다:
public class FeatureFlagIdentityFilter : IAsyncActionFilter
{
private readonly RollgateClient _client;
private static readonly HashSet<string> _identified = new();
private static readonly SemaphoreSlim _gate = new(1, 1);
public FeatureFlagIdentityFilter(RollgateClient client) => _client = client;
public async Task OnActionExecutionAsync(ActionExecutingContext ctx, ActionExecutionDelegate next)
{
var userId = ctx.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
if (!string.IsNullOrEmpty(userId) && !_identified.Contains(userId))
{
await _gate.WaitAsync();
try
{
if (!_identified.Contains(userId))
{
await _client.IdentifyAsync(new UserContext { Id = userId });
_identified.Add(userId);
}
}
finally { _gate.Release(); }
}
await next();
}
}
생산 환경용 정리: 로그인 시 한 번 IdentifyAsync을 호출하고, 로그아웃할 때까지 다시 호출하지 않습니다.
미니멀 API의 기능 플래그
app.MapPost("/api/search", async (SearchRequest req, IFeatureFlags flags) =>
{
return flags.IsEnabled("semantic-search", false)
? Results.Ok(await RunSemanticSearchAsync(req.Query))
: Results.Ok(await RunKeywordSearchAsync(req.Query));
});
엔드포인트 필터를 통해 전체 경로를 게이트, IFeatureFlags을 요청 서비스에서 해결:
public static class FeatureFlagEndpointExtensions
{
public static TBuilder RequireFeature<TBuilder>(this TBuilder builder, string flagKey)
where TBuilder : IEndpointConventionBuilder
{
return builder.AddEndpointFilter(async (context, next) =>
{
var flags = context.HttpContext.RequestServices.GetRequiredService<IFeatureFlags>();
if (!flags.IsEnabled(flagKey, false)) return Results.NotFound();
return await next(context);
});
}
}
app.MapGet("/api/v2/analytics", GetAnalyticsV2Handler)
.RequireFeature("analytics-v2")
.RequireAuthorization();
Blazor Server의 기능 플래그
@page "/checkout"
@inject IFeatureFlags Flags
@inject AuthenticationStateProvider AuthStateProvider
@if (_showNewCheckout) { <NewCheckoutFlow /> } else { <LegacyCheckoutFlow /> }
@code {
private bool _showNewCheckout;
protected override void OnInitialized()
{
// Runs once per circuit, not per render.
_showNewCheckout = Flags.IsEnabled("checkout-v2", false);
}
}
Blazor WebAssembly에 대해서는, Program.cs 전에 서버에서 플래그를 가져옵니다RunAsync():
var host = builder.Build();
await host.Services.GetRequiredService<FlagService>().LoadAsync();
await host.RunAsync();
C에서 Feature Flags 테스트
컨트롤러는IFeatureFlags, 모킹 프레임워크가 필요 없음:
public class FakeFeatureFlags : IFeatureFlags
{
private readonly Dictionary<string, bool> _flags;
public FakeFeatureFlags(Dictionary<string, bool>? f = null) => _flags = f ?? new();
public bool IsEnabled(string key, bool def = false)
=> _flags.TryGetValue(key, out var v) ? v : def;
}
public class CheckoutControllerTests
{
[Fact]
public async Task Returns_V2_When_Flag_Enabled()
{
var flags = new FakeFeatureFlags(new() { ["checkout-v2"] = true });
var controller = new CheckoutController(flags);
var result = await controller.CreateOrder(new OrderRequest { Amount = 99 });
// assert v2 path
}
}
항상 두 플래그 상태를 테스트하세요.
점진적 롤아웃 및 사용자 타겟팅
사용자 속성을 식별할 때(세션당 한 번):
await _client.IdentifyAsync(new UserContext
{
Id = userId,
Email = userEmail,
Attributes = new Dictionary<string, object?>
{
["plan"] = user.SubscriptionPlan,
["country"] = user.Country,
}
});
대시보드에서 백분율 롤아웃과 속성 대상 설정 — SDK는 모든 규칙을 로컬에서 평가하며, 각 평가마다 API 호출이 필요 없습니다.
FAQ
2026년에 어떤 .NET 기능 플래그 접근 방식을 사용할까요?
- 실행 시간 변경 없는 간단한 토글 →
Microsoft.FeatureManagement - CNCF 벤더 중립 SDK →
OpenFeature .NET - 대시보드 + 타겟팅 + 점진적 롤아웃 → 관리 서비스 (Rollgate, LaunchDarkly 등)
.NET 백그라운드 서비스와 호환되나요?
네. RollgateClient을 싱글톤으로 등록하고 BackgroundService에 주입하고, IsEnabled을 ExecuteAsync에서 사용하세요.
InitializeAsync이 시작 시 실패할 경우 어떡하지요?
캐시가 없으면 예외를 던집니다. catch하고 기본값으로 진행하거나, 그것이 드러나도록 (보통 빠른 실패가 더 안전합니다).
전체 버전을 읽으세요 — circuit breaker 설정, kill switches용 SSE 스트리밍, 그리고 Blazor WebAssembly 패턴을 포함합니다 — rollgate.io/blog/feature-flags-aspnet-core.
무료 Rollgate 계정을 https://app.rollgate.io/register에서 만드세요 — 신용카드가 필요 없습니다.











