[로봇활용_12주차] C# 간단한 Validation 프레임워크 만들기

최윤호·2025년 11월 1일
post-thumbnail

나만의 프레임워크 만들기

C# 애트리뷰트와 리플렉션의 세계를 탐험하는 마지막 여정입니다.
이번에는 우리가 직접 우리만의 규칙을 담은 애트리뷰트를 만들고, 리플렉션을 이용해
이 규칙을 검사하는 유효성 검사(Validation) 프레임워크를 흉내 내보겠습니다.

1단계: 우리가 만들 기능 구상

우리가 만들 최종 목표는 다음과 같습니다.

  • 문자열의 최대 길이를 제한하는 [StringLength]애트리뷰트를 만든다.
  • 숫자의 최소, 최대 범위를 제한하는 [Range]애트리뷰트를 만든다.
  • 이 애트리뷰트들이 적용된 객체를 받아, 유효성을 검사하고
    결과를 알려주는 Validator클래스를 만든다.

2단계: 애트리뷰트 클래스 정의

먼저, 우리만의 규칙을 담을 애트리뷰트 클래스들을 정의해 보겠습니다.
애트리뷰트는 System.Attribute를 상속받는 클래스라는 점, 기억하시죠?

using System;
using System.Reflection;

// 이 애트리뷰트는 프로퍼티에만 적용할 수 있도록 제한합니다.
[AttributeUsage(AttributeTargets.Property)]
public class StringLengthAttribute : Attribute
{
    public int MaxLength { get; }

    public StringLengthAttribute(int maxLength)
    {
        MaxLength = maxLength;
    }
}

// 이 애트리뷰트도 프로퍼티에만 적용합니다.
[AttributeUsage(AttributeTargets.Property)]
public class RangeAttribute : Attribute
{
    public int Min { get; }
    public int Max { get; }

    public RangeAttribute(int min, int max)
    {
        Min = min;
        Max = max;
    }
}

이제 우리의 규칙을 담은 '꼬리표'들이 준비되었습니다. 간단하죠?
생성자를 통해 규칙(최대 길이, 최소/최대 범위)을 받고,
나중에 검사기가 읽어갈 수 있도록 공개 속성(Property)으로 값을 저장해 둡니다.

3단계: 애트리뷰트 적용

이제 이 꼬리표들을 유효성 검사를 할 모델 클래스에 붙여봅시다.
회원가입 폼을 나타내는 RegisterForm클래스를 예로 들어볼게요.

public class RegisterForm
{
    [StringLength(10)]
    public string Username { get; set; }

    [StringLength(20)]
    public string Password { get; set; }

    [Range(1, 150)]
    public int Age { get; set; }
}

코드가 정말 깔끔하고 선언적으로 바뀌었습니다!
Username은 10자를 넘으면 안 되고, Age는 1에서 150 사이여야 한다는
규칙이 코드만 봐도 명확하게 보입니다. 이것이 바로 애트리뷰트의 힘이죠.

4단계: 검증기(Validator) 구현

꼬리표를 붙이는 것만으로는 아무 일도 일어나지 않습니다.
이 꼬리표를 읽고 실제로 규칙을 검사하는 '리플렉션'이 필요합니다.

public static class Validator
{
    public static bool Validate(object obj)
    {
        // 1. 객체의 Type 정보를 가져옵니다.
        Type type = obj.GetType();

        // 2. 해당 Type의 모든 프로퍼티를 순회합니다.
        foreach (PropertyInfo property in type.GetProperties())
        {
            // 3. 각 프로퍼티에 적용된 모든 애트리뷰트를 가져옵니다.
            foreach (Attribute attribute in property.GetCustomAttributes())
            {
                // 4. 애트리뷰트 타입에 따라 분기하여 유효성을 검사합니다.
                if (attribute is StringLengthAttribute stringLengthAttr)
                {
                    string value = (string)property.GetValue(obj);
                    if (value.Length > stringLengthAttr.MaxLength)
                    {
                        Console.WriteLine(
                            $"[유효성 오류] " +
                            $"{property.Name}의 길이가 너무 깁니다. " +
                            $"(최대: {stringLengthAttr.MaxLength})");
                        return false;
                    }
                }
                else if (attribute is RangeAttribute rangeAttr)
                {
                    int value = (int)property.GetValue(obj);
                    if (value < rangeAttr.Min || value > rangeAttr.Max)
                    {
                        Console.WriteLine(
                            $"[유효성 오류] " +
                            $"{property.Name}의 값이 범위를 벗어났습니다. " +
                            $"(범위: {rangeAttr.Min}~{rangeAttr.Max})");
                        return false;
                    }
                }
            }
        }

        Console.WriteLine("[유효성 검사] 모든 항목이 유효합니다.");
        return true;
    }
}

5단계: Main 메소드 작성

이제 Main메소드에서 유효한 경우와 유효하지 않은 경우를 모두 테스트해 봅시다.

class Program
{
    static void Main()
    {
        // 경우 1: 유효한 데이터
        var validForm = new RegisterForm
        {
            Username = "guest",
            Password = "password123",
            Age = 30
        };
        Console.WriteLine("--- 유효한 폼 검사 시작 ---");
        Validator.Validate(validForm);

        Console.WriteLine("\n");

        // 경우 2: 유효하지 않은 데이터 (사용자 이름이 너무 김)
        var invalidForm = new RegisterForm
        {
            Username = "this_username_is_too_long",
            Password = "password123",
            Age = 99
        };
        Console.WriteLine("--- 유효하지 않은 폼 검사 시작 ---");
        Validator.Validate(invalidForm);
    }
}

[실행 결과]

--- 유효한 폼 검사 시작 ---
[유효성 검사] 모든 항목이 유효합니다.


--- 유효하지 않은 폼 검사 시작 ---
[유효성 오류] Username의 길이가 너무 깁니다. (최대: 10)

우리가 직접 만든 애트리뷰트와 검증기가 완벽하게 동작하는 것을 확인했습니다.

마무리하며

이 원리를 이해한다면, 단순히 프레임워크를 사용하는 것을 넘어,
프레임워크가 어떻게 동작하는지 그 내부를 이해하게 된 것입니다.
이제 여러분은 프로젝트에 필요한 반복적인 로직을 자동화하고,
더 깔끔하고 유연한 코드를 작성할 수 있는 강력한 무기를 손에 넣으신 겁니다.
리플렉션과 애트리뷰트를 활용해서 여러분의 코드를 한 단계 더 발전시켜 보시길 바랍니다.

profile
🚀 미래의 엔지니어를 꿈꾸는 훈련생의 기록 📝

0개의 댓글