출시·고도화 중
ASP.NET Core 안내서 · 3/6
ASP.NET Core는 서로 맞물리는 몇 가지 개념으로 이루어져 있습니다. 앱을 실행하는 호스트, 모든 요청이 지나가는 미들웨어 파이프라인, 의존성 주입 컨테이너, 최소 API나 컨트롤러로 작성하는 엔드포인트, 그리고 이 모두가 함께 쓰는 설정과 로깅입니다.
WebApplication.CreateBuilder는 Kestrel, 설정, 로깅, DI를 갖춘 호스트를 만듭니다. Build() 뒤에 부르는 Use... 호출 하나하나가 미들웨어를 추가합니다. 요청은 추가된 순서대로 미들웨어를 지나가며, 각 미들웨어는 다음 단계를 부르기 전후에 일을 하거나 요청을 그 자리에서 끝낼 수 있습니다.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddProblemDetails();
var app = builder.Build();
app.UseExceptionHandler(); // 맨 앞: 뒤에서 나는 오류를 모두 잡습니다
app.UseHttpsRedirection();
app.Use(async (context, next) =>
{
var watch = System.Diagnostics.Stopwatch.StartNew();
await next(context); // 파이프라인의 나머지를 실행
app.Logger.LogInformation("{Path} took {Ms} ms", context.Request.Path, watch.ElapsedMilliseconds);
});
app.UseAuthentication();
app.UseAuthorization();
app.MapGet("/", () => "Hello");
app.Run();순서가 중요합니다. 예외 처리기는 모든 것을 감싸도록 맨 앞에 두고, 인증은 권한 부여보다 먼저 실행되어야 합니다. 엔드포인트(Map...)는 파이프라인의 끝에서 실행됩니다.
서비스는 builder.Services에 세 가지 수명 중 하나로 등록합니다.
| 수명 | 인스턴스 단위 | 주로 쓰는 곳 |
|---|---|---|
AddSingleton | 앱 전체에 하나 | 캐시, 시계, 상태 없는 도우미 |
AddScoped | HTTP 요청마다 하나 | DbContext, 작업 단위 서비스 |
AddTransient | 요청할 때마다 새로 | 가벼운 상태 없는 서비스 |
builder.Services.AddSingleton(TimeProvider.System);
builder.Services.AddScoped<TodoService>();프레임워크는 객체를 만드는 곳마다 서비스를 주입합니다. 최소 API 처리기의 매개변수, 컨트롤러 생성자, 다른 서비스의 생성자가 모두 해당됩니다. 범위(scoped) 서비스를 싱글턴에 주입해서는 안 되며, Development 환경에서는 컨테이너가 이를 감지해 예외를 냅니다.
두 방식은 라우팅, DI, 미들웨어를 똑같이 사용합니다. 최소 API는 람다나 메서드를 바로 연결합니다. TypedResults와 Results<...>를 쓰면 가능한 응답이 시그니처와 OpenAPI 문서에 드러납니다.
using Microsoft.AspNetCore.Http.HttpResults;
var todos = app.MapGroup("/todos");
todos.MapGet("/{id:int}", Results<Ok<Todo>, NotFound> (int id, TodoService service) =>
service.Find(id) is { } todo ? TypedResults.Ok(todo) : TypedResults.NotFound());
todos.MapPost("/", (Todo todo, TodoService service) =>
{
service.Add(todo);
return TypedResults.Created($"/todos/{todo.Id}", todo);
});컨트롤러는 액션을 클래스로 묶고 특성(attribute)으로 경로를 적습니다.
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/[controller]")]
public class TodosController(TodoService service) : ControllerBase
{
[HttpGet("{id:int}")]
public ActionResult<Todo> Get(int id)
{
var todo = service.Find(id);
if (todo is null) return NotFound();
return todo;
}
}
// Program.cs: builder.Services.AddControllers(); ... app.MapControllers();새 API에는 코드가 간결하고 성능이 좋은 최소 API가 기본입니다. 컨트롤러는 MVC 관례, 필터, 특성 기반 모델 바인딩을 선호하는 팀에 맞고, 뷰가 있는 MVC 앱의 바탕이 됩니다.
경로 템플릿에는 {id:int}, {slug}, {page?} 같은 매개변수와 제약 조건을 씁니다. 처리기 매개변수는 자동으로 바인딩됩니다. 단순 형식은 경로 값과 쿼리 문자열에서, 복합 형식은 JSON 본문에서, 등록된 서비스는 DI에서 가져오며, HttpContext, HttpRequest, CancellationToken 같은 특수 형식도 받을 수 있습니다. [FromQuery], [FromHeader], [FromServices] 특성(네임스페이스 Microsoft.AspNetCore.Mvc)으로 출처를 명시할 수도 있습니다.
// GET /posts/hello-world?page=2
app.MapGet("/posts/{slug}", (string slug, int? page, HttpRequest request) =>
$"{slug}, page {page ?? 1}, agent {request.Headers.UserAgent}");설정 섹션을 클래스에 바인딩하고 옵션 패턴으로 주입합니다.
using Microsoft.Extensions.Options;
builder.Services.Configure<TodoOptions>(builder.Configuration.GetSection("Todo"));
app.MapGet("/settings", (IOptions<TodoOptions> options) => options.Value.PageSize);
public class TodoOptions
{
public int PageSize { get; set; } = 20;
}IOptionsSnapshot<T>는 요청마다 값을 다시 읽고, IOptionsMonitor<T>는 값이 바뀌면 알려 줍니다. AddOptions<T>().BindConfiguration("Todo").ValidateDataAnnotations().ValidateOnStart()를 쓰면 잘못된 설정을 시작 시점에 바로 잡아냅니다.
public class TodoService(ILogger<TodoService> logger)
{
public void Add(Todo todo)
{
logger.LogInformation("Added todo {TodoId}", todo.Id);
}
}ILogger<T>는 DI로 어디서든 받을 수 있습니다. 문자열 보간 대신 이름 있는 자리 표시자를 쓴 메시지 템플릿을 사용하면, 로깅 공급자가 TodoId를 구조화된 필드로 저장할 수 있습니다. 범주별 최소 수준은 설정의 Logging:LogLevel 섹션에서 정합니다.
ILogger<T>로 구조화된 로그를 다룹니다.
댓글 0개
로그인 · 로그인하면 댓글을 남길 수 있습니다.
첫 댓글을 남겨 보세요.