Blazor EditForm 폼 유효성 검사 완전 정복

폼과 유효성 검사
📚 핵심 정리

폼과 유효성 검사

EditForm 컴포넌트와 DataAnnotations를 사용해 입력 폼을 만들고 유효성을 검사합니다.

📌 학습 목표

  • EditForm 컴포넌트의 역할과 기본 구조를 이해합니다.
  • DataAnnotations 어트리뷰트를 사용해 모델에 유효성 규칙을 선언합니다.
  • ValidationSummary와 ValidationMessage로 오류 메시지를 화면에 출력합니다.
  • OnValidSubmit과 OnInvalidSubmit 이벤트로 폼 제출을 처리합니다.
  • 사용자 지정 유효성 검사 어트리뷰트를 직접 작성합니다.

 

 

🔄 EditForm 유효성 검사 처리 흐름

 

① 모델 정의
(DataAnnotations)
② EditForm
컴포넌트 구성
③ 사용자
입력
④ Submit
이벤트
⑤ DataAnnotationsValidator
유효성 판정
⑥ OnValidSubmit
또는 OnInvalidSubmit

📝 개념 설명

1. EditForm 컴포넌트

Blazor에서 폼을 처리할 때는 HTML의 <form> 태그 대신 EditForm 컴포넌트를 사용합니다. EditForm은 단순한 래퍼가 아니라, 내부적으로 EditContext 객체를 생성해 폼 전체 상태(필드 수정 여부, 유효성 오류 목록 등)를 일괄 관리합니다.

EditForm의 주요 속성과 이벤트

  • Model: 폼에 바인딩할 C# 데이터 모델 객체
  • OnValidSubmit: 유효성 검사 통과 시 호출되는 이벤트 콜백
  • OnInvalidSubmit: 유효성 검사 실패 시 호출되는 이벤트 콜백
  • OnSubmit: 유효성 결과와 무관하게 항상 호출 (직접 검사 코드 작성 필요)
@* RegisterForm.razor — EditForm 기본 구조 *@
<EditForm Model="@registerModel" OnValidSubmit="@HandleValidSubmit" OnInvalidSubmit="@HandleInvalidSubmit">
    <DataAnnotationsValidator />
    <ValidationSummary />

    <div>
        <label>이름</label>
        <InputText @bind-Value="registerModel.Name" />
        <ValidationMessage For="@(() => registerModel.Name)" />
    </div>

    <button type="submit">제출</button>
</EditForm>

@code {
    private RegisterModel registerModel = new();

    private void HandleValidSubmit()
    {
        Console.WriteLine("폼 제출 성공!");
    }

    private void HandleInvalidSubmit()
    {
        Console.WriteLine("입력값을 확인해 주세요.");
    }
}
💡 OnSubmit vs OnValidSubmit
OnSubmit을 사용하면 DataAnnotationsValidator가 있어도 유효성 검사가 자동 실행되지 않습니다. editContext.Validate()를 직접 호출해야 합니다. 특별한 이유가 없다면 OnValidSubmit / OnInvalidSubmit 조합을 사용하세요.

2. DataAnnotations — 선언형 유효성 규칙

DataAnnotations는 C# 모델 클래스의 프로퍼티에 어트리뷰트(Attribute)를 부착해 유효성 규칙을 선언적으로 정의하는 방식입니다. System.ComponentModel.DataAnnotations 네임스페이스를 사용하며, ASP.NET Core 전반에서 폭넓게 활용됩니다.

주요 DataAnnotations 어트리뷰트

어트리뷰트설명주요 매개변수
[Required]필수 입력 (null·빈 값 불허)ErrorMessage
[StringLength(max)]문자열 최대·최소 길이MaximumLength, MinimumLength
[Range(min, max)]숫자 범위 제한minimum, maximum
[EmailAddress]이메일 형식 검사ErrorMessage
[Phone]전화번호 형식 검사ErrorMessage
[Url]URL 형식 검사ErrorMessage
[RegularExpression]정규식 패턴 검사pattern
[Compare]다른 프로퍼티와 값 비교otherProperty
[MinLength(n)]최소 길이length
[MaxLength(n)]최대 길이length
using System.ComponentModel.DataAnnotations;

public class RegisterModel
{
    [Required(ErrorMessage = "이름을 입력해 주세요.")]
    [StringLength(50, MinimumLength = 2, ErrorMessage = "이름은 2~50자 사이여야 합니다.")]
    public string Name { get; set; } = "";

