Community코딩 & 개발github.com

microsoft-extensions-configuration

Microsoft.Extensions.Options 패턴을 활용한 강력한 타입 설정, 시작 시 유효성 검사, IValidateOptions 및 옵션 패턴을 통한 깔끔한 구성 관리.

microsoft-extensions-configuration란 무엇인가요?

이 기술은 .NET 애플리케이션에서 Microsoft.Extensions.Options 라이브러리를 사용하여 구성 관리를 체계화하는 방법을 다룹니다. 주로 appsettings.json과 같은 설정 파일을 강력한 타입의 POCO 클래스에 바인딩하고, Data Annotations 또는 IValidateOptions<T> 인터페이스를 통해 애플리케이션 시작 시점에 설정을 검증하여 런타임 오류를 방지합니다. 또한 IOptions<T>, IOptionsSnapshot<T>, IOptionsMonitor<T>의 수명 주기 차이를 설명하고, PostConfigure를 사용한 후처리, 의존성 주입을 지원하는 복잡한 유효성 검사기, 그리고 설정 변경 감지(OnChange 콜백)와 같은 고급 패턴을 포함합니다. 이 기술을 사용하면 설정 오류로 인한 예상치 못한 장애를 방지하고, 테스트 가능하고 유지보수하기 쉬운 구성 클래스를 설계할 수 있습니다. 주요 IDE 확장 (Claude Code, Cursor 등)과 함께 사용하면 .NET 개발 시 생산성을 크게 향상시킬 수 있습니다.

지원 대상~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Aaronontheweb/dotnet-skills/tree/main/skills/microsoft-extensions-configuration

Installed? Explore more 코딩 & 개발 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

Microsoft.Extensions 구성 패턴

이 기술을 사용해야 할 때

다음과 같은 경우에 이 기술을 사용하세요:

  • appsettings.json의 구성 데이터를 강력한 타입의 클래스에 바인딩할 때
  • 애플리케이션 시작 시 구성을 검증하여 빠르게 실패(fail fast)하고자 할 때
  • 설정에 대한 복잡한 유효성 검사 로직을 구현할 때
  • 테스트 가능하고 유지보수하기 쉬운 구성 클래스를 설계할 때
  • IOptions, IOptionsSnapshot, IOptionsMonitor의 차이를 이해하고 사용할 때

참조 파일

  • advanced-patterns.md: 의존성이 있는 유효성 검사기, 명명된 옵션, 완전한 프로덕션 예제(AkkaSettings), 유효성 검사기 테스트

구성 유효성 검사가 중요한 이유

문제점: 애플리케이션은 잘못된 구성(연결 문자열 누락, 잘못된 URL, 범위를 벗어난 값)으로 인해 런타임에 자주 실패합니다. 이러한 실패는 구성이 로드된 위치와는 전혀 다른 비즈니스 로직 깊숙한 곳에서 발생합니다.

해결책: 시작 시 구성을 검증하세요. 잘못된 경우 명확한 오류 메시지와 함께 즉시 실패합니다.

// 나쁨: 누군가 서비스를 사용하려고 할 때 런타임에 실패
public class EmailService
{
    public EmailService(IOptions<SmtpSettings> options)
    {
        var settings = options.Value;
        // 프로덕션에서 10분 후에 NullReferenceException 발생
        _client = new SmtpClient(settings.Host, settings.Port);
    }
}

// 좋음: 시작 시 명확한 오류와 함께 실패
// "SmtpSettings 유효성 검사 실패: Host가 필요합니다."

패턴 1: 기본 옵션 바인딩

설정 클래스 정의

public class SmtpSettings
{
    public const string SectionName = "Smtp";

    public string Host { get; set; } = string.Empty;
    public int Port { get; set; } = 587;
    public string? Username { get; set; }
    public string? Password { get; set; }
    public bool UseSsl { get; set; } = true;
}

구성에서 바인딩

