ASP.NET Core에서 FluentValidation: 요청당 하나의 Validator가 진정한 베스트 프랙티스인 이유

발행: (2026년 2월 4일 오전 10:02 GMT+9)
5 min read
원문: Dev.to

Source: Dev.to

FluentValidation in ASP.NET Core: 요청당 하나의 Validator가 진정한 베스트 프랙티스인 이유에 대한 표지 이미지

FluentValidation이란?

FluentValidation은 유창하고 강타입 문법을 사용해 검증 규칙을 정의할 수 있게 해 주는 인기 있는 .NET 라이브러리이며, 특성(attribute)이나 컨트롤러 로직 대신에 사용할 수 있습니다.

다음과 같이 작성하는 대신:

[Required]
[EmailAddress]
public string Email { get; set; }

별도의 Validator 클래스에서 규칙을 정의합니다:

RuleFor(x => x.Email)
    .NotEmpty()
    .EmailAddress();

이렇게 하면 API 모델이 깔끔해지고 검증 로직이 전용 검증 레이어로 이동합니다.

검증을 컨트롤러에 두면 안 되는 이유

컨트롤러에 검증 로직을 넣으면 다음과 같은 문제가 발생합니다.

  • 중복된 로직
  • 테스트하기 어려운 코드
  • 비대해진 컨트롤러
  • 비즈니스 규칙이 API 레이어에 누수

FluentValidation은 ASP.NET Core 파이프라인에 직접 통합되므로 컨트롤러가 실행되기 전에 자동으로 검증이 수행됩니다. 검증에 실패하면 프레임워크가 400 Bad Request를 반환하므로 컨트롤러에 별도의 코드를 작성할 필요가 없습니다.

ASP.NET Core에서 FluentValidation 구현 방법

패키지 설치

dotnet add package FluentValidation
dotnet add package FluentValidation.AspNetCore

요청 DTO 만들기

public class CreateUserRequest
{
    public string FirstName { get; set; }
    public string Email { get; set; }
    public int Age { get; set; }
}

이는 데이터베이스 엔터티가 아니라 요청 DTO입니다.

Validator 만들기

public class CreateUserRequestValidator : AbstractValidator
{
    public CreateUserRequestValidator()
    {
        RuleFor(x => x.FirstName)
            .NotEmpty()
            .MinimumLength(3);

        RuleFor(x => x.Email)
            .NotEmpty()
            .EmailAddress();

        RuleFor(x => x.Age)
            .InclusiveBetween(18, 60);
    }
}

FluentValidation 등록

builder.Services.AddControllers()
    .AddFluentValidationAutoValidation();

builder.Services.AddValidatorsFromAssemblyContaining();

이렇게 하면 모든 요청이 자동으로 검증됩니다.

일반적인 Generic Validator를 사용하면 안 되는 이유

많은 개발자들이 “모든 요청에 대해 하나의 제네릭 Validator를 만들 수 있을까?”라고 묻습니다.

짧은 답변: 아니요.

이유는 다음과 같습니다.

  • 각 요청마다 다른 비즈니스 규칙이 존재한다
  • 제네릭 Validator는 거대한 God 클래스가 된다
  • 규칙이 if/else 혼란으로 변한다
  • 타입 안전성을 잃는다
  • 단일 책임 원칙(SRP)을 위반한다

검증은 제네릭이 아니라 사용 사례별이어야 합니다.

업계 표준 패턴

CreateUserRequest      → CreateUserRequestValidator
UpdateUserRequest      → UpdateUserRequestValidator
CreateOrderRequest     → CreateOrderRequestValidator

각 API 계약마다 자체 Validator를 두면 다음과 같은 장점을 얻을 수 있습니다.

  • 사용 사례별 명확한 규칙
  • 쉬운 단위 테스트
  • 중복 제로
  • 깔끔한 컨트롤러
  • 확장 가능한 아키텍처

중복 없이 규칙 공유하기

Rule Extensions

public static class ValidationExtensions
{
    public static IRuleBuilderOptions ValidEmail(
        this IRuleBuilder rule)
    {
        return rule.NotEmpty().EmailAddress();
    }
}

사용 예시

RuleFor(x => x.Email).ValidEmail();

이렇게 하면 Validator를 깨끗하고 재사용 가능하게 유지하면서도 아키텍처를 해치지 않습니다.

마무리 생각

FluentValidation은 단순한 검증 도구가 아니라 디자인 결정입니다. 다음을 활용하면:

  • 요청 DTO
  • 요청당 하나의 Validator
  • 파이프라인 자동 검증

다음과 같은 API를 만들 수 있습니다.

  • 더 안전함
  • 더 깔끔함
  • 테스트 용이
  • 유지 보수 용이

이것이 실제 현업에서 사용되는 프로덕션 급 .NET 시스템의 모습입니다.

Back to Blog

관련 글

더 보기 »

CodeBehind 4.5 출시; 고급 비동기 기능

개요 Version 4.5는 .NET 7을 기반으로 구축된 CodeBehind 프레임워크의 최신 릴리스입니다. 곧 출시될 4.6 버전은 .NET 10을 목표로 하며 현재 개발 중입니다.