0Pricing
C# Academy · 강의

사용자 지정 미들웨어 작성

Use/Run/Map으로 인라인 미들웨어를 만들고 InvokeAsync를 사용하는 클래스 기반 미들웨어로 재사용 가능한 구성 요소를 구현합니다.

사용자 지정 미들웨어 작성은(는) CoddyKit의 무료 C# Academy 강의입니다. 이것은 4개 중 2번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 C# Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. C# Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

사용자 지정 미들웨어가 필요한 이유

사용자 지정 미들웨어는 여러 요청에 적용되는 횡단 관심사를 처리합니다. 예를 들면 로깅, 시간 측정, 오류 처리, 인증, 압축, 캐싱 등이 있습니다. 한 번 작성하면 일치하는 모든 요청에 해당 논리가 자동으로 실행됩니다.

Use를 사용한 인라인 미들웨어

미들웨어를 추가하는 가장 빠른 방법은 app.Use()에 대리자를 전달하는 것입니다. 프로토타이핑이나 정말 단순한 논리에 적합합니다.

app.Use(async (context, next) =>
{
    var start = DateTimeOffset.UtcNow;

    await next(context);

    var elapsed = DateTimeOffset.UtcNow - start;
    context.Response.Headers.Append(
        "X-Elapsed", elapsed.TotalMilliseconds.ToString("F0") + "ms");
});

클래스 기반 미들웨어(관례)

관례 기반 미들웨어 클래스에는 RequestDelegate를 받는 생성자와 public InvokeAsync 메서드가 있습니다. 추가 생성자 매개 변수는 DI에서 주입할 수 있습니다.

public class SecurityHeadersMiddleware
{
    private readonly RequestDelegate _next;

    public SecurityHeadersMiddleware(RequestDelegate next) => _next = next;

    public async Task InvokeAsync(HttpContext context)
    {
        var headers = context.Response.Headers;
        headers.Append("X-Content-Type-Options", "nosniff");
        headers.Append("X-Frame-Options", "DENY");
        headers.Append("X-XSS-Protection", "1; mode=block");
        headers.Append("Referrer-Policy", "strict-origin-when-cross-origin");

        await _next(context);
    }
}

// Extension method for clean registration:
public static class SecurityHeadersExtensions
{
    public static IApplicationBuilder UseSecurityHeaders(
        this IApplicationBuilder app) =>
        app.UseMiddleware<SecurityHeadersMiddleware>();
}

DI 범위 수명 의존성을 위한 IMiddleware

미들웨어에 DbContext와 같은 범위 지정 서비스가 필요하다면 IMiddleware를 구현하세요. 이를 범위 수명으로 등록하면 프레임워크가 요청마다 새 인스턴스를 확인합니다.

public class AuditMiddleware : IMiddleware
{
    private readonly AuditDbContext _db;
    private readonly IHttpContextAccessor _http;

    public AuditMiddleware(AuditDbContext db, IHttpContextAccessor http)
    {
        _db = db;
        _http = http;
    }

    public async Task InvokeAsync(HttpContext ctx, RequestDelegate next)
    {
        await next(ctx);

        if (ctx.Request.Method != "GET")
        {
            _db.AuditLogs.Add(new AuditLog
            {
                User   = ctx.User.Identity?.Name,
                Path   = ctx.Request.Path,
                Status = ctx.Response.StatusCode,
                At     = DateTime.UtcNow
            });
            await _db.SaveChangesAsync();
        }
    }
}

builder.Services.AddScoped<AuditMiddleware>();
app.UseMiddleware<AuditMiddleware>();

옵션을 사용하는 미들웨어

생성자의 옵션 객체를 통해 미들웨어에 구성을 전달하세요. DI 컨테이너에 옵션을 등록하거나 미들웨어를 등록할 때 인라인으로 전달할 수 있습니다.

public class ThrottleOptions
{
    public int MaxRequestsPerSecond { get; set; } = 100;
    public string[] ExcludedPaths { get; set; } = Array.Empty<string>();
}

public class ThrottleMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ThrottleOptions _options;

    public ThrottleMiddleware(RequestDelegate next, ThrottleOptions options)
    {
        _next   = next;
        _options = options;
    }
    // ...
}

// Registration with options:
app.UseMiddleware<ThrottleMiddleware>(new ThrottleOptions
{
    MaxRequestsPerSecond = 50
});

예외 처리 미들웨어

사용자 지정 예외 처리 미들웨어는 처리되지 않은 모든 예외를 포착하고 구조화된 오류 응답을 반환합니다.

public class GlobalExceptionMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<GlobalExceptionMiddleware> _logger;

    public GlobalExceptionMiddleware(RequestDelegate next, ILogger<GlobalExceptionMiddleware> l)
    {
        _next = next;
        _logger = l;
    }

    public async Task InvokeAsync(HttpContext ctx)
    {
        try { await _next(ctx); }
        catch (NotFoundException ex)
        {
            ctx.Response.StatusCode = 404;
            await ctx.Response.WriteAsJsonAsync(new { error = ex.Message });
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Unhandled exception");
            ctx.Response.StatusCode = 500;
            await ctx.Response.WriteAsJsonAsync(new { error = "Internal server error" });
        }
    }
}