    [Required(ErrorMessage = "이메일을 입력해 주세요.")]
    [EmailAddress(ErrorMessage = "올바른 이메일 형식이 아닙니다.")]
    public string Email { get; set; } = "";

    [Required(ErrorMessage = "비밀번호를 입력해 주세요.")]
    [StringLength(100, MinimumLength = 8, ErrorMessage = "비밀번호는 8~100자 사이여야 합니다.")]
    public string Password { get; set; } = "";

    [Required(ErrorMessage = "비밀번호 확인을 입력해 주세요.")]
    [Compare(nameof(Password), ErrorMessage = "비밀번호가 일치하지 않습니다.")]
    public string ConfirmPassword { get; set; } = "";

    [Range(14, 120, ErrorMessage = "나이는 14~120세 사이여야 합니다.")]
    public int Age { get; set; }
}

3. 유효성 검사 컴포넌트

① DataAnnotationsValidator

EditForm 내에 배치해 DataAnnotations 어트리뷰트 기반 검사를 활성화하는 컴포넌트입니다. EditForm 내에 반드시 배치해야 어트리뷰트 검사가 실행됩니다.

<DataAnnotationsValidator />

② ValidationSummary

폼 전체의 유효성 오류 메시지를 <ul> 목록 형태로 한꺼번에 표시합니다. 폼 상단에 배치해 사용자가 모든 오류를 한눈에 파악하게 합니다.

<ValidationSummary />

③ ValidationMessage

특정 프로퍼티의 오류 메시지만 해당 필드 옆에 표시합니다. For 속성에 람다 표현식으로 프로퍼티를 지정합니다.

<ValidationMessage For="@(() => registerModel.Email)" />
💡 ValidationSummary vs ValidationMessage 선택 기준
두 컴포넌트를 함께 사용하면 동일한 오류가 두 곳에 중복 표시됩니다. 간단한 폼은 ValidationSummary(폼 상단 전체 목록)만, 복잡한 폼은 ValidationMessage(필드 옆 개별 오류)만 사용하는 것이 일반적입니다.

4. Blazor 입력(Input) 컴포넌트

Blazor는 HTML 입력 요소를 감싸는 전용 컴포넌트를 제공합니다. 이 컴포넌트들은 EditContext와 자동으로 연동되어 유효성 상태에 따라 CSS 클래스(valid / invalid / modified)가 자동 적용됩니다.

Blazor 컴포넌트대응 HTML 요소바인딩 타입
InputText<input type="text">string
InputNumber<T><input type="number">int, decimal, double 등
InputDate<T><input type="date">DateTime, DateOnly
InputCheckbox<input type="checkbox">bool
InputSelect<T><select>enum, string 등
InputTextArea<textarea>string

 

 

📌 DataAnnotations 핵심 어트리뷰트

 

🔒
[Required]
값 필수 입력. null·빈 문자열 불허. 모든 필수 필드의 기본 어트리뷰트
📏
[StringLength]
문자열 최대·최소 길이 지정. MinimumLength 매개변수로 하한도 함께 설정
📧
[EmailAddress]
이메일 형식 자동 검증. 별도 정규식 없이 xxx@yyy.zzz 패턴을 검사
🔗
[Compare]
다른 프로퍼티와 값 동일 여부 비교. 비밀번호 확인 필드에 필수적으로 사용

💡 예제 & 실습 — 회원가입 폼 완성하기

DataAnnotations 모델 정의, EditForm 구성, 오류 메시지 표시, 폼 제출 처리를 모두 포함한 완전한 회원가입 폼을 단계별로 작성합니다.

Step 1. 모델 클래스 정의 (RegisterModel.cs)

// Models/RegisterModel.cs
using System.ComponentModel.DataAnnotations;

public class RegisterModel
{
    [Required(ErrorMessage = "이름을 입력해 주세요.")]
    [StringLength(50, MinimumLength = 2, ErrorMessage = "이름은 2~50자 사이여야 합니다.")]
    public string Name { get; set; } = "";

    [Required(ErrorMessage = "이메일을 입력해 주세요.")]
    [EmailAddress(ErrorMessage = "올바른 이메일 형식이 아닙니다.")]
    public string Email { get; set; } = "";

