출시·고도화 중
ASP.NET Core 안내서 · 4/6
HTTP 요청은 하나하나 독립적으로 처리되므로, 오래 남아야 하는 상태는 데이터베이스 같은 외부 저장소에 둡니다. 이 장에서는 Entity Framework Core(EF Core)로 데이터를 다루고, 입력 검증과 인증의 기초를 살펴봅니다.
EF Core는 Microsoft의 객체-관계 매퍼(ORM)입니다. 데이터베이스 공급자와 디자인 타임 패키지를 추가하고 dotnet-ef 도구를 설치합니다.
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet tool install --global dotnet-ef다른 공급자로는 Microsoft.EntityFrameworkCore.SqlServer, Npgsql.EntityFrameworkCore.PostgreSQL 등이 있습니다.
// Data/TodoDb.cs
using Microsoft.EntityFrameworkCore;
public class Todo
{
public int Id { get; set; }
public required string Title { get; set; }
public bool IsDone { get; set; }
}
public class TodoDb(DbContextOptions<TodoDb> options) : DbContext(options)
{
public DbSet<Todo> Todos => Set<Todo>();
}Program.cs에서 컨텍스트를 등록합니다. AddDbContext는 범위(scoped) 수명을 쓰므로 요청마다 컨텍스트가 따로 만들어집니다.
builder.Services.AddDbContext<TodoDb>(options =>
options.UseSqlite(builder.Configuration.GetConnectionString("Default")));마이그레이션은 모델 변경을 버전이 매겨진 스키마 변경으로 바꿉니다.
dotnet ef migrations add InitialCreate # Migrations/*.cs 생성
dotnet ef database update # 아직 적용하지 않은 마이그레이션 적용
dotnet ef migrations script --idempotent -o migrate.sql생성된 마이그레이션은 검토한 뒤 커밋합니다. 운영 환경에서는 앱 시작 시 자동으로 마이그레이션하기보다, SQL 스크립트나 마이그레이션 번들(dotnet ef migrations bundle)을 배포 단계에서 적용하는 편이 안전합니다.
var todos = app.MapGroup("/todos");
todos.MapGet("/", async (TodoDb db) =>
await db.Todos.AsNoTracking().ToListAsync());
todos.MapGet("/{id:int}", async (int id, TodoDb db) =>
await db.Todos.FindAsync(id) is { } todo ? Results.Ok(todo) : Results.NotFound());
todos.MapPost("/", async (Todo todo, TodoDb db) =>
{
db.Todos.Add(todo);
await db.SaveChangesAsync();
return Results.Created($"/todos/{todo.Id}", todo);
});
todos.MapDelete("/{id:int}", async (int id, TodoDb db) =>
await db.Todos.Where(t => t.Id == id).ExecuteDeleteAsync() == 1
? Results.NoContent()
: Results.NotFound());I/O를 기다리는 동안 요청 스레드가 막히지 않도록 비동기 API를 쓰고, 읽기 전용 조회에는 AsNoTracking()을 붙입니다. 엔터티를 요청에서 바로 받는 방식은 예제로는 괜찮지만, 실제 앱에서는 클라이언트가 Id 같은 필드를 마음대로 정하지 못하도록 별도의 요청 형식(DTO)을 받는 것이 일반적입니다.
[ApiController]가 붙은 컨트롤러에서는 요청 형식의 데이터 주석 특성이 자동으로 검사되고, 잘못된 입력에는 ValidationProblemDetails 본문과 함께 400 응답이 나갑니다. 최근 버전의 ASP.NET Core에서는 builder.Services.AddValidation()으로 최소 API에서도 같은 검증을 쓸 수 있습니다.
using System.ComponentModel.DataAnnotations;
public class CreateTodo
{
[Required, StringLength(200)]
public string Title { get; set; } = "";
public bool IsDone { get; set; }
}규칙이 복잡하면 FluentValidation 라이브러리를 쓰거나, 직접 검사한 뒤 TypedResults.ValidationProblem(errors)를 반환하기도 합니다.
인증(authentication)은 호출한 사람이 누구인지 확인하고, 권한 부여(authorization)는 그 사람이 무엇을 할 수 있는지 정합니다. API에서는 JWT 전달자(bearer) 토큰을 흔히 씁니다.
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
dotnet user-jwts create # 개발용 토큰을 발급하고 앱 설정을 채웁니다using System.Security.Claims;
builder.Services.AddAuthentication().AddJwtBearer();
builder.Services.AddAuthorization();
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
app.MapGet("/me", (ClaimsPrincipal user) => user.Identity?.Name)
.RequireAuthorization();AddJwtBearer()는 설정의 Authentication:Schemes:Bearer 섹션을 읽으며, Development 환경에서는 dotnet user-jwts가 이 값을 채워 줍니다. 컨트롤러에서는 [Authorize]와 [AllowAnonymous] 특성을 씁니다. 서버에서 화면을 그리는 앱은 보통 쿠키 인증과 ASP.NET Core Identity로 로그인과 비밀번호 해시를 처리하거나, OpenID Connect 공급자에게 로그인을 맡깁니다.
AddDbContext로 범위 수명의 DbContext를 등록합니다.dotnet ef migrations add로 관리하고, 마이그레이션은 배포 단계에서 적용합니다.[ApiController] 컨트롤러에서, 그리고 AddValidation()을 쓰면 최소 API에서도 자동으로 검사됩니다.UseAuthentication, UseAuthorization을 호출한 뒤 RequireAuthorization이나 [Authorize]로 엔드포인트를 보호합니다.
댓글 0개
로그인 · 로그인하면 댓글을 남길 수 있습니다.
첫 댓글을 남겨 보세요.