회사에서 react-hook-form을 쓰는데 zod로 유효성검사를 하는데 할 때마다 헷갈렸던 부분 한번 정리해보려고 한다.
React hook form
React Hook Form은 React에서 폼을 관리하기 위한 경량 폼 라이브러리이다. 최소한의 리렌더링과 간결한 API를 통해 폼 상태를 효율적으로 관리할 수 있다.
주요 장점:
- 간단한 API: useForm 훅을 사용하여 폼 상태와 검증 로직을 선언적으로 관리할 수 있음.
- 퍼포먼스 최적화: 입력 필드별로 리렌더링을 최소화하여 성능을 높임.
- 유연한 검증: 사용자 정의 검증 로직 및 다양한 검증 라이브러리와 통합 가능.
React Hook Form(RHF)의 다양한 기능들
1. resolver
역할: 외부 검증 라이브러리(Zod, Yup 등)를 React Hook Form과 통합하는 기능.
쉽게 설명하면 검증로직을 외부에서 가져와서 자동으로 처리해주는 것
import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod';
const schema = z.object({
email: z.string().email('유효한 이메일을 입력하세요.'),
});
const { register, handleSubmit, formState } = useForm({
resolver: zodResolver(schema), // 외부 라이브러리로 검증
});
2. register
역할: 폼 필드(input, select 등)를 React Hook Form에 연결.
HTML 요소에 register를 사용하면 해당 필드의 상태를 자동으로 관리
<input {...register('email')} />
3. watch
역할: 특정 입력 필드나 전체 폼 상태의 변경을 실시간으로 추적.
값이 변경될 때마다 최신 값을 반환함
const emailValue = watch('email'); // 특정 필드 추적
const allValues = watch(); // 전체 필드 추적
4. mode 관련
역할: mode 옵션으로 검증(validation)이 언제 실행될지를 설정
- onSubmit (기본값): 폼 제출 시 검증.
- onChange: 입력 값이 변경될 때마다 검증.
- onBlur: 입력 필드에서 포커스가 벗어날 때 검증.
- all: onChange와 onBlur 모두 실행.
const { register, handleSubmit, formState } = useForm({
mode: 'onChange', // 입력할 때마다 검증
});
개인적으로 onChange랑 onBlur의 차이가 뭔지 헷갈렸다. 둘 다 입력값이 변경될때 실시간으로 검증되는 것 같았기 때문이다.
그래서 찾아보니,
onBlur는 포커스가 빠져나갈 때 실행된다.
입력필드에서 사용자가 input필드가 아닌 다른 곳을 클릭하거나 탭을 이동할 때 즉, 포커스가 벗어난 경우 실행된다.
사용자가 입력을 끝낸 후 검증, 입력 도중 에러 메시지를 보여주지 않고 최소한의 인터렉션만 제공하고 싶을 때 사용 된다.
적은 검증 빈도라 성능에 유리하다
ex.이메일 형식 확인, 필수 입력값 체크
onChange는 값이 변경될 때 실행된다.
입력필드의 값을 실시간으로 확인하거나 검증할 때, 자동완성, 검색 기능 등 입력값에 반응하는 로직이 필요할때 사용된다
값이 바뀔 때마다 실행되어 성능 부담이 될 수도 있다.
ex.검색 추천어, 실시간 비밀번호 검증
5. handleSubmit
역할: 폼 데이터를 제출하는 함수
제출할때 검증하고 데이터를 넘겨줌!
유효성 검사를 통과한 경우에만 onSubmit 콜백을 호출함
예시:
const onSubmit = (data) => console.log(data);
<form onSubmit={handleSubmit(onSubmit)}>
<button type="submit">제출</button>
</form>
6. formState
역할: 폼의 상태를 담고 있는 객체로, 검증 상태와 입력값 변경 여부 등을 확인.
주요 속성:
- errors: 각 필드의 검증 에러 정보.
- isDirty: 폼이 수정되었는지 여부.
- isValid: 폼이 유효한지 여부.
7. reset
역할: 폼 상태를 초기화.
기본값으로 돌아가거나 특정 값으로 폼을 리셋
const { reset } = useForm();
<button onClick={() => reset()}>초기화</button>
8. setValue
역할: 특정 필드의 값을 프로그래밍 방식으로 설정.
유저가 직접 입력하지 않아도 내가 원하는 값을 강제로 설정할때 사용
setValue('email', 'test@example.com'); // email 필드에 값 설정
9. getValues
역할: 현재 입력된 값을 가져옵니다.
유효성 검증과 상관없이 현재 값을 즉시 확인할 때
const values = getValues();
console.log(values);
10. defaultValues
역할: 폼 필드의 초기 값을 설정.
//회원가입 폼 상태 관리
const form = useForm({
defaultValues: {
name: '',
email: '',
password: '',
confirmPassword: '',
phone: '',
},
mode: 'onChange',
});
Zod란?
Zod는 TypeScript를 우선으로 설계된 스키마 선언 및 데이터 검증 라이브러리.
Zod를 사용하면 데이터 검증과 타입 선언을 한 번에 처리할 수 있어, 중복된 타입 선언을 제거하고 효율적인 코드를 작성할 수 있다. 한 번 선언된 스키마는 자동으로 정적 TypeScript 타입을 추론하여, 타입 안전성을 보장한다.
Zod의 주요 장점:
- 종속성 없음
Zod는 외부 라이브러리에 의존하지 않아 가볍고 독립적으로 사용할 수 있다. - 다양한 환경에서 작동
Node.js와 최신 브라우저를 포함한 대부분의 JavaScript 환경에서 사용할 수 있다. - 작은 용량
라이브러리 자체가 경량으로 설계 되었다. - 불변성(Immutability)
모든 Zod 객체는 불변성을 가지며, 원래 객체를 수정하지 않고 새로운 객체를 반환한다. - 간결하고 체인 가능한 인터페이스
직관적이고 선언적인 API를 제공하여 가독성이 높고, 복잡한 검증 로직도 간단히 작성할 수 있다.
const PasswordSchema = z.string().min(8).max(20).regex(/[A-Z]/, '대문자 필요');
Zod의 주요 메서드와 사용법
1) 기본 스키마 생성
다양한 기본 데이터 타입을 선언한다.
const StringSchema = z.string();
const NumberSchema = z.number();
const BooleanSchema = z.boolean();
2) 객체 스키마
객체의 키와 값을 정의
const UserSchema = z.object({
name: z.string(),
email: z.string().email(),
age: z.number().optional(),
});
3) 조건 추가
const AgeSchema = z.number().min(18, '최소 나이는 18세입니다').max(100);
4) 선택적
필드가 선택사항일 경우에는 optional()을 쓰면 된다.
const OptionalField = z.string().optional();
5) 커스텀 검증
const CustomSchema = z.string().refine((val) => val.startsWith('z'), {
message: 'z로 시작해야 합니다.',
});
뭐가 너무 많아서 헷갈렸던 거....
refine, superRefine, transform 언제 쓰이는건가?
refine
- 추가 조건을 검증할 때 사용.
- 조건이 통과되지 않으면 에러를 반환.
const schema = z.string().refine((val) => val.length >= 5, {
message: "최소 5자 이상이어야 합니다.",
});
schema.parse("hello"); // ✅ 성공
schema.parse("hi"); // ❌ 에러
superRefine
refine보다 강력한 기능으로 객체 전체를 기반으로 복잡한 조건을 추가할 때 사용
여러 에러메시지를 반환하거나, 특정 필드에 에러를 연결 할 수도 있다
const schema = z.object({
password: z.string(),
confirmPassword: z.string(),
}).superRefine((data, ctx) => {
if (data.password !== data.confirmPassword) {
ctx.addIssue({
path: ['confirmPassword'], // 에러를 특정 필드에 연결
message: '비밀번호가 일치하지 않습니다.',
});
}
});
schema.parse({ password: '1234', confirmPassword: '1234' }); // ✅ 성공
schema.parse({ password: '1234', confirmPassword: 'abcd' });
// ❌ 에러: confirmPassword - 비밀번호가 일치하지 않습니다.
transform
- 검증 후 데이터를 변환.
const schema = z.string().transform((val) => val.toUpperCase());
schema.parse("hello"); // ✅ "HELLO" 반환
1. Zod 설치
npm install react-hook-form zod @hookform/resolvers // npm
yarn add react-hook-form zod @hookform/resolvers // yarn
2. 사용
import React from 'react';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { zodResolver } from '@hookform/resolvers/zod';
// Zod 스키마 정의
const schema = z.object({
username: z.string().min(1, '사용자 이름은 필수입니다.'),
email: z.string().email('유효한 이메일 주소를 입력하세요.'),
password: z.string().min(6, '비밀번호는 최소 6자 이상이어야 합니다.'),
});
type FormData = z.infer<typeof schema>;
const App: React.FC = () => {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<FormData>({
resolver: zodResolver(schema),
});
const onSubmit = (data: FormData) => {
console.log('폼 데이터:', data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div>
<label>사용자 이름</label>
<input {...register('username')} />
{errors.username && <p>{errors.username.message}</p>}
</div>
<div>
<label>이메일</label>
<input {...register('email')} />
{errors.email && <p>{errors.email.message}</p>}
</div>
<div>
<label>비밀번호</label>
<input type="password" {...register('password')} />
{errors.password && <p>{errors.password.message}</p>}
</div>
<button type="submit">제출</button>
</form>
);
};
export default App;
'React' 카테고리의 다른 글
[React] 초기로딩속도 줄이기! 최적화 방법 (1) | 2025.01.19 |
---|---|
[React] react-daum-postcode 우편번호, 주소 검색 (2) | 2024.11.15 |
[React] Props로 전달되는 문자열 줄바꿈 하기 (0) | 2024.09.26 |
[React]페이지 이탈 시 확인 모달창 띄우기 (0) | 2024.09.13 |
[React]useCallback (0) | 2024.09.03 |