푸터 콘텐츠로 바로가기
Iron Academy Logo
C# 배우기
C# 배우기

다른 카테고리

.NET 10 최소 API의 데이터 유효성 검사

Tim Corey
~10m

미니멀 API는 컨트롤러 기반의 ASP.NET Core에 대한 슬림한 대안이었지만, 오랜 시간 동안 눈에 띄는 간격이 있었습니다: 들어오는 데이터를 검증하기 위한 내장 지원이 없었습니다. 이는 각 핸들러 내부에서 수동으로 검사하거나, 서드 파티 라이브러리에 의존해야 했습니다. .NET 10은 추가 패키지 없이 쿼리 문자열, 헤더 및 요청 본문에 대한 일급 데이터 주석 검증을 통해 그 간격을 메웁니다.

Tim Corey's 설명을 바탕으로 한 이 .NET 10 Minimal APIs에 대한 심층 분석은 검증 서비스 등록, 모델 클래스 주석 작성 및 설정을 망가뜨릴 수 있는 일반 접근자 한정자 함정을 피하는 방법을 설명합니다.

설정: 간단한 Minimal API

[0:44 - 1:35] 데모는 .NET 10에서 실행되는 간소한 ASP.NET Core Minimal API 프로젝트로 시작합니다. 표면적 영역은 의도적으로 작습니다: 서로 다른 모델을 받는 두 개의 POST 엔드포인트.

app.MapPost("/person", (Person person) => Results.Ok(person));
app.MapPost("/login", (LoginModel login) => Results.Ok(login));
app.MapPost("/person", (Person person) => Results.Ok(person));
app.MapPost("/login", (LoginModel login) => Results.Ok(login));

Person 모델은 기본 ID 필드를 포함합니다. LoginModel는 이메일 주소, 비밀번호 및 비밀번호 확인 필드를 처리합니다. 둘 다 JSON 본문으로 보냅니다. 이 시점에서는 입력 검사 자체가 전혀 없습니다; API는 빈 문자열과 형식이 잘못된 이메일 주소를 포함하여 전달받은 모든 것을 허용합니다.

.NET 10에 포함된 현대적 OpenAPI UI인 Scalar를 사용하여 브라우저에서 테스트 요청을 직접 실행할 수 있어서, 검증이 연결되기 전과 후에 API가 정확히 반환하는 것을 쉽게 확인할 수 있습니다.

검증 서비스 등록

[2:36 - 3:06] 어떠한 검증 속성도 효과를 발휘하기 전에, 서비스 수준에서 이를 선택해야 합니다. 서비스 등록 블록에서 단일 호출을 통해 모든 미니멀 API 엔드포인트 전반에서 집행을 활성화합니다:

builder.Services.AddValidation();
builder.Services.AddValidation();

그 한 줄이 전체 구성 단계입니다. 추가해야 할 미들웨어도 없고 연결할 파이프라인 단계도 없습니다. 서비스가 등록되면 프레임워크가 자동으로 이를 관리합니다. 이 호출을 건너뛰면, 데이터 주석 속성이 모델에 존재하지만 결코 평가되지 않으며, 요청은 포함된 내용을 막론하고 통과시킵니다.

이것은 첫 번째 디버깅 단계로 고려할 가치가 있습니다: 검증이 조용히 아무 것도 하지 않는다면, AddValidation()가 보통 누락된 부분입니다.

클래스 모델에 검증 추가

[3:00 - 3:55] 서비스가 등록된 상태에서, 클래스 기반 모델에 검증을 추가하는 것은 데이터 주석 속성을 사용하여 속성을 장식하는 문제입니다:

public class Person
{
    [Required]
    public string FirstName { get; set; }

    [Required]
    public string LastName { get; set; }
}
public class Person
{
    [Required]
    public string FirstName { get; set; }

    [Required]
    public string LastName { get; set; }
}

파일 상단에 System.ComponentModel.DataAnnotations using 지시문을 추가한 후, FirstNameLastName[Required]로 표시하면 검증이 보장됩니다. 이제 Scalar를 통해 빈 본문으로 POST 요청을 보내면 구조화된 오류 응답이 있는 400 Bad Request가 반환됩니다:

{
  "errors": {
    "FirstName": ["The FirstName field is required."],
    "LastName": ["The LastName field is required."]
  }
}

맞춤형 오류 처리도 필터 속성도 없습니다. 프레임워크는 응답을 자동으로 생성하고, 체크는 핸들러 본문이 실행되기 전에 실행되므로 엔드포인트 로직 내에서 null을 방어할 필요가 없습니다.

레코드에 대한 검증 적용

[4:29 - 5:30] 동일한 속성은 C# 레코드에 대해 작동하지만, 레코드 속성이 주로 기본 생성자에서 정의되는 데 비해 별도의 멤버 선언으로 정의되기 때문에 구문이 약간 다릅니다.

public record LoginModel(
    [Required] [EmailAddress] string Email,
    [Required] string Password,
    [Required] string ConfirmPassword
);
public record LoginModel(
    [Required] [EmailAddress] string Email,
    [Required] string Password,
    [Required] string ConfirmPassword
);