    [Required(ErrorMessage = "비밀번호를 입력해 주세요.")]
    [StringLength(100, MinimumLength = 8, ErrorMessage = "비밀번호는 8~100자 사이여야 합니다.")]
    public string Password { get; set; } = "";

    [Required(ErrorMessage = "비밀번호 확인을 입력해 주세요.")]
    [Compare(nameof(Password), ErrorMessage = "비밀번호가 일치하지 않습니다.")]
    public string ConfirmPassword { get; set; } = "";

    [Range(14, 120, ErrorMessage = "나이는 14~120세 사이여야 합니다.")]
    public int Age { get; set; }

    [Range(typeof(bool), "true", "true", ErrorMessage = "이용약관에 동의해 주세요.")]
    public bool AgreeToTerms { get; set; }
}

Step 2. 폼 컴포넌트 작성 (Register.razor)

@page "/register"

<h2>회원가입</h2>

@if (isSuccess)
{
    <p>@successMessage</p>
}
else
{
    <EditForm Model="@registerModel" OnValidSubmit="@HandleValidSubmit">
        <DataAnnotationsValidator />
        <ValidationSummary />

        <div class="form-group">
            <label>이름 *</label>
            <InputText @bind-Value="registerModel.Name" class="form-control" />
            <ValidationMessage For="@(() => registerModel.Name)" />
        </div>

        <div class="form-group">
            <label>이메일 *</label>
            <InputText @bind-Value="registerModel.Email" class="form-control" />
            <ValidationMessage For="@(() => registerModel.Email)" />
        </div>

        <div class="form-group">
            <label>비밀번호 (8자 이상) *</label>
            <InputText @bind-Value="registerModel.Password" type="password" class="form-control" />
            <ValidationMessage For="@(() => registerModel.Password)" />
        </div>

        <div class="form-group">
            <label>비밀번호 확인 *</label>
            <InputText @bind-Value="registerModel.ConfirmPassword" type="password" class="form-control" />
            <ValidationMessage For="@(() => registerModel.ConfirmPassword)" />
        </div>

        <div class="form-group">
            <label>나이</label>
            <InputNumber @bind-Value="registerModel.Age" class="form-control" />
            <ValidationMessage For="@(() => registerModel.Age)" />
        </div>

        <div class="form-group">
            <label>
                <InputCheckbox @bind-Value="registerModel.AgreeToTerms" />
                이용약관에 동의합니다 *
            </label>
            <ValidationMessage For="@(() => registerModel.AgreeToTerms)" />
        </div>

        <button type="submit">가입하기</button>
    </EditForm>
}

@code {
    private RegisterModel registerModel = new();
    private bool isSuccess = false;
    private string successMessage = "";

    private void HandleValidSubmit()
    {
        // 유효성 검사 통과 — 실제 저장 로직 실행
        isSuccess = true;
        successMessage = $"{registerModel.Name}님, 회원가입이 완료되었습니다!";
    }
}

Step 3. 코드 해설

  1. Model=”@registerModel”: EditForm이 registerModel 객체를 추적 대상으로 등록하고 내부 EditContext를 생성합니다.
  2. DataAnnotationsValidator: 없으면 어트리뷰트 검사가 전혀 실행되지 않아 OnValidSubmit이 항상 성공으로 처리됩니다.
  3. ValidationSummary: 폼 상단에 모든 유효성 오류를 목록으로 표시합니다.
  4. @bind-Value: 입력 컴포넌트와 모델 프로퍼티를 양방향 바인딩합니다.
  5. ValidationMessage For=”@(() => …)”: 람다 표현식으로 특정 프로퍼티를 지정해 해당 필드의 오류 메시지만 표시합니다.
  6. OnValidSubmit: 모든 유효성 검사를 통과한 후에만 HandleValidSubmit이 호출됩니다.

Step 4. 사용자 지정(Custom) 유효성 검사 어트리뷰트

DataAnnotations로 표현하기 어려운 복잡한 규칙은 ValidationAttribute를 상속해 직접 작성할 수 있습니다.

// 한국 휴대폰 번호 형식 검사 어트리뷰트
using System.ComponentModel.DataAnnotations;
using System.Text.RegularExpressions;

public class KoreanPhoneAttribute : ValidationAttribute
{
    protected override ValidationResult? IsValid(
        object? value, ValidationContext validationContext)
    {
        if (value is null || string.IsNullOrEmpty(value.ToString()))
            return ValidationResult.Success; // null 여부는 [Required]가 담당