builder.Services.AddOptions<SmtpSettings>()
    .BindConfiguration(SmtpSettings.SectionName);

// appsettings.json
{
  "Smtp": {
    "Host": "smtp.example.com",
    "Port": 587,
    "Username": "[email protected]",
    "Password": "secret",
    "UseSsl": true
  }
}

서비스에서 소비

public class EmailService
{
    private readonly SmtpSettings _settings;

    // IOptions<T> - 싱글톤, 시작 시 한 번만 읽음
    public EmailService(IOptions<SmtpSettings> options)
    {
        _settings = options.Value;
    }
}

패턴 2: Data Annotations 유효성 검사

간단한 유효성 검사 규칙에는 Data Annotations을 사용하세요:

using System.ComponentModel.DataAnnotations;

public class SmtpSettings
{
    public const string SectionName = "Smtp";

    [Required(ErrorMessage = "SMTP 호스트가 필요합니다")]
    public string Host { get; set; } = string.Empty;

    [Range(1, 65535, ErrorMessage = "포트는 1에서 65535 사이여야 합니다")]
    public int Port { get; set; } = 587;

    [EmailAddress(ErrorMessage = "사용자 이름은 유효한 이메일 주소여야 합니다")]
    public string? Username { get; set; }

    public string? Password { get; set; }
    public bool UseSsl { get; set; } = true;
}

Data Annotations 유효성 검사 활성화

builder.Services.AddOptions<SmtpSettings>()
    .BindConfiguration(SmtpSettings.SectionName)
    .ValidateDataAnnotations()  // 속성 기반 유효성 검사 활성화
    .ValidateOnStart();         // 시작 시 즉시 검증

핵심 사항: .ValidateOnStart()는 중요합니다. 이것이 없으면 유효성 검사는 옵션이 처음 액세스될 때만 실행됩니다.


패턴 3: 복잡한 유효성 검사를 위한 IValidateOptions

Data Annotations은 간단한 규칙에 적합하지만, 복잡한 유효성 검사는 IValidateOptions<T>가 필요합니다:

시나리오Data AnnotationsIValidateOptions
필수 필드예예
범위 확인예예
속성 간 유효성 검사아니요예
조건부 유효성 검사아니요예
외부 서비스 확인아니요예
유효성 검사기의 의존성 주입아니요예

IValidateOptions 구현

using Microsoft.Extensions.Options;

public class SmtpSettingsValidator : IValidateOptions<SmtpSettings>
{
    public ValidateOptionsResult Validate(string? name, SmtpSettings options)
    {
        var failures = new List<string>();

        if (string.IsNullOrWhiteSpace(options.Host))
            failures.Add("Host가 필요합니다");

        if (options.Port is < 1 or > 65535)
            failures.Add($"Port {options.Port}가 잘못되었습니다. 1에서 65535 사이여야 합니다");

        // 속성 간 유효성 검사
        if (!string.IsNullOrEmpty(options.Username) && string.IsNullOrEmpty(options.Password))
            failures.Add("Username이 지정된 경우 Password가 필요합니다");

        // 조건부 유효성 검사
        if (options.UseSsl && options.Port == 25)
            failures.Add("포트 25는 일반적으로 SSL과 함께 사용되지 않습니다. 포트 465 또는 587을 고려하세요");

        return failures.Count > 0
            ? ValidateOptionsResult.Fail(failures)
            : ValidateOptionsResult.Success;
    }
}

유효성 검사기 등록

builder.Services.AddOptions<SmtpSettings>()
    .BindConfiguration(SmtpSettings.SectionName)
    .ValidateDataAnnotations()
    .ValidateOnStart();

builder.Services.AddSingleton<IValidateOptions<SmtpSettings>, SmtpSettingsValidator>();

순서가 중요합니다: Data Annotations이 먼저 실행되고, 그 다음 IValidateOptions 유효성 검사기가 실행됩니다. 모든 실패는 함께 수집됩니다.