생성자 매개 변수에 있는 속성은 생성된 속성에 적용되므로 [Required][EmailAddress]는 클래스에서처럼 정확히 동일하게 작동합니다. 잘못된 이메일, 예를 들어 "notanemail"로 요청을 보내면 이제 Email 필드가 유효하지 않음을 식별하는 400가 반환됩니다.

[Compare] 속성은 ConfirmPasswordPassword와 일치하도록 강제하기 위해서도 사용될 수 있습니다. 레코드에서는 속성 대상에 대한 명시적 자극이 필요한데, 컴파일러가 매개변수 자체가 아닌 생성된 멤버를 의미한다는 것을 알아야 하기 때문입니다:

[property: Compare(nameof(Password))]
string ConfirmPassword
[property: Compare(nameof(Password))]
string ConfirmPassword

[property:] 대상은 컴파일러에게 속성을 매개 변수 대신 생성된 멤버에 연결하도록 지시합니다. 없다면, [Compare]는 컴파일되지만 검증 중에는 실행되지 않습니다. 이러한 맥락에서 레코드와 클래스 작업의 가장 까다로운 부분입니다: 클래스 속성은 속성을 자연스럽게 받아들이지만, 레코드 매개변수는 멤버 수준에서 작동하는 속성에 대해 명시적 대상을 필요로 합니다.

공개 접근 한정자 요구 사항

[7:51 - 9:00] 일반적인 함정은 쉽게 놓치기 쉬우며 오류 메시지를 생성하지 않습니다. 검증 시스템은 런타임에 모델 유형을 검사하기 위해 반사를 사용합니다. 특정 유형의 멤버를 찾기 위해서는 그 자체로 public로 표시되어야 합니다.

// Validation will NOT run; the class is internal by default
class Person { ... }

// Validation runs correctly
public class Person { ... }
// Validation will NOT run; the class is internal by default
class Person { ... }

// Validation runs correctly
public class Person { ... }

레코드도 동일한 규칙이 적용됩니다. 모델이 접근 수정자 없이 선언된 경우, C#은 기본적으로 internal로 설정하고, 서비스는 이를 완전히 건너뜁니다. 엔드포인트는 여전히 요청을 받고, 핸들러는 여전히 실행되며, 오류는 반환되지 않습니다; 검사는 단지 조용히 아무 행위도 하지 않습니다.

이것은 ASP.NET Core의 동작이며, Minimal APIs에 특정한 것은 아니지만 이 프로젝트들은 간결한 경향이 있어, 개발자가 가시성에 대해 생각하지 않고 Program.cs와 같은 파일 내에서 모델을 정의하는 경우가 종종 드러납니다.

프로젝트를 빠르게 감사하는 방법: 엔드포인트 핸들러에 전달된 모든 모델 유형은 명시적으로 public로 표시되어야 합니다. 그렇지 않으면 어떤 속성도 프레임워크를 작동시키지 않습니다.

검증할 수 있는 것

내장된 데이터 주석 속성은 추가 작업 없이 가장 일반적인 시나리오를 다룹니다:

  • [Required]는 null 또는 빈 값을 거부합니다.
  • [EmailAddress]는 이메일 문자열의 형식을 검증합니다.
  • [Compare]는 두 속성이 일치하는지 확인하며, 비밀번호 확인에 유용합니다.
  • [Range]는 숫자 또는 날짜 경계를 강화합니다.
  • [StringLength]는 최소값에 대한 선택적 제한과 함께 문자열 길이를 제한합니다.
  • [RegularExpression]는 사용자 정의 패턴에 대한 검증을 진행합니다.

이들은 모두 쿼리 문자열 매개변수, 요청 헤더 및 JSON 본문에 걸쳐 작동합니다. 동일한 모델 클래스나 레코드는 어떤 속성도 수정하지 않고 다른 소스에서 바인딩될 수 있습니다.

결론

[7:46 - 끝] AddValidation()가 등록되고 모델이 장식되면, Minimal APIs는 클래스와 레코드 모두에 걸쳐 입력 제약 조건을 자동으로 강화합니다. .NET 10에서 이를 작동시키려면, builder.Services.AddValidation()를 한 번 호출하고, 모델 속성을 표준 데이터 주석 속성으로 장식하며, 모든 모델 유형이 public로 표시되었는지 확인하십시오.

레코드의 [property:] 대상과 public 수정자 요구는 유일하게 진짜 번거로운 점입니다. 이는 쉽게 간과되고 컴파일 오류 대신에 조용한 실패를 일으키며, 입력 검사가 아무것도 하지 않는 것처럼 보일 때마다 이를 체크리스트에 포함하세요.

전체 소스 코드 설명을 보려면, Tim Corey의 유튜브 동영상을 시청하십시오.

Hero Worlddot related to .NET 10 최소 API의 데이터 유효성 검사
Hero Affiliate related to .NET 10 최소 API의 데이터 유효성 검사

사랑하는 것을 공유하여 더 많은 수익을 얻으세요

당신은 .NET, C#, Java, Python, 또는 Node.js를 다루는 개발자를 위한 콘텐츠를 만드나요? 당신의 전문성을 추가 수입으로 전환하세요!

아이언 서포트 팀

저희는 주 5일, 24시간 온라인으로 운영합니다.
채팅
이메일
전화해