본문으로 건너뛰기 Vuelidate 완벽 가이드 | Vue 폼 검증·Validation·TypeScript·실전 활용

Vuelidate 완벽 가이드 | Vue 폼 검증·Validation·TypeScript·실전 활용

Vuelidate 완벽 가이드 | Vue 폼 검증·Validation·TypeScript·실전 활용

이 글의 핵심

Vuelidate 완벽 가이드에 대해 정리한 개발 블로그 글입니다. Vuelidate로 Vue 폼 검증을 구현하는 완벽 가이드입니다. Built-in Validators, Custom Validators, Async Validation까지 실전 예제로 정리했습니다. > 실무 경험 공유:… 개념과 예제 코드를 단계적으로 다루며, 실무·학습에 참고할 수 있도록 구성했습니다. 관련 키워드:…

이 글의 핵심

Vuelidate로 Vue 폼 검증을 구현하는 완벽 가이드입니다. Built-in Validators, Custom Validators, Async Validation까지 실전 예제로 정리했습니다.

실무 경험 공유: 수동 검증 로직을 Vuelidate로 전환하면서, 코드가 60% 감소하고 검증 로직이 재사용 가능해진 경험을 공유합니다.

들어가며: “폼 검증이 복잡해요”

실무에서 마주치는 문제들

검증 로직이 중복돼요

각 컴포넌트마다 작성합니다. Vuelidate는 재사용 가능합니다. 에러 메시지가 일관적이지 않아요

수동 관리가 어렵습니다. Vuelidate는 통일된 에러 처리를 제공합니다. 비동기 검증이 필요해요

직접 구현이 복잡합니다. Vuelidate는 Async Validation을 지원합니다.

1. Vuelidate란?

핵심 특징

Vuelidate는 Vue 폼 검증 라이브러리입니다. 주요 장점

  • Model 기반: 반응형 검증
  • Composition API: Vue 3 친화적
  • TypeScript: 완벽한 지원
  • Async Validation: 비동기 검증
  • 커스터마이징: 자유로운 확장

2. 설치 및 기본 사용

설치

npm install @vuelidate/core @vuelidate/validators

기본 검증

<script setup lang="ts">
import { reactive, computed } from 'vue';
import { useVuelidate } from '@vuelidate/core';
import { required, email, minLength } from '@vuelidate/validators';
const state = reactive({
  email: ',
  password: ',
});
const rules = computed(() => ({
  email: { required, email },
  password: { required, minLength: minLength(8) },
}));
const v$ = useVuelidate(rules, state);
const handleSubmit = async () => {
  const isValid = await v$.value.$validate();
  if (!isValid) {
    return;
  }
  console.log('Valid data:', state);
};
</script>
<template>
  <form @submit.prevent="handleSubmit">
    <input v-model="state.email" />
    <span v-if="v$.email.$error">
      {{ v$.email.$errors[0].$message }}
    </span>
    <input v-model="state.password" type="password" />
    <span v-if="v$.password.$error">
      {{ v$.password.$errors[0].$message }}
    </span>
    <button type="submit">Submit</button>
  </form>
</template>

3. Built-in Validators

import {
  required,
  email,
  minLength,
  maxLength,
  minValue,
  maxValue,
  between,
  alpha,
  alphaNum,
  numeric,
  url,
  sameAs,
} from '@vuelidate/validators';
const rules = {
  email: { required, email },
  password: { required, minLength: minLength(8) },
  age: { required, between: between(18, 120) },
  username: { required, alphaNum, minLength: minLength(3) },
  website: { url },
  confirmPassword: { required, sameAs: sameAs(computed(() => state.password)) },
};

4. Custom Validators

import { helpers } from '@vuelidate/validators';
const strongPassword = helpers.withMessage(
  'Password must contain uppercase, lowercase, and number',
  (value: string) => {
    return /[A-Z]/.test(value) && /[a-z]/.test(value) && /[0-9]/.test(value);
  }
);
const rules = {
  password: { required, strongPassword },
};

5. Async Validation

import { helpers } from '@vuelidate/validators';
const uniqueEmail = helpers.withAsync(async (value: string) => {
  if (!value) return true;
  const response = await fetch(`/api/check-email?email=${value}`);
  const { exists } = await response.json();
  return !exists;
});
const rules = {
  email: {
    required,
    email,
    uniqueEmail: helpers.withMessage('Email already taken', uniqueEmail),
  },
};

6. 에러 메시지

커스텀 메시지

import { required, email } from '@vuelidate/validators';
import { helpers } from '@vuelidate/validators';
const rules = {
  email: {
    required: helpers.withMessage('이메일을 입력하세요', required),
    email: helpers.withMessage('유효한 이메일을 입력하세요', email),
  },
};

다국어