의존성이 있는 유효성 검사기, 명명된 옵션 및 완전한 프로덕션 예제는 advanced-patterns.md를 참조하세요.


패턴 4: 옵션 수명

인터페이스수명변경 시 다시 로드사용 사례
IOptions<T>싱글톤아니요정적 구성, 한 번만 읽기
IOptionsSnapshot<T>범위예 (요청별)최신 구성이 필요한 웹 앱
IOptionsMonitor<T>싱글톤예 (콜백 포함)백그라운드 서비스, 실시간 업데이트

백그라운드 서비스를 위한 IOptionsMonitor

public class BackgroundWorker : BackgroundService
{
    private readonly IOptionsMonitor<WorkerSettings> _optionsMonitor;
    private WorkerSettings _currentSettings;

    public BackgroundWorker(IOptionsMonitor<WorkerSettings> optionsMonitor)
    {
        _optionsMonitor = optionsMonitor;
        _currentSettings = optionsMonitor.CurrentValue;

        _optionsMonitor.OnChange(settings =>
        {
            _currentSettings = settings;
        });
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            await DoWorkAsync();
            await Task.Delay(_currentSettings.PollingInterval, stoppingToken);
        }
    }
}

패턴 5: 사후 구성(Post-Configuration)

바인딩 후 유효성 검사 전에 옵션을 수정합니다:

builder.Services.AddOptions<ApiSettings>()
    .BindConfiguration("Api")
    .PostConfigure(options =>
    {
        if (!string.IsNullOrEmpty(options.BaseUrl) && !options.BaseUrl.EndsWith('/'))
            options.BaseUrl += '/';

        options.Timeout ??= TimeSpan.FromSeconds(30);
    })
    .ValidateDataAnnotations()
    .ValidateOnStart();

피해야 할 안티 패턴

1. 수동 구성 액세스

// 나쁨: 유효성 검사를 우회하고 테스트하기 어려움
public class MyService
{
    public MyService(IConfiguration configuration)
    {
        var host = configuration["Smtp:Host"]; // 유효성 검사 없음!
    }
}

// 좋음: 강력한 타입, 유효성 검사됨
public class MyService
{
    public MyService(IOptions<SmtpSettings> options)
    {
        var host = options.Value.Host; // 시작 시 유효성 검사됨
    }
}

2. 생성자에서 유효성 검사

// 나쁨: 유효성 검사가 시작 시가 아니라 런타임에 발생
public class MyService
{
    public MyService(IOptions<Settings> options)
    {
        if (string.IsNullOrEmpty(options.Value.Required))
            throw new ArgumentException("Required가 누락되었습니다"); // 너무 늦음!
    }
}

// 좋음: IValidateOptions + ValidateOnStart()를 통해 시작 시 유효성 검사

3. ValidateOnStart()를 잊는 경우

// 나쁨: 유효성 검사가 처음 액세스될 때만 실행됨
builder.Services.AddOptions<Settings>()
    .ValidateDataAnnotations(); // ValidateOnStart() 누락!

// 좋음: 잘못된 경우 즉시 실패
builder.Services.AddOptions<Settings>()
    .ValidateDataAnnotations()
    .ValidateOnStart();

4. IValidateOptions에서 예외를 던지는 경우

// 나쁨: 예외를 던져 유효성 검사 체인을 깨뜨림
public ValidateOptionsResult Validate(string? name, Settings options)
{
    if (options.Value < 0)
        throw new ArgumentException("값은 음수가 될 수 없습니다"); // 잘못됨!
    return ValidateOptionsResult.Success;
}

// 좋음: 실패 결과를 반환
public ValidateOptionsResult Validate(string? name, Settings options)
{
    if (options.Value < 0)
        return ValidateOptionsResult.Fail("값은 음수가 될 수 없습니다");
    return ValidateOptionsResult.Success;
}

요약

