Overview
폼 유효성 검사는 사용자 입력의 정확성과 데이터의 무결성을 보장하기 위한 필수적인 과정입니다. 하지만 복잡한 폼일수록 이 검사를 효율적으로 처리하기 어렵습니다. React Hook Form은 React에서 폼을 쉽게 관리할 수 있도록 돕는 라이브러리로, 간단한 API와 뛰어난 성능을 제공합니다. 하지만 폼 검증을 더욱 최적화하려면, Yup이나 Zod와 같은 유효성 검증 라이브러리와 결합하는 것이 필요합니다.
이 글에서는 Yup이 무엇인지, 왜 사용하는지, 그리고 Zod와 비교했을 때 어떤 차이점이 있는지 살펴보겠습니다. 또한, React Hook Form과 함께 Yup을 사용하여 폼 검증을 구현하는 방법을 예시를 통해 보여드리겠습니다.
Yup이란 무엇이며 왜 사용하는가?
Yup은 자바스크립트 객체의 스키마를 정의하고 유효성을 검증할 수 있는 라이브러리입니다. 복잡한 유효성 검증 로직을 간단하게 구현할 수 있도록 개발자 친화적인 API를 제공합니다.
Yup의 주요 장점은 다음과 같습니다:
- 유연성 : 다양한 데이터 타입과 유효성 검사 규칙을 지원합니다. 문자열, 숫자, 배열 등 여러 타입의 유효성 검사를 설정할 수 있습니다.
- 체이닝 : 메서드 체이닝을 지원하여, 여러 유효성 검증 규칙을 간편하게 연결할 수 있습니다. 이를 통해 읽기 쉬운 코드를 작성할 수 있습니다.
- 비동기 유효성 검사 : 비동기 유효성 검사를 지원합니다.
사용 방법
React Hook Form과 Yup을 함께 사용하는 방법을 예시를 통해 살펴보겠습니다. 먼저, 프로젝트에 Yup과 React Hook Form을 설치해야 합니다:
npm install @hookform/resolvers yup react-hook-form
이제 Yup을 사용하여 폼 스키마를 정의하고, React Hook Form과 결합해 이메일과 비밀번호 입력 폼을 구현해 보겠습니다:
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import * as yup from 'yup';
const validationSchema = yup.object({
email: yup.string().email('올바른 이메일 형식이 아닙니다').required('이메일을 입력해주세요'),
password: yup.string().min(6, '비밀번호는 최소 6자 이상이어야 합니다').required('비밀번호를 입력해주세요'),
});
const MyForm = () => {
const { register, handleSubmit, formState: { errors } } = useForm({
resolver: yupResolver(validationSchema),
});
const onSubmit = (data) => {
console.log(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div>
<label>이메일</label>
<input {...register('email')} />
{errors.email && <p>{errors.email.message}</p>}
</div>
<div>
<label>비밀번호</label>
<input {...register('password')} />
{errors.password && <p>{errors.password.message}</p>}
</div>
<button type="submit">제출</button>
</form>
);
};
위 코드에서는 yupResolver를 사용하여 Yup 스키마를 React Hook Form에 통합했습니다. 폼 필드가 Yup 스키마에 정의된 유효성 검사를 따르게 되고, 에러 메시지도 설정한 대로 표시됩니다.
validate prop을 사용하는 것과 Yup을 사용했을 때의 차이점?
React Hook Form에서는 기본적으로 Controller 컴포넌트를 사용하여 폼 필드를 제어합니다. 이때 validate prop을 통해 개별 필드의 유효성을 직접 설정할 수 있지만, 폼이 복잡해지면 각 필드마다 개별 검증 규칙을 설정하는 것이 비효율적일 수 있습니다.
다음은 validate prop을 사용한 간단한 예시입니다:
<Controller
name="email"
control={control}
rules={{
validate: (value) => value.includes('@') || '올바른 이메일 형식이 아닙니다',
}}
render={({ field }) => <input {...field} />}
/>
<Controller
name="password"
control={control}
rules={{
validate: (value) => value.length >= 8 || '비밀번호는 최소 8자리 이상이어야 합니다.',
}}
render={({ field }) => <input {...field} />}
/>
위 코드는 간단한 유효성 검사를 설정한 예시입니다. 하지만, 각 필드마다 검증 규칙을 직접 작성해야 하므로 관리가 어려워질 수 있습니다.
다음은 Yup을 사용한 간단한 예시입니다:
const validationSchema = yup.object({
email: yup.string()
.email('올바른 이메일 형식이 아닙니다')
.required('이메일을 입력해주세요'),
password: yup.string()
.min(8, '비밀번호는 최소 8자리 이상이어야 합니다.')
.required('비밀번호를 입력해주세요.'),
});
Yup을 사용하면 스키마 객체를 통해 폼의 유효성 검사를 한 곳에서 정의할 수 있으며, React Hook Form과 결합하여 훨씬 더 효율적이고 재사용 가능한 코드를 작성할 수 있습니다.
조금 더 복잡한 조건일 때를 보시죠!
아래 동일한 유효성 조건을 react hook form만 사용했을 때와 yup사용했을 때를 비교해 보겠습니다.
- 출발 시간은 오늘 이후여야 한다.
- 출발 시간은 도착 시간보다 전이어야 한다.
- 도착 시간은 출발 시간 이후여야 한다.
- 도착 시간은 3개월 이후를 선택할 수 없다.
다음은 validate prop을 사용한 간단한 예시입니다:
...
const today = dayjs().startOf('day');
const maxDate = dayjs().add(3, 'month').endOf('day');
...
<label>출발 시간:</label>
<Controller
name="departureTime"
control={control}
rules={{
required: '출발 시간을 입력해주세요.',
validate: {
afterToday: value =>
dayjs(value).isAfter(today) || '출발 시간은 오늘 이후여야 합니다.',
beforeArrival: value =>
!departureTime || dayjs(value).isBefore(watch('arrivalTime')) || '출발 시간은 도착 시간보다 전이어야 합니다.'
},
}}
render={({ field }) => (
<input type="datetime-local" {...field} />
)}
/>
<label>도착 시간:</label>
<Controller
name="arrivalTime"
control={control}
rules={{
required: '도착 시간을 입력해주세요.',
validate: {
afterDeparture: value =>
dayjs(value).isAfter(departureTime) || '도착 시간은 출발 시간 이후여야 합니다.',
withinThreeMonths: value =>
dayjs(value).isBefore(maxDate) || '도착 시간은 3개월 이내여야 합니다.',
},
}}
render={({ field }) => (
<input type="datetime-local" {...field} />
)}
/>
다음은 Yup을 사용한 간단한 예시입니다:
// 오늘 날짜와 3개월 이후 날짜
const today = dayjs().startOf('day');
const maxDate = dayjs().add(3, 'month').endOf('day');
// yup 스키마 설정
const schema = Yup.object().shape({
departureTime: Yup.date()
.required('출발 시간을 입력해주세요.')
.min(today.toDate(), '출발 시간은 오늘 이후여야 합니다.')
.max(Yup.ref('arrivalTime'), '출발 시간은 도착 시간보다 전이어야 합니다.'),
arrivalTime: Yup.date()
.required('도착 시간을 입력해주세요.')
.min(Yup.ref('departureTime'), '도착 시간은 출발 시간 이후여야 합니다.')
.max(maxDate.toDate(), '도착 시간은 3개월 이내여야 합니다.'),
});
...
const { ... } = useForm({
resolver: yupResolver(schema),
});
유효성 검사를 Controller에 작성했을 때는 코드도 복잡하고, 코드도 흩어져 있기 때문에 조건을 확인하기 어렵습니다. 하지만, Yup을 사용하면 스키마 객체를 통해 폼의 유효성 검사를 한 곳에서 정의할 수 있으며, React Hook Form과 결합하여 훨씬 더 효율적이고 재사용 가능한 코드를 작성할 수 있습니다.
Zod와 비교
Yup과 비슷한 역할을 하는 또 다른 라이브러리로 Zod가 있습니다. 두 라이브러리는 모두 폼 유효성 검사를 위한 스키마 기반 검증 도구이지만, 몇 가지 차이점이 있습니다.
특징 | Yup | Zod |
문법 | 선언적이고 직관적인 문법 | 함수형 프로그래밍 스타일에 가까운 문법 |
TypeScript 지원 | 기본적으로 타입 지원은 가능하지만, 타입 추론에 한계가 있음 | TypeScript와의 통합이 좋음 |
학습 곡선(러닝 커브) | 처음 접하는 개발자도 쉽게 사용할 수 있음 | 함수형 스타일로 인해 다소 높은 진입장벽 |
마치면서
회사 프로젝트를 진행하면서 Yup을 처음 접하고, 이를 추천하는 글을 작성하려 했습니다. 그런데 공부를 하다 보니 Zod라는 강력한 대안을 알게 되었고, 비교해 보면 Zod가 더 나은 선택처럼 느껴졌습니다. 😅
그렇지만, Yup 은 여전히 직관적인 문법과 다양한 검증 규칙을 제공하여 복잡한 폼 검증을 손쉽게 구현할 수 있는 강력한 도구입니다. 특히 React Hook Form을 처음 접하는 초보자나 간단한 프로젝트에서는 Yup이 더 적합할 수 있습니다. Yup에 익숙해진 후 필요에 따라 Zod로 넘어가는 것도 좋은 선택이 될 것 같습니다!
참고자료
'Frontend > React Hook Form' 카테고리의 다른 글
React Hook Form의 Controller와 Register 함수: 활용 방법 및 비교 (0) | 2023.12.24 |
---|---|
React Hook Form의 useWatch와 watch 함수, 제대로 알고 쓰자! (0) | 2023.09.24 |