Community코딩 & 개발github.com

의존성 주입 패턴

IServiceCollection 확장 메서드를 사용하여 DI 등록을 체계화합니다. 관련 서비스를 구성 가능한 Add* 메서드로 그룹화하여 Program.cs를 깔끔하게 유지하고 테스트에서 재사용할 수 있습니다.

의존성 주입 패턴란 무엇인가요?

이 스킬은 ASP.NET Core 애플리케이션에서 의존성 주입(DI) 등록을 체계적으로 구성하는 방법을 설명합니다. Program.cs나 Startup.cs 파일이 수백 줄의 서비스 등록으로 비대해지는 문제를 해결하기 위해, `IServiceCollection` 확장 메서드를 사용하여 관련 서비스를 `Add{Feature}Services()`와 같은 명명된 메서드로 그룹화합니다. 이를 통해 코드 가독성이 향상되고, 서비스 등록의 경계가 명확해지며, 프로덕션 코드를 테스트에서 재사용할 수 있습니다. Microsoft.Extensions.DependencyInjection, Akka.Hosting, WebApplicationFactory 등 다양한 플랫폼과 테스트 환경에서 동일한 패턴을 적용할 수 있으며, 수명 주기 관리(Scoped/Singleton/Transient)와 일반적인 실수(예: Singleton에 Scoped 주입, 백그라운드 작업에서 Scope 누락)에 대한 모범 사례도 제공합니다.

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

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

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

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

문서

의존성 주입 패턴은(는) 무엇을 하나요?

이 스킬을 사용해야 할 때

다음 상황에서 이 스킬을 사용하세요:

  • ASP.NET Core 애플리케이션에서 서비스 등록을 체계화할 때
  • 수백 개의 등록이 있는 Program.cs/Startup.cs 파일을 방지할 때
  • 프로덕션과 테스트 간에 서비스 구성을 재사용할 때
  • Microsoft.Extensions.DependencyInjection과 통합되는 라이브러리를 설계할 때

참고 파일

  • advanced-patterns.md: DI 확장을 사용한 테스트, Akka.NET 액터 범위 관리, 조건부/팩토리/키 기반 등록 패턴

문제

체계화 없이 Program.cs는 관리 불가능해집니다:

// BAD: 200줄 이상의 비체계적 등록
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddScoped<IUserRepository, UserRepository>();
builder.Services.AddScoped<IOrderRepository, OrderRepository>();
builder.Services.AddScoped<IProductRepository, ProductRepository>();
builder.Services.AddScoped<IUserService, UserService>();
// ... 150줄 더 ...

문제점: 관련 등록을 찾기 어렵고, 명확한 경계가 없으며, 테스트에서 재사용할 수 없고, 병합 충돌이 발생합니다.


해결책: 확장 메서드 구성

관련 등록을 확장 메서드로 그룹화하세요:

// GOOD: 깔끔하고 구성 가능한 Program.cs
var builder = WebApplication.CreateBuilder(args);

builder.Services
    .AddUserServices()
    .AddOrderServices()
    .AddEmailServices()
    .AddPaymentServices()
    .AddValidators();

var app = builder.Build();

확장 메서드 패턴

기본 구조

namespace MyApp.Users;

public static class UserServiceCollectionExtensions
{
    public static IServiceCollection AddUserServices(this IServiceCollection services)
    {
        services.AddScoped<IUserRepository, UserRepository>();
        services.AddScoped<IUserReadStore, UserReadStore>();
        services.AddScoped<IUserWriteStore, UserWriteStore>();
        services.AddScoped<IUserService, UserService>();
        services.AddScoped<IUserValidationService, UserValidationService>();

        return services;
    }
}

구성 포함

namespace MyApp.Email;

public static class EmailServiceCollectionExtensions
{
    public static IServiceCollection AddEmailServices(
        this IServiceCollection services,
        string configSectionName = "EmailSettings")
    {
        services.AddOptions<EmailOptions>()
            .BindConfiguration(configSectionName)
            .ValidateDataAnnotations()
            .ValidateOnStart();

        services.AddSingleton<IMjmlTemplateRenderer, MjmlTemplateRenderer>();
        services.AddSingleton<IEmailLinkGenerator, EmailLinkGenerator>();
        services.AddScoped<IUserEmailComposer, UserEmailComposer>();
        services.AddScoped<IEmailSender, SmtpEmailSender>();

        return services;
    }
}

파일 구성

확장 메서드를 등록하는 서비스 근처에 배치하세요:

src/
  MyApp.Api/
    Program.cs                    # 모든 Add* 메서드 구성
  MyApp.Users/
    Services/
      UserService.cs
    UserServiceCollectionExtensions.cs   # AddUserServices()
  MyApp.Orders/
    OrderServiceCollectionExtensions.cs  # AddOrderServices()
  MyApp.Email/
    EmailServiceCollectionExtensions.cs  # AddEmailServices()

규칙: {Feature}ServiceCollectionExtensions.cs를 해당 기능의 서비스 옆에 배치합니다.


명명 규칙

패턴용도
Add{Feature}Services()일반 기능 등록
Add{Feature}()모호하지 않을 때 짧은 형태
Configure{Feature}()주로 옵션 설정 시
Use{Feature}()미들웨어 (IApplicationBuilder에서)

테스트 이점

Add* 패턴을 사용하면 테스트에서 프로덕션 구성을 재사용하고 다른 부분만 재정의할 수 있습니다. WebApplicationFactory, Akka.Hosting.TestKit 및 독립형 ServiceCollection과 함께 작동합니다.