원칙구현
빠른 실패.ValidateOnStart()
강력한 타입POCO 클래스에 바인딩
간단한 유효성 검사Data Annotations
복잡한 유효성 검사IValidateOptions<T>
속성 간 규칙IValidateOptions<T>
환경 인식IHostEnvironment 주입
테스트 가능유효성 검사기는 일반 클래스

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

akka-hosting-actor-patterns

Patterns for building entity actors with Akka.Hosting - GenericChildPerEntityParent, message extractors, cluster sharding abstraction, akka-reminders, and ITimeProvider. Supports both local testing and clustered production modes.

akka-net-aspire-configuration

Configure Akka.NET with .NET Aspire for local development and production deployments. Covers actor system setup, clustering, persistence, Akka.Management integration, and Aspire orchestration patterns.

akka-net-best-practices

Critical Akka.NET best practices including EventStream vs DistributedPubSub, supervision strategies, error handling, Props vs DependencyResolver, work distribution patterns, and cluster/local mode abstractions for testability.

akka-net-management

Akka.Management for cluster bootstrapping, service discovery (Kubernetes, Azure, Config), health checks, and dynamic cluster formation without static seed nodes.

akka-net-testing-patterns

Write unit and integration tests for Akka.NET actors using modern Akka.Hosting.TestKit patterns. Covers dependency injection, TestProbes, persistence testing, and actor interaction verification. Includes guidance on when to use traditional TestKit.

api-design

Design stable, compatible public APIs using extend-only design principles. Manage API compatibility, wire compatibility, and versioning for NuGet packages and distributed systems.

aspire-configuration

Configure Aspire AppHost to emit explicit app config via environment variables; keep app code free of Aspire clients and service discovery.

aspire-integration-testing

Write integration tests using .NET Aspire

aspire-service-defaults

Create a shared ServiceDefaults project for Aspire applications. Centralizes OpenTelemetry, health checks, resilience, and service discovery configuration across all services.

crap-analysis

Analyze code coverage and CRAP (Change Risk Anti-Patterns) scores to identify high-risk code. Use OpenCover format with ReportGenerator for Risk Hotspots showing cyclomatic complexity and untested code paths.

csharp-concurrency-patterns

Choosing the right concurrency abstraction in .NET - from async/await for I/O to Channels for producer/consumer to Akka.NET for stateful entity management. Avoid locks and manual synchronization unless absolutely necessary.

database-performance

Database access patterns for performance. Separate read/write models, avoid N+1 queries, use AsNoTracking, apply row limits, and never do application-side joins. Works with EF Core and Dapper.

dependency-injection-patterns

Organize DI registrations using IServiceCollection extension methods. Group related services into composable Add* methods for clean Program.cs and reusable configuration in tests.

dotnet-devcert-trust

Diagnose and fix .NET HTTPS dev certificate trust issues on Linux. Covers the full certificate lifecycle from generation to system CA bundle inclusion, with distro-specific guidance for Ubuntu, Fedora, Arch, and WSL2.

dotnet-local-tools

Managing local .NET tools with dotnet-tools.json for consistent tooling across development environments and CI/CD pipelines.

dotnet-project-structure

Modern .NET project structure including .slnx solution format, Directory.Build.props, central package management, SourceLink, version management with RELEASE_NOTES.md, and SDK pinning with global.json.

dotnet-slopwatch

Use Slopwatch to detect LLM reward hacking in .NET code changes. Run after every code modification to catch disabled tests, suppressed warnings, empty catch blocks, and other shortcuts that mask real problems.

efcore-patterns

Entity Framework Core best practices including NoTracking by default, query splitting for navigation collections, migration management, dedicated migration services, and common pitfalls to avoid.

ilspy-decompile

Understand implementation details of .NET code by decompiling assemblies. Use when you want to see how a .NET API works internally, inspect NuGet package source, view framework implementation, or understand compiled .NET binaries.

mailpit-integration

Test email sending locally using Mailpit with .NET Aspire. Captures all outgoing emails without sending them. View rendered HTML, inspect headers, and verify delivery in integration tests.

관련 스킬