유효성 검사는 보통은 JavaScript를 통해 클라이언트에서 미리 체크하거나, 또는 보안상의 이유로 서버에서 다시 한 번 검사한다.
그런데 만약 사용자의 입력이 여러 필드에 걸쳐 동시에 잘못되었고,
한꺼번에 모든 에러를 사용자에게 보여주는 구조가 필요하다면 IValidatableObject 를 통해 유효성 검사가 가능하다.
1. 모델 정의
using System.ComponentModel.DataAnnotations;
public class SaveModel : IValidatableObject
{
public string ID { get; set; } = null!;
public string Name { get; set; } = null!;
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (string.IsNullOrWhiteSpace(ID))
{
yield return new ValidationResult(
"ID 값이 필요합니다.",
new[] { nameof(ID) }
);
}
if (string.IsNullOrWhiteSpace(Name))
{
yield return new ValidationResult(
"Name 값이 필요합니다.",
new[] { nameof(Name) }
);
}
}
}
2. Razor Page 또는 Controller (POST)
public class IndexModel : PageModel
{
public async Task<JsonResult> OnPostSave([FromForm] SaveModel data)
{
//이 부분은 테스트 용
if (!ModelState.IsValid)
{
var errors = ModelState
.Where(kvp => kvp.Value.Errors.Count > 0)
.SelectMany(kvp => kvp.Value.Errors.Select(e => new
{
ErrorMessage = e.ErrorMessage,
MemberNames = new[] { kvp.Key }
}));
return new JsonResult(new
{
Success = false,
Errors = errors
});
}
return new JsonResult(new { Success = true });
}
}
3. 유효성 처리 JavaScript
<form id="saveForm">
<label>ID: <input type="text" name="ID" id="ID" /></label><br />
<label>Name: <input type="text" name="Name" id="Name" /></label><br />
<button type="submit">저장</button>
</form>
<div id="errorArea" style="color:red; margin-top: 10px;"></div>
const formElement = document.getElementById("saveForm");
function markError(field) {
field.classList.add("error-field");
}
function clearErrorMarks() {
const errorFields = formElement.querySelectorAll(".error-field");
errorFields.forEach(field => field.classList.remove("error-field"));
}
formElement.addEventListener("submit", async function (e) {
e.preventDefault();
clearErrorMarks();
document.getElementById("errorArea").innerHTML = "";
const formData = new FormData(this);
const response = await fetch("/Index?handler=Save", {
method: "POST",
body: formData
});
const data = await response.json();
if (data.Errors) {
const errors = data.Errors;
const errorMessages = `<div>${errors.map(error => `• ${error.ErrorMessage}`).join("<br />")}</div>`;
document.getElementById("errorArea").innerHTML = errorMessages;
errors.forEach(error => {
if (error.MemberNames && error.MemberNames.length > 0) {
error.MemberNames.forEach(fieldName => {
const field = formElement.querySelector(`[name="${fieldName}"]`);
if (field) {
markError(field);
}
});
}
});
} else if (!data.Success) {
document.getElementById("errorArea").innerHTML = "알 수 없는 오류가 발생했습니다.";
} else {
console.log("저장 완료!");
this.reset();
}
});
IValidatableObject를 이용한 유효성 검사는 자주 쓰이진 않지만 검증 로직이 여러 필드에 걸쳐 복잡하게 얽혀 있을 때
또는 여러 에러를 한 번에 수집해서 처리해야 할 때 유용하다.
보통은 각 필드에 어노테이션을 붙이거나 하나씩 수동으로 추가하는 방식이 많지만
이렇게 Validate() 안에서 조건별로 yield return만 추가해 주면
복잡한 유효성 체크도 훨씬 깔끔하게 정리할 수 있다.
'.NET' 카테고리의 다른 글
.NET Core API에서 JWT + Refresh Token 인증 구현하기 (0) | 2025.04.07 |
---|---|
Dapper vs Entity Framework vs ADO.NET – 어떤 걸 써야 할까? (0) | 2025.04.06 |