인셔셔RSS 관심 있는 블로그, 뉴스, 기술 정보를 효율적으로 추적하고 읽으세요
원문 읽기 InertiaRSS에서 열기

추천 피드

Google DeepMind News
Google DeepMind News
人人都是产品经理
人人都是产品经理
M
MIT News - Artificial intelligence
博客园 - 叶小钗
MyScale Blog
MyScale Blog
V
Visual Studio Blog
月光博客
月光博客
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
量子位
I
InfoQ
有赞技术团队
有赞技术团队
阮一峰的网络日志
阮一峰的网络日志
Jina AI
Jina AI
V
V2EX
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Blog — PlanetScale
Blog — PlanetScale
Last Week in AI
Last Week in AI
雷峰网
雷峰网
Stack Overflow Blog
Stack Overflow Blog
博客园 - Franky

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python)
.NET 8에서의 기능 플래그: ASP.NET Core, 최소 API, Blazor
Domenico Gio · 2026-05-25 · via DEV Community

이 게시물은 원래 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에 주입하고, IsEnabledExecuteAsync에서 사용하세요.

InitializeAsync이 시작 시 실패할 경우 어떡하지요?
캐시가 없으면 예외를 던집니다. catch하고 기본값으로 진행하거나, 그것이 드러나도록 (보통 빠른 실패가 더 안전합니다).


전체 버전을 읽으세요 — circuit breaker 설정, kill switches용 SSE 스트리밍, 그리고 Blazor WebAssembly 패턴을 포함합니다 — rollgate.io/blog/feature-flags-aspnet-core.

무료 Rollgate 계정을 https://app.rollgate.io/register에서 만드세요 — 신용카드가 필요 없습니다.