회원가입 기능을 구현할 때 유저로부터 이름, 이메일 주소, 비밀번호 등 여러가지 데이터를 입력 받아야 한다. state를 생성해서 입력받는 방법도 있겠지만 항목이 많을수록 그만큼 state를 여러 개 만들어야 하는 번거로움이 있다. 이를 개선해주는 React 라이브러리인 React-hook-form을 이용해서 회원가입 폼 유효성검사를 구현해보았다.
Form의 Validation을 좀더 효율적으로 구현할 수 있게 도와주는 React 라이브러리이다.
다음의 장점을 가지고 있다.
현재 만들고 있는 프로젝트의 회원가입 폼에는 총 4개의 input
이 필요하다.
state를 사용했다면 각 항목별로 state를 모두 만들고, onChange 핸들러를 각각 만들어야 하는 번거로움이 있지만 React-hook-form을 사용하여 좀 더 간단하게 구현할 수 있었다.
useForm()
hook을 사용하여 필요한 함수, 객체 불러오기// form에 사용되는 데이터 타입 정의
interface IAuthForm {
nickname: string;
email: string;
pw: string;
pwConfirm: string;
}
const {
register,
formState: { errors },
handleSubmit,
setError,
} = useForm<IAuthForm>({mode: 'onBlur'});
Methods / Functions | Description |
---|---|
register | input 요소를 React hook form과 연결시켜 검증 규칙을 적용할 수 있게 하는 메소드 |
formState | form state에 관한 정보를 담고 있는 객체 |
handleSubmit | form을 submit했을 때 실행할 함수. Validation을 통과했을때 실행할 콜백함수(SubmitHandler)가 반드시 필요하다. 실패했을때의 콜백함수(SubmitErrorHandler)는 옵셔널. |
setError | error 관련 설정에 사용되는 함수 |
사용자가 submit 버튼을 누르기 전에 form에 입력한 값이 유효한 값이 아니라는 것을 미리 표시해주는 것이 좀 더 나은 UX를 제공한다고 생각하는데, 이를 가능하게 해주는 것이 mode
이다.
mode
는 useForm()
에 넘겨줄 수 있는 다양한 optional arguments 중 하나로 사용자가 form을 submit 하기 전에 validation이 실행될 수 있게 해준다.
// mode에 사용 가능한 값
mode: onChange | onBlur | onSubmit | onTouched | all = 'onSubmit'
//onBlur 이벤트가 발생할때마다 validation이 실행된다.
const { register } = useForm({mode: 'onBlur'})
register
메소드를 사용해서 input 요소를 React hook form에 등록한다.
register
는 객체를 반환하기 때문에 스프레드 문법을 사용해서 props로 전달해줘야 준다. 인자로 input
요소의 name으로 사용될 string 값을 써준다.
register: (name: string, RegisterOptions?) => ({ onChange, onBlur, name, ref })
const AuthForm = () => {
// ...
return (
<form>
<fieldset>
<legend>회원가입</legend>
<label htmlFor="nickname">닉네임</label>
<input {...register("nickname")} />
<label htmlFor="email">이메일</label>
<input {...register("email")} />
<label>비밀번호</label>
<input {...register("pw")} />
<label>비밀번호 확인</label>
<input {...register("pwConfirm")} />
</fieldset>
</form>
)
}
option name | Description |
---|---|
required | 필수 입력 항목 지정할 때 사용할 수 있다. boolean값도 가능하지만 문자열 값을 주면, input의 value가 없을 때 해당 문자열이 errors 객체로 반환되어 에러 메세지로 표시할 수 있다. |
minLength | value의 최소 길이 |
pattern | input 입력값에 적용할 정규식 패턴 |
validate | validate 함수를 직접 만들어서 validation을 할 수 있다. 대부분의 validation 옵션들은 string, string[], number, boolean 타입 데이터에 해당되므로, 객체나 배열 input 데이터의 경우, validate 함수를 사용하는 것이 권장된다.(참고) |
<input
{...register('nickname', {
required: '닉네임을 입력해주세요.',
// required: true,
// boolean값도 가능하지만 문자열 값을 주면, input의 value가 없을 때 해당 문자열이 errors 객체로 반환되어 에러 메세지로 표시할 수 있다.
minLength: { // value의 최소 길이
value: 3,
message: '3글자 이상 입력해주세요.', // 에러 메세지
},
pattern: { // input의 정규식 패턴
value: /^[A-za-z0-9가-힣]{3,10}$/,
message: '가능한 문자: 영문 대소문자, 글자 단위 한글, 숫자', // 에러 메세지
},
})}
placeholder="닉네임을 입력해주세요"
/>
<p>{errors?.nickname?.message}</p> // validation fail 시 에러 메세지 표시
// validate 예시
<input
{...register("test1", {
validate: {
checkUrl: async () => await fetch() || 'error message',
messages: v => !v && ['test', 'test2']
}
})}
/>
handleSubmit
함수의 인자로 반드시 SubmitHandler
(validation 통과 됐을때의 콜백함수)를 써준다. 콜백함수가 비동기 함수인 경우 handleSubmit
함수가 따로 에러 핸들링을 하지 않으므로 try...catch 로 에러 핸들링을 해준다.
const AuthForm = () => {
const {
//...
handleSubmit,
setError
} = useForm<IAuthForm>({mode: 'onBlur'});
// SubmitHandler
const onValid = (data : IAuthForm) => {
console.log("성공!")
}
return (
// handleSubmit함수를 onSubmit props로 보내준다
<form onSubmit={handleSubmit(() => {
try {
onValid()
}catch(e){
console.log(e)
}
)}>
<fieldset>
<legend>회원가입</legend>
// ...
</fieldset>
</form>
)
}
const AuthForm = () => {
const {
//...
handleSubmit,
setError,
} = useForm<IAuthForm>({mode: 'onBlur'});
const onValid = (data : IAuthForm) => {
if (data.pw !== data.pwConfirm) {
setError(
'pwConfirm', // 에러 핸들링할 input요소 name
{ message: '비밀번호가 일치하지 않습니다.' }, // 에러 메세지
{ shouldFocus: true }, // 에러가 발생한 input으로 focus 이동
);
}
}
return (
<form onSubmit={handleSubmit(() => {
try {
onValid()
}catch(e){
console.log(e)
}
)}>
<fieldset>
<legend>회원가입</legend>
// ...
<label>비밀번호</label>
<input {...register("pw")} />
<label>비밀번호 확인</label>
<input {...register("pwConfirm")} />
</fieldset>
</form>
)
}