import { createI18n } from '@vuelidate/validators';
const { withI18nMessage } = createI18n({
  ko: {
    required: '{property}을(를) 입력하세요',
    email: '유효한 이메일을 입력하세요',
  },
});
const rules = {
  email: {
    required: withI18nMessage(required),
    email: withI18nMessage(email),
  },
};

7. Nested Objects

<script setup lang="ts">
import { reactive } from 'vue';
import { useVuelidate } from '@vuelidate/core';
import { required, email } from '@vuelidate/validators';
const state = reactive({
  user: {
    name: ',
    email: ',
  },
  address: {
    street: ',
    city: ',
  },
});
const rules = {
  user: {
    name: { required },
    email: { required, email },
  },
  address: {
    street: { required },
    city: { required },
  },
};
const v$ = useVuelidate(rules, state);
</script>
<template>
  <input v-model="state.user.name" />
  <span v-if="v$.user.name.$error">{{ v$.user.name.$errors[0].$message }}</span>
  <input v-model="state.user.email" />
  <span v-if="v$.user.email.$error">{{ v$.user.email.$errors[0].$message }}</span>
</template>

8. 실전 예제: 회원가입 폼

<script setup lang="ts">
import { reactive, computed } from 'vue';
import { useVuelidate } from '@vuelidate/core';
import { required, email, minLength, sameAs, helpers } from '@vuelidate/validators';
const state = reactive({
  email: ',
  password: ',
  confirmPassword: ',
  name: ',
  agreeToTerms: false,
});
const strongPassword = helpers.withMessage(
  'Password must contain uppercase, lowercase, and number',
  (value: string) => /[A-Z]/.test(value) && /[a-z]/.test(value) && /[0-9]/.test(value)
);
const rules = computed(() => ({
  email: { required, email },
  password: { required, minLength: minLength(8), strongPassword },
  confirmPassword: { required, sameAs: sameAs(state.password) },
  name: { required, minLength: minLength(2) },
  agreeToTerms: {
    checked: helpers.withMessage('You must agree to terms', (value: boolean) => value === true),
  },
}));
const v$ = useVuelidate(rules, state);
const handleSubmit = async () => {
  const isValid = await v$.value.$validate();
  if (!isValid) {
    return;
  }
  console.log('Submitting:', state);
};
</script>
<template>
  <form @submit.prevent="handleSubmit">
    <div>
      <input v-model="state.email" placeholder="Email" />
      <span v-if="v$.email.$error" class="error">
        {{ v$.email.$errors[0].$message }}
      </span>
    </div>
    <div>
      <input v-model="state.password" type="password" placeholder="Password" />
      <span v-if="v$.password.$error" class="error">
        {{ v$.password.$errors[0].$message }}
      </span>
    </div>
    <div>
      <input v-model="state.confirmPassword" type="password" placeholder="Confirm Password" />
      <span v-if="v$.confirmPassword.$error" class="error">
        {{ v$.confirmPassword.$errors[0].$message }}
      </span>
    </div>
    <div>
      <input v-model="state.name" placeholder="Name" />
      <span v-if="v$.name.$error" class="error">
        {{ v$.name.$errors[0].$message }}
      </span>
    </div>
    <div>
      <label>
        <input v-model="state.agreeToTerms" type="checkbox" />
        I agree to terms
      </label>
      <span v-if="v$.agreeToTerms.$error" class="error">
        {{ v$.agreeToTerms.$errors[0].$message }}
      </span>
    </div>
    <button type="submit" :disabled="v$.$invalid">Sign Up</button>
  </form>
</template>

정리 및 체크리스트

핵심 요약

  • Vuelidate: Vue 폼 검증
  • Model 기반: 반응형 검증
  • Composition API: Vue 3 친화적
  • Built-in Validators: 다양한 검증
  • Custom Validators: 자유로운 확장
  • Async Validation: 비동기 검증

구현 체크리스트

  • Vuelidate 설치
  • Rules 정의
  • 기본 검증 구현
  • Custom Validator 작성
  • Async Validation 구현
  • 에러 메시지 커스터마이징
  • 복잡한 폼 구현

같이 보면 좋은 글


이 글에서 다루는 키워드

Vuelidate, Vue 3, Validation, Form, TypeScript, Frontend, Composition API

자주 묻는 질문 (FAQ)

Q. Vue 2에서도 사용할 수 있나요?

A. 네, Vuelidate 2는 Vue 2와 Vue 3 모두 지원합니다.

Q. Zod와 함께 사용할 수 있나요?

A. 네, Zod 스키마를 Vuelidate Validator로 변환할 수 있습니다.

Q. 성능은 어떤가요?

A. 좋습니다. 반응형 시스템을 활용해 효율적입니다.

Q. 프로덕션에서 사용해도 되나요?

A. 네, 많은 Vue 프로젝트에서 안정적으로 사용하고 있습니다.