        string phone = value.ToString()!;
        bool isValid = Regex.IsMatch(phone, @"^010[-]?[0-9]{4}[-]?[0-9]{4}$");

        return isValid
            ? ValidationResult.Success
            : new ValidationResult(
                ErrorMessage ?? "올바른 휴대폰 번호를 입력해 주세요. (예: 010-1234-5678)");
    }
}

// 모델에서 사용
public class ContactModel
{
    [Required]
    [KoreanPhone(ErrorMessage = "휴대폰 번호 형식이 올바르지 않습니다.")]
    public string Phone { get; set; } = "";
}

⚠️ 자주 틀리는 것 / 주의사항

⚠️ 주의 1: DataAnnotationsValidator 누락
EditForm 내에 <DataAnnotationsValidator />를 배치하지 않으면 DataAnnotations 어트리뷰트가 전혀 동작하지 않습니다. OnValidSubmit이 항상 성공으로 처리되는 현상이 발생합니다.
⚠️ 주의 2: Model 객체 재생성 시 동작
폼 리셋 목적으로 registerModel = new RegisterModel()을 실행하면 EditForm의 내부 EditContext가 함께 초기화됩니다. 이는 의도된 동작이지만, EditForm이 변경을 감지하려면 StateHasChanged()를 함께 호출해야 합니다.
⚠️ 주의 3: [Required]와 nullable 타입
C# 8 이상에서 string?(nullable)으로 선언된 프로퍼티는 [Required]가 있어도 null이 유효한 값으로 취급될 수 있습니다. string(non-nullable)으로 선언하고 = ""로 초기화하는 것이 권장 패턴입니다.
⚠️ 주의 4: ValidationMessage For에는 반드시 람다 표현식
For="@(registerModel.Name)"처럼 값을 직접 전달하면 컴파일 오류가 발생합니다. 반드시 For="@(() => registerModel.Name)"처럼 () => 람다 표현식으로 감싸야 합니다.
⚠️ 주의 5: InputText에서 type=”password” 사용
Blazor 6 이하에서는 <InputText type="password">가 동작하지 않았습니다. Blazor 7 이상부터는 추가 어트리뷰트 전달(attribute splatting)이 지원되어 type="password"를 직접 사용할 수 있습니다. 하위 호환이 걱정된다면 <input type="password" @bind="model.Password" @bind:event="oninput">를 사용하되, 이 경우 ValidationMessage가 동작하지 않을 수 있으니 주의하세요.

🎯 마무리

Blazor의 EditForm과 DataAnnotations를 조합하면 별도의 유효성 검사 로직 없이도 모델 클래스 선언만으로 강력한 폼 유효성 검사를 구현할 수 있습니다. 모델에 어트리뷰트를 부착하고, EditForm 내에 DataAnnotationsValidator를 배치하며, ValidationMessage로 오류를 표시하는 세 가지 패턴만 숙지해도 실무의 대부분 요구사항을 충족할 수 있습니다.

✅ 핵심 정리
  • EditForm은 Blazor의 폼 컴포넌트로, 내부적으로 EditContext를 생성해 폼 전체 상태를 관리합니다.
  • 모델 프로퍼티에 DataAnnotations 어트리뷰트([Required], [StringLength], [EmailAddress], [Compare] 등)를 부착해 유효성 규칙을 선언합니다.
  • DataAnnotationsValidator는 EditForm 내에 반드시 배치해야 어트리뷰트 검사가 실행됩니다.
  • ValidationSummary는 폼 전체 오류 목록, ValidationMessage For=”@(() => 프로퍼티)”는 필드별 오류 메시지를 표시합니다.
  • OnValidSubmit은 유효성 통과 시, OnInvalidSubmit은 실패 시 호출됩니다.
  • 복잡한 유효성 규칙은 ValidationAttribute를 상속한 사용자 지정 어트리뷰트로 구현합니다.
  • Blazor Input 컴포넌트(InputText, InputNumber, InputCheckbox 등)는 EditContext와 자동 연동되어 valid/invalid CSS 클래스가 자동 적용됩니다.

댓글 남기기

Wordpress Social Share Plugin powered by Ultimatelysocial
Copy link
URL has been copied successfully!
THREADS
RSS
error: 저작권 콘텐츠보호를 부탁드립니다.