[Vue3, TypeScript] vee-validate와 yup에 대한 예제 총정리
vee-validate
와 yup
은 둘 다 웹 폼의 유효성 검사(validation) 를 도와주는 라이브러리지만, 역할과 사용 방식이 다릅니다.
둘의 차이점과 각각의 목적을 아래에 정리해드릴게요.
ee-validate
란?
Vue.js 전용 폼 유효성 검사 라이브러리
vee-validate
는 Vue.js (특히 Composition API) 프로젝트에서 폼 상태 관리 + 유효성 검사를 간편하게 할 수 있게 해주는 라이브러리입니다.
주요 기능
기능 | 설명 |
---|---|
useForm() | 폼 전체의 상태와 동작을 관리 |
Field 컴포넌트 | 입력 필드 바인딩 + 유효성 검사 연결(개별 폼 필드 관리) |
Form 컴포넌트 | 폼 제출 처리 |
ErrorMessage 컴포넌트 | 필드의 에러 메시지 출력 |
yup , zod 등의 스키마와 연동 가능 | 스키마 기반 유효성 검사 사용 가능 |
사용 기술
- Vue 3 Composition API에 최적화
- 선언적이고 타입 안전한 유효성 검사
useForm
in vee-validate
import { useForm, Field } from 'vee-validate';
실제 사용 예시(템플릿)
<template>
<form @submit="onSubmit">
<Field name="customerNm" as="input" />
<Field name="contractNo" as="input" />
<button type="submit">제출</button>
<button type="button" @click="resetForm">초기화</button>
</form>
</template>
<script lang="ts" setup>
import { useForm, Field } from 'vee-validate';
const { handleSubmit, resetForm } = useForm({
initialValues: {
contractNo: '',
customerNm: '',
},
});
const onSubmit = handleSubmit((values) => {
console.log('제출된 값:', values);
});
</script>
타입스크립트 연동 팁
interface FormValues {
contractNo: string;
customerNm: string;
// ...생략
}
const { handleSubmit } = useForm<FormValues>({
initialValues: {
contractNo: '',
customerNm: '',
// ...
}
});
yup
이란?
자바스크립트/타입스크립트용 유효성 검사 라이브러리
yup
은 독립적인 스키마 기반 유효성 검사 도구입니다.
객체의 구조를 정의하고, 그 구조에 맞는 유효성 조건을 선언적으로 작성할 수 있습니다.
주요 기능
기능 | 설명 |
---|---|
객체 스키마 정의 | .object({ ... }) 로 객체 구조 선언 |
필드 유효성 검사 | .required() , .min() , .matches() 등 |
커스텀 메시지 | 각 조건에 맞는 에러 메시지 지정 가능 |
비동기 유효성 검사 | .test() 를 통해 async 유효성 검사도 가능 |
타입 추론 | yup.InferType<typeof schema> 로 타입 추론 가능 (하지만 제한적) |
관계: 왜 둘을 같이 쓰는가?
vee-validate
는 폼 입력과 상태를 관리하고,yup
은 유효성 검사만을 담당합니다.
이 둘은 아래처럼 연결됩니다:
const schema = yup.object({
name: yup.string().required('이름은 필수입니다.'),
});
useForm({
validationSchema: schema
});
→ vee-validate
가 yup
스키마를 기반으로 유효성 검사를 자동으로 처리해줍니다.
둘의 차이 요약
항목 | vee-validate | yup |
---|---|---|
목적 | Vue 컴포넌트에서 폼 상태 및 유효성 관리 | 유효성 로직만 담당 |
종속성 | Vue.js 필요 | Vue, React 등과 무관 (독립적) |
사용 방식 | <Form> , <Field> , useForm() 등 제공 | schema.validate() 형태로 사용 |
주 사용처 | Vue 프로젝트 전용 | 다양한 JS 프로젝트 |
조합 | yup , zod , joi 등과 연동 | 단독 사용 가능 |
실제 예시 (함께 쓰는 모습)
// yup 스키마 정의
const schema = yup.object({
email: yup.string().email().required(),
password: yup.string().min(6).required()
});
// vee-validate로 연결
const { handleSubmit } = useForm({
validationSchema: schema
});
const onSubmit = handleSubmit(values => {
console.log('제출 성공:', values);
});
useForm
에 validationSchema
추가
import { useForm, Field, Form } from 'vee-validate';
import * as yup from 'yup';
const schema = yup.object({
customerNm: yup.string().required('고객명은 필수입니다.'),
contractNo: yup.string().length(10, '계약번호는 10자여야 합니다.'),
customerHp: yup
.string()
.matches(/^\d{3}-\d{3,4}-\d{4}$/, '올바른 전화번호 형식이 아닙니다.'),
});
const { handleSubmit, resetForm } = useForm({
validationSchema: schema,
});
템플릿 예시 (Vue 3 <script setup>
)
<template>
<Form @submit="onSubmit">
<div>
<label>고객명</label>
<Field name="customerNm" as="input" />
<ErrorMessage name="customerNm" />
</div>
<div>
<label>계약번호</label>
<Field name="contractNo" as="input" />
<ErrorMessage name="contractNo" />
</div>
<div>
<label>전화번호</label>
<Field name="customerHp" as="input" />
<ErrorMessage name="customerHp" />
</div>
<button type="submit">제출</button>
</Form>
</template>
<script lang="ts" setup>
import { useForm, Field, Form, ErrorMessage } from 'vee-validate';
import * as yup from 'yup';
const schema = yup.object({
customerNm: yup.string().required('고객명은 필수입니다.'),
contractNo: yup.string().length(10, '계약번호는 10자여야 합니다.'),
customerHp: yup
.string()
.matches(/^\d{3}-\d{3,4}-\d{4}$/, '올바른 전화번호 형식이 아닙니다.'),
});
const { handleSubmit, resetForm } = useForm({
validationSchema: schema,
});
const onSubmit = handleSubmit((values) => {
console.log('제출 성공:', values);
});
</script>
어떤 걸 선택하면 좋을까?
상황 | 추천 |
---|---|
간단하고 직관적인 유효성 검사 | yup |
TypeScript 기반 타입 추론 최우선 | zod + @vee-validate/zod |
복잡한 조건 분기나 커스텀 타입 검사 | zod |
vee-validate에서 커스텀 유효성 검사 작성법
vee-validate
에서 커스텀 유효성 검사(Custom Validation) 를 작성하는 방법은 크게 두 가지가 있습니다
1. useField
/ Field
에서 직접 커스텀 검사 함수 정의
가장 직관적이고 간단한 방법입니다.
📌 예시: 이름이 반드시 “홍길동”이어야 하는 필드
<template>
<Form @submit="onSubmit">
<div>
<label>이름</label>
<Field
name="name"
as="input"
:rules="isHongGildong"
/>
<ErrorMessage name="name" />
</div>
<button type="submit">제출</button>
</Form>
</template>
<script lang="ts" setup>
import { Field, Form, ErrorMessage, useForm } from 'vee-validate';
const isHongGildong = (value: string) => {
if (value !== '홍길동') {
return '이름은 반드시 홍길동이어야 합니다.';
}
return true;
};
const { handleSubmit } = useForm();
const onSubmit = handleSubmit(values => {
console.log('성공적으로 제출됨:', values);
});
</script>
2. 글로벌 커스텀 유효성 규칙 등록 (defineRule
사용)
이 방법은 재사용 가능한 커스텀 규칙을 만드는 데 적합합니다.
📌 예시: 전화번호가 010-
으로 시작해야 하는 규칙
// main.ts 또는 setup 파일에 작성
import { defineRule } from 'vee-validate';
defineRule('startsWith010', (value: string) => {
if (!value.startsWith('010-')) {
return '전화번호는 010-으로 시작해야 합니다.';
}
return true;
});
📌 템플릿에서 사용
<Field name="phone" as="input" rules="required|startsWith010" />
<ErrorMessage name="phone" />
✅ 여러 개의 기본 rule (
required
,
3. yup
또는 zod
를 사용하는 경우의 커스텀 검사
📌 yup 예시 – 조건부 검사
import * as yup from 'yup';
const schema = yup.object({
email: yup
.string()
.email('이메일 형식이 아닙니다.')
.test('is-gmail', 'gmail만 허용됩니다.', (value) => {
return value?.includes('@gmail.com');
}),
});
정리 – 선택 기준
방식 | 사용 상황 | 장점 | 예시 |
---|---|---|---|
Field 에서 함수 전달 | 단순한 1회성 검사 | 간단하고 빠름 | :rules="value => value === '홍길동'" |
defineRule | 여러 곳에서 반복 사용 | 재사용 가능 | 전화번호, 사용자 정의 포맷 |
yup.test() / zod.refine() | 스키마 기반 검사 사용 시 | 스키마 일관성 유지 | 조건부 검사, 커스텀 메시지 |
✨ 참고: async 커스텀 유효성 검사 (예: 서버 중복 체크)
defineRule('uniqueUsername', async (value: string) => {
const res = await fetch(`/api/check-username?name=${value}`);
const { isTaken } = await res.json();
return isTaken ? '이미 사용 중인 아이디입니다.' : true;
});
비동기 커스텀 검사는 버튼 디스에이블 처리 등과 함께 고려하면 UX가 좋아집니다.
4. 커스텀 rule에 파라미터 전달하기
🔧 defineRule
에서 매개변수 받기
vee-validate
는 defineRule
함수에서 두 번째 인자로 params
를 넘겨줍니다.
📌 예시: minLength
커스텀 룰
import { defineRule } from 'vee-validate';
defineRule('minLength', (value: string, [limit]: [number]) => {
if (!value || value.length < limit) {
return `최소 ${limit}자 이상 입력해야 합니다.`;
}
return true;
});
📌 템플릿에서 사용
<Field name="username" rules="required|minLength:5" />
<ErrorMessage name="username" />
⚠️ 파라미터는 문자열로 전달되므로 내부에서
Number()
로 변환하는 게 안전할 수 있어요.
5. 커스텀 컴포넌트 안에서 useField()
로 유효성 검사 적용
✨목표: MyInput.vue
같은 사용자 정의 <input>
을 vee-validate
에 연결
📦 MyInput.vue
<template>
<div>
<label>{{ label }}</label>
<input v-bind="field" v-model="value" :type="type" />
<span v-if="errorMessage" style="color: red;">{{ errorMessage }}</span>
</div>
</template>
<script lang="ts" setup>
import { useField } from 'vee-validate';
import { computed, defineProps } from 'vue';
const props = defineProps<{
name: string;
label: string;
rules?: any;
type?: string;
}>();
const { value, errorMessage, field } = useField(props.name, props.rules);
</script>
📦 상위 컴포넌트 사용 예시
<template>
<Form @submit="onSubmit">
<MyInput name="email" label="이메일" rules="required|email" />
<MyInput name="nickname" label="닉네임" rules="required|minLength:3" />
<button type="submit">제출</button>
</Form>
</template>
<script setup lang="ts">
import { Form, useForm } from 'vee-validate';
import MyInput from './MyInput.vue';
const { handleSubmit } = useForm();
const onSubmit = handleSubmit((values) => {
console.log('성공:', values);
});
</script>
정리 요약
기능 | 설명 | 예시 |
---|---|---|
defineRule + 파라미터 | 커스텀 rule에 :5 같은 값 전달 | minLength:5 |
useField() | 커스텀 input 컴포넌트에서 vee-validate와 연결 | value , errorMessage , field 반환 |
커스텀 컴포넌트 사용 | 폼 내에서 Field 대신 커스텀 UI 사용 가능 | <MyInput name="..." /> |
✨ 보너스: 커스텀 컴포넌트에서 emit('update:modelValue')
로 통신하려면?
<template>
<input :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" />
</template>
<script setup lang="ts">
defineProps<{ modelValue: string }>();
defineEmits(['update:modelValue']);
</script>
➡ vee-validate
는 <Field as="MyInput" />
로도 연결할 수 있습니다.
요약 한 줄 정리
vee-validate
: Vue 3에서 폼 상태 및 유효성 관리를 담당하는 Vue 전용 라이브러리yup
: JavaScript/TypeScript에서 스키마 기반 유효성 검사를 담당하는 범용 라이브러리- 함께 사용하면: Vue 프로젝트에서 타입 안전한, 깔끔한 폼 유효성 검사가 가능해짐
[연관자료]
[Vue3, TypeScript] vee-validate + pinia 연동 방법 예제 총정리
[Vue3, TypeScript] vee-validate에서 입력 필드 마스킹/포맷팅방법