전체 테스트 예제는 advanced-patterns.md를 참조하세요.


계층적 확장

대규모 애플리케이션의 경우 확장을 계층적으로 구성하세요:

public static class AppServiceCollectionExtensions
{
    public static IServiceCollection AddAppServices(this IServiceCollection services)
    {
        return services
            .AddDomainServices()
            .AddInfrastructureServices()
            .AddApiServices();
    }
}

public static class DomainServiceCollectionExtensions
{
    public static IServiceCollection AddDomainServices(this IServiceCollection services)
    {
        return services
            .AddUserServices()
            .AddOrderServices()
            .AddProductServices();
    }
}

Akka.Hosting 통합

동일한 패턴이 Akka.NET 액터 구성에도 적용됩니다:

public static class OrderActorExtensions
{
    public static AkkaConfigurationBuilder AddOrderActors(
        this AkkaConfigurationBuilder builder)
    {
        return builder
            .WithActors((system, registry, resolver) =>
            {
                var orderProps = resolver.Props<OrderActor>();
                var orderRef = system.ActorOf(orderProps, "orders");
                registry.Register<OrderActor>(orderRef);
            });
    }
}

// Program.cs에서 사용
builder.Services.AddAkka("MySystem", (builder, sp) =>
{
    builder
        .AddOrderActors()
        .AddInventoryActors()
        .AddNotificationActors();
});

전체 Akka.Hosting 패턴은 akka-hosting-actor-patterns 스킬을 참조하세요.


안티 패턴

하지 말아야 할 것: 모든 것을 Program.cs에 등록

// BAD: 200줄 이상의 등록이 있는 거대한 Program.cs

하지 말아야 할 것: 지나치게 일반적인 확장 생성

// BAD: 너무 모호하며, 무엇이 등록되는지 전달하지 못함
public static IServiceCollection AddServices(this IServiceCollection services) { ... }

하지 말아야 할 것: 중요한 구성 숨기기

// BAD: 설정이 묻혀 있음
public static IServiceCollection AddDatabase(this IServiceCollection services)
{
    services.AddDbContext<AppDbContext>(options =>
        options.UseSqlServer("하드코딩된-연결-문자열"));  // 숨겨짐!
}

// GOOD: 구성을 명시적으로 받음
public static IServiceCollection AddDatabase(
    this IServiceCollection services,
    string connectionString)
{
    services.AddDbContext<AppDbContext>(options =>
        options.UseSqlServer(connectionString));
}

모범 사례 요약

사례이점
관련 서비스를 Add* 메서드로 그룹화깔끔한 Program.cs, 명확한 경계
확장 메서드를 등록하는 서비스 근처에 배치쉽게 찾고 유지 관리 가능
체이닝을 위해 IServiceCollection 반환유창한 API
구성 매개변수 허용유연성
일관된 명명 사용 (Add{Feature}Services)검색 가능성
프로덕션 확장을 재사용하여 테스트자신감, 중복 감소

수명 관리

수명사용 시기예시
Singleton상태 비저장, 스레드 안전, 생성 비용 높음구성, HttpClient 팩토리, 캐시
Scoped요청별 상태 저장, 데이터베이스 컨텍스트DbContext, 리포지토리, 사용자 컨텍스트
Transient가벼움, 상태 저장, 생성 비용 낮음유효성 검사기, 단기 헬퍼
// SINGLETON: 상태 비저장 서비스, 안전하게 공유
services.AddSingleton<IMjmlTemplateRenderer, MjmlTemplateRenderer>();

// SCOPED: 데이터베이스 액세스, 요청별 상태
services.AddScoped<IUserRepository, UserRepository>();

// TRANSIENT: 저렴하고 단기적
services.AddTransient<CreateUserRequestValidator>();

Scoped 서비스에는 범위가 필요합니다. ASP.NET Core는 HTTP 요청당 하나의 범위를 생성합니다. 백그라운드 서비스와 액터에서는 수동으로 범위를 생성하세요.

액터 범위 관리 패턴은 advanced-patterns.md를 참조하세요.


일반적인 실수

Singleton에 Scoped 주입

// BAD: Singleton이 Scoped 서비스를 캡처 - 오래된 DbContext!
public class CacheService  // Singleton으로 등록됨
{
    private readonly IUserRepository _repo;  // Scoped - 시작 시 캡처됨!
}

// GOOD: IServiceProvider를 주입하고, 작업마다 범위 생성
public class CacheService
{
    private readonly IServiceProvider _serviceProvider;

    public async Task<User> GetUserAsync(string id)
    {
        using var scope = _serviceProvider.CreateScope();
        var repo = scope.ServiceProvider.GetRequiredService<IUserRepository>();
        return await repo.GetByIdAsync(id);
    }
}

백그라운드 작업에서 범위 누락

// BAD: Scoped 서비스에 대한 범위 없음
public class BadBackgroundService : BackgroundService
{
    private readonly IOrderService _orderService;  // Scoped - 예외 발생!
}

// GOOD: 각 작업 단위에 대해 범위 생성
public class GoodBackgroundService : BackgroundService
{
    private readonly IServiceScopeFactory _scopeFactory;

    protected override async Task ExecuteAsync(CancellationToken ct)
    {
        using var scope = _scopeFactory.CreateScope();
        var orderService = scope.ServiceProvider.GetRequiredService<IOrderService>();
        // ...
    }
}

리소스

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.

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.

marketplace-publishing

Workflow for publishing skills and agents to the dotnet-skills Claude Code marketplace. Covers adding new content, updating plugin.json, validation, and release tagging.

관련 스킬