요청 로깅 미들웨어

구조화된 요청/응답 로깅 미들웨어는 모든 요청에 대해 메서드, 경로, 상태 코드, 경과 시간을 기록합니다.

public class RequestLoggingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<RequestLoggingMiddleware> _log;

    public RequestLoggingMiddleware(RequestDelegate n, ILogger<RequestLoggingMiddleware> l)
    { _next = n; _log = l; }

    public async Task InvokeAsync(HttpContext ctx)
    {
        var sw = System.Diagnostics.Stopwatch.StartNew();
        await _next(ctx);
        sw.Stop();

        _log.LogInformation(
            "{Method} {Path} -> {Status} in {Ms}ms",
            ctx.Request.Method,
            ctx.Request.Path,
            ctx.Response.StatusCode,
            sw.ElapsedMilliseconds);
    }
}

UseWhen을 사용한 조건부 미들웨어

UseWhen은 조건식에 따라 파이프라인을 조건부로 분기하며, 파이프라인을 영구적으로 분리하지는 않습니다. 분기된 파이프라인은 이후 주 파이프라인으로 다시 합쳐집니다.

// Apply authentication middleware only to /api/* routes
app.UseWhen(
    ctx => ctx.Request.Path.StartsWithSegments("/api"),
    apiApp =>
    {
        apiApp.UseAuthentication();
        apiApp.UseAuthorization();
    });

// Public routes (/public/*) bypass authentication
app.MapGet("/public/health", () => "OK");
app.MapGet("/api/data", [Authorize] async (DataService s) => await s.GetAsync());

응답 캐싱 미들웨어

기본 제공 응답 캐싱 미들웨어는 전체 응답을 메모리에 저장합니다. 헤더나 특성을 사용해 엔드포인트별 캐시 정책을 구성하세요.

builder.Services.AddResponseCaching();

app.UseResponseCaching();

app.MapGet("/products", async (AppDbContext db) =>
{
    var products = await db.Products.AsNoTracking().ToListAsync();
    return Results.Ok(products);
})
.CacheOutput(p => p.Expire(TimeSpan.FromMinutes(2)).Tag("products"));

실제 사례: 유지 관리 모드 미들웨어

구성에서 플래그를 읽고 IOptionsMonitor를 통한 핫 리로드를 지원하며, 유지 관리 중에는 상태 확인이 아닌 요청을 거부하는 유지 관리 모드 미들웨어입니다.

public class MaintenanceMiddleware
{
    private readonly RequestDelegate _next;
    private readonly IOptionsMonitor<MaintenanceOptions> _options;

    public MaintenanceMiddleware(RequestDelegate next,
        IOptionsMonitor<MaintenanceOptions> opts)
    { _next = next; _options = opts; }

    public async Task InvokeAsync(HttpContext ctx)
    {
        if (_options.CurrentValue.IsEnabled
            && !ctx.Request.Path.StartsWithSegments("/health"))
        {
            ctx.Response.StatusCode = 503;
            ctx.Response.Headers.Append("Retry-After", "300");
            await ctx.Response.WriteAsJsonAsync(
                new { message = "Service under maintenance" });
            return;
        }
        await _next(ctx);
    }
}

빠른 확인

DbContext와 같은 범위 지정 서비스가 필요할 때 관례 기반 방식 대신 IMiddleware를 구현해야 하는 이유는 무엇인가요?

복습: 사용자 지정 미들웨어 작성

핵심 요점:

  • 인라인(Use 대리자): 빠르고 한 줄로 작성할 수 있으며, 클래스 기반 방식은 재사용과 테스트에 적합합니다
  • 관례 기반 클래스: 생성자가 RequestDelegate를 받고 InvokeAsync(HttpContext)를 구현합니다
  • IMiddleware: 요청마다 DI에서 확인되므로 범위 지정 의존성에 필요합니다
  • UseWhen: 주 파이프라인으로 다시 합쳐지는 조건부 분기를 제공합니다
  • 모든 오류를 포착하려면 예외 처리 미들웨어를 가장 먼저(가장 바깥쪽에) 작성합니다
  • 깔끔하고 쉽게 찾을 수 있는 등록을 위해 확장 메서드(UseXxx)를 만듭니다

자주 묻는 질문

“사용자 지정 미들웨어 작성” 강의는 무료인가요?

네 — “사용자 지정 미들웨어 작성” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 C# Academy 강의 전체를 잠금 해제할 수 있습니다. C# Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“사용자 지정 미들웨어 작성”에서 뭘 배우나요?

Use/Run/Map으로 인라인 미들웨어를 만들고 InvokeAsync를 사용하는 클래스 기반 미들웨어로 재사용 가능한 구성 요소를 구현합니다. 브라우저에서 직접 실행하는 실습 코드로 C# Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

C# Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 C# Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 2번째 강의입니다.

“사용자 지정 미들웨어 작성” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 C# Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 C# Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. ASP.NET Core 파이프라인 개요
  2. 사용자 지정 미들웨어 작성
  3. 단락 처리와 분기
  4. 미들웨어 순서와 기본 제공 미들웨어
← C# Academy(으)로 돌아가기