[TypeScript] 타입 기반 Validation 시스템 설계

Chan의 기술 블로그·2025년 11월 12일

TypeScript

목록 보기
10/10

이 글은 Chat GPT로 TypeScript를 공부하며 정리한 글이다.

Form 필드 타입에서 자동으로 검증 로직 생성하기

배경

Form을 만들 때 다음 두 가지를 항상 반복한다.

  • 필드 구조 정의 (예: FormFields 타입)
  • 검증 스키마 정의 (예: zod, yup, ajv 등)

하지만 타입스크립트를 쓰는 이상,
이미 Form 타입이 있는데 왜 또 검증 스키마를 수동으로 만들어야 할까?

이번 글에서는 Form 타입에서 Validation 규칙을 자동으로 유도하는 시스템을 만든다.

즉, “타입 = 데이터 구조 + 제약 조건”
으로 확장하는 패턴을 다룬다.

Form 타입 예시

이전 글의 FormMapper에서 이미 이런 타입을 얻을 수 있었다.

type UserForm = {
  'user.profile.name': string;
  'user.profile.email': string;
  'user.profile.age': number;
};

이제 이 타입을 기반으로 자동 검증 스키마를 생성한다.

타입 기반 Validation 매퍼 설계

타입 기반 Validation 매퍼 설계

type ValidationRule<T> =
  T extends string
    ? { required: boolean; maxLength?: number; pattern?: RegExp }
    : T extends number
    ? { required: boolean; min?: number; max?: number }
    : T extends boolean
    ? { required: boolean }
    : never;

이제 각 필드 타입을 돌며 규칙을 자동으로 만든다.

type ValidationMap<T> = {
  [K in keyof T]: ValidationRule<T[K]>;
};

자동 생성된 Validation 타입 예시

type UserValidation = ValidationMap<UserForm>;
/*
{
  "user.profile.name": { required: boolean; maxLength?: number; pattern?: RegExp };
  "user.profile.email": { required: boolean; maxLength?: number; pattern?: RegExp };
  "user.profile.age": { required: boolean; min?: number; max?: number };
}
*/

✅ 결과적으로, 각 필드에 자동으로 규칙 슬롯이 생긴다.

런타임 매퍼 — 타입에서 실제 검증 스키마로

런타임에서도 활용할 수 있도록 createValidator를 만든다.

function createValidator<T extends Record<string, any>>(
  rules: ValidationMap<T>
) {
  return (values: T) => {
    const errors: Partial<Record<keyof T, string>> = {};

    for (const key in values) {
      const value = values[key];
      const rule = rules[key];

      if (rule.required && (value === null || value === undefined || value === "")) {
        errors[key] = "필수 항목입니다.";
        continue;
      }

      if (typeof value === "string") {
        if (rule.maxLength && value.length > rule.maxLength)
          errors[key] = `최대 ${rule.maxLength}자 이하로 입력하세요.`;
        if (rule.pattern && !rule.pattern.test(value))
          errors[key] = `형식이 올바르지 않습니다.`;
      }

      if (typeof value === "number") {
        if (rule.min !== undefined && value < rule.min)
          errors[key] = `${rule.min} 이상이어야 합니다.`;
        if (rule.max !== undefined && value > rule.max)
          errors[key] = `${rule.max} 이하이어야 합니다.`;
      }
    }

    return errors;
  };
}

사용 예시 — 타입 안전한 검증

const rules: ValidationMap<UserForm> = {
  "user.profile.name": { required: true, maxLength: 10 },
  "user.profile.email": { required: true, pattern: /^[^@]+@[^@]+\.[^@]+$/ },
  "user.profile.age": { required: true, min: 1, max: 120 },
};

const validate = createValidator(rules);

const result = validate({
  "user.profile.name": "홍길동",
  "user.profile.email": "test@example.com",
  "user.profile.age": 25,
});

console.log(result); // ✅ {}

✅ rules의 타입이 UserForm 기반으로 자동 검증되어
필드 이름이나 타입이 틀리면 컴파일 타임에 에러가 발생한다.
(예: "user.profile.ag" → ❌)

실무 확장 아이디어

기능설명
zod 스키마 자동 변환ValidationMap<T>을 바탕으로 z.object() 자동 생성
서버 DTO와 동기화백엔드 응답 모델과 동일한 타입에서 검증 규칙 파생
FormBuilder 통합폼 UI, 검증, 제출 로직까지 타입 기반 자동화
다국어 메시지 매핑keyof T를 기반으로 i18n 키 자동 생성

마무리 요약

개념설명
ValidationRule타입에 따라 다른 검증 규칙 생성
ValidationMap모든 필드의 검증 규칙 집합
createValidator런타임 검증 로직 생성
효과타입과 검증 스키마의 완전한 동기화

💡 한 문장 요약
“타입이 곧 검증 규칙이다.”
— Form 구조, Validation, Submission 모두 타입에서 출발한다.

profile
퍼블리셔에서 프론트앤드로 전향하기

0개의 댓글