반응형

react-hook-form을 사용하여 form태그 내의 input태그의 onChange이벤트, 유효성 검사, submit후 데이터 값 확인 등 다양한 작업을 손쉽게 처리 할 수 있습니다.

 

기존의 react를 사용하여 해당 기능을 처리하려면 useState를 통해 처리할 값고 set함수를 선언하고 onChange이벤트에 넣어주고  value값에 state값을 넣어주고, 유효성을 체크하려면 onChange에 걸어둔 함수에서 길이를 체크하거나 정규식으로 패턴을 검사하거나 한개의 input만 해도 아주 복잡한 코드가 만들어지는데, 복잡한 회원가입이나 입력폼에서 이 과정을 실행하려면 상당한 긴 소스가 작성될 것입니다. 이런 문제를 간단하게 해결해 줄 수 있습니다.

 

먼저 설치를 진행합니다.

npm install react-hook-form

 

기본적으로 사용을 위해서 useForm이라는 hook을 항상 import해서 사용합니다.

import { useForm } from "react-hook-form"

 

1. register

register는 input 기능에 특화되어 있는 함수로 onBlur, onChange를 가지고 있는 함수입니다.

처리하고자 하는 input태그 내에 {...register("name")} 하나만으로 모든 처리가 끝납니다.

 

-사용 방법

import { useForm } from "react-hook-form";

interface IForm{
    userId: string;
    email: string;
}

function App(){
    const { register } = useForm();

    return (
        <div>
            <form>
                <input {...register("userId")} placeholder="아이디" />
                <input {...register("email")} placeholder="이메일" />
                <button>추가</button>
            </form>
        </div>
    )
}

export default App;

단지 jsx input 내부에 {...register("이름")}만으로 onChange이벤트에 의해 value값이 처리되는 걸 볼 수있습니다.

 

-유효성 검사

register함수의 2번째 파라미터부터 유효성 검사를 할 요소를 object형태로 넣어주면 됩니다.

{
    required: "아이디를 입력하세요.",
    minLength: {
        value: 5,
        message: "아이디는 최소 5자리 이상입니다."
    },
    maxLength: {
        value: 12,
        message: "아이디는 최대 12자리 이상입니다."
    }    
}

required: true, minLength:5, maxLength: 12 이런식으로 넣어줘도 되지만, 오류메시지 출력을 위해 value값과 message로 나눠서 처리해주었습니다.

 

-유효성 검사 예시

import { useForm } from "react-hook-form";

interface IForm{
    userId: string;
    email: string;
}

function App(){
    const { register } = useForm();

    return (
        <div>
            <form>
                <input {
                    ...register("userId",
                    {
                        required: "아이디를 입력하세요.",
                        minLength: {
                            value: 5,
                            message: "아이디는 최소 5자리 이상입니다."
                        },
                        maxLength: {
                            value: 12,
                            message: "아이디는 최대 12자리 이상입니다."
                        },
                    }
                )} placeholder="아이디" />
                <input {...register("email")} placeholder="이메일" />
                <button>추가</button>
            </form>
        </div>
    )
}

export default App;

 

-pattern(정규식 체크)

<input {
    ...register("email", {
        required: "이메일을 입력하세요.",
        pattern: {
            value: /^[a-zA-Z0-9+-\_.]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/,
            message: "이메일 형식이 아닙니다."
        },
})} placeholder="이메일" />

 

-validate옵션을 통한 커스텀을 통한 유효성 검사

validate: {
    noAdmin: (value) => value.includes("admin") ? "admin은 포함 시킬 수 없습니다." : true,
    noShin: (value) => value.includes("Shin") ? "Shin은 포함 시킬 수 없습니다." : true,
}

validate의 return값이 false 또는 문자열이 발생하면 유효하지 않는 값으로 인식하기 때문에, 오류로 처리하고자 하는 값이면 false처리하거나 메시지를 입력하면 되고 유효한 데이터라면 true를 처리해주면 됩니다.

 

 

2. watch

form태그의 입력된 값들의 변화를 관찰해주는 함수입니다.

userForm에서 watch함수를 호출하기만 하면 값이 변경될 때 마다 동작하는 것을 확인 할 수 있습니다.

const { register, watch } = useForm<IForm>();

console.log(watch());

 

값을 입력할때마다 console.log에 와치함수가 동작하여 태그 값들이 찍히고 있다.

 

 

3. handleSubmit

onSubmit의 동작을 처리해줄 handleSubmit입니다.

handleSubmit에는 2개의 인자를 받습니다.

onValid와 onInvalid

타입스크립트 설명에서 알 수 있듯이 첫번째 파라미터는 필수값이며, 유효한 경우 동작할 함수를 넣어줍니다.

두번째 파라미터는 선택이며, 유효하지 않은 경우 동작할 함수를 넣어주면 됩니다.

 

사용법은 form태그 onSubmit에 해당 함수를 넣어주면 됩니다.

import { useForm } from "react-hook-form";

interface IForm{
    userId: string;
    email: string;
}

function App(){
    const { register, handleSubmit } = useForm<IForm>();

    const onValid = (data:IForm) => {
        console.log(data);
    }

    return (
        <div>
            <form onSubmit={handleSubmit(onValid)}>
            </form>
        </div>
    )
}

export default App;

onValid라는 함수를 만들었습니다.

IForm interface의 정의를 받는 타입의 object값들을 받아서 console.log에 찍어주는 함수를 생성하였습니다.

form태그 내부에는 onSubmit태그 내의 handleSubmit함수를 생성하였고 첫번째 인자에 onValid를 넣어주었습니다.

 

 

4. formState:erros

useForm내의 formState를 사용하면 유효하지 않는 값들을 걸러내고 어떤 오류의 종류인지 오류메시지는 무엇인지 알 수 있습니다.

formState를 호출하고 formState의 errors메시지를 호출하는 것으로 에러들을 체크 할 수 있습니다.

const { register, handleSubmit, formState } = useForm<IForm>();
console.log(formState.errors);

이것들을 각 항목별로 유효성 오류를 표출시킬 수 있습니다.

사진에서 볼 수 있듯이 register에서 등록한 이름별로 오류메시지들이 출력됩니다.

type에는 어떤 종류인지 나옵니다.

message에는 문자열로 정의한 오류 메시지가 출력됩니다.

해당 정보들을 토대로 아래처럼 각 항목 아래에 오류 메시지를 출력시킬 수 있습니다.

 

errors?.항목이름?.message

오류 데이터가 없는 경우가 있을 수 있으니 꼭 optional(?.)처리를 해주세요.

 

-오류 표출해보기

import { useForm } from "react-hook-form";

interface IForm{
    address: string;
    email: string;
    nickName: string;
    phone: string;
    userId: string;
    userPw: string;
    userPwConfirm: string;
}

function Join(){
    const { register, watch, handleSubmit, formState:{errors} } = useForm<IForm>();

    //handleSubmit은 2개의 인자를 받는데 첫번째는 유효한 데이터인 경우 호출되는 함수다.
    //두번째 인자는 유효하지 않는 경우 호출되는 함수로 첫번째 함수만 필수값이다.
    const onValid = (data:IForm) => {
        console.log(data);
    }
    
    //에러체크는 formState의 error함수를 사용하여 알 수 있다.
    console.log(errors);
    return (
        <div>
            <form 
                style={{display:"flex", flexDirection:"column"}} 
                onSubmit={handleSubmit(onValid)} >
                <input {...register("userId", {required: "회원 아이디를 입력하세요."})} placeholder="아이디"/>
                <span>{errors?.userId?.message}</span>
                <input {...register("userPw", {required: "비밀번호를 입력하세요.", minLength: 10})} placeholder="비밀번호"/>
                <span>{errors?.userPw?.message}</span>
                <input {...register("userPwConfirm", {required: "비밀번호 확인을 입력하세요.", minLength: 10})} placeholder="비밀번호확인"/>
                <span>{errors?.userPwConfirm?.message}</span>
                <input {...register("nickName", {required: "닉네임을 입력하세요.", minLength: 10})} placeholder="닉네임"/>
                <span>{errors?.nickName?.message}</span>
                <input {
                    ...register("email", {
                        required: "이메일을 입력하세요.",
                        pattern: {
                            value: /^[a-zA-Z0-9+-\_.]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/,
                            message: "이메일 형식이 아닙니다."
                        },
                    })}
                    placeholder="이메일"/>
                <span>{errors?.email?.message}</span>
                <input {...register("address", {required: "주소를 입력하세요."})} placeholder="주소"/>
                <span>{errors?.address?.message}</span>
                <input {...register("phone", {required: "전화번호를 입력하세요."})} placeholder="전화번호"/>
                <span>{errors?.phone?.message}</span>
                <button>회원가입</button>
            </form>
        </div>
    );
}

export default Join;

작성 후 회원가입 버튼을 눌러서 submit동작을 시켜보면 각 오류메시지 타입들이 노출됩니다.

formState의 errors메소들을 주로 많이 사용하기 때문에 아래처럼 바로 가져와서 사용합니다.

const { formState:{errors} } = useForm<IForm>();

 

 

5. defaultValues

defaultValues로 기본값을 처리할 수 있습니다.

useForm의 첫번째 인자에 object형태로 정의해주면 됩니다.

const { register, handleSubmit, formState:{errors} } = useForm<IForm>({
    defaultValues: {
        phone: "010"
    }
});

타입스크립트로 useForm<IForm> 을 처리해둔 덕에 자동완성으로 리스트들이 나옵니다.

defaultValues내부에 원하는 값을 넣고 기본값을 입력해주면 UI에 바로 입력되어서 나오는 걸 볼 수 있습니다.

 

 

6. setError

setError를 통해 강제로 에러를 발생시킬 수도 있습니다.

역시나 타입스크립트가 사용법에 대해서 알려주고 있습니다.

 

첫번째 인자에는 에러를 발생시킬 항목을  두번째에는 에러 옵션을 넣으라고 나옵니다. 세번째는 선택입니다.

 

아래는 비밀번호 확인을 하여 오류가 발생한 경우를 작성했습니다.

const { setError } = useForm<IForm>();

const onValid = (data:IForm) => {
    if(data.userPw !== data.userPwConfirm){
        setError("userPwConfirm", 
                    {message: "비밀번호가 일치하지 않습니다."},
                    {shouldFocus: true}
            );
    }
}

submit동작을 해보면 오류가 발생하는 것을 볼 수 있습니다.

캡처에서는 빨간 밑줄이 그어져있지만 바로 포커싱을 가게 하고 싶다면 shouldFocus옵션을 true로 주면 된다.

 

 

 

또한 onValid나 InValid쪽에서 서버 오류등이 발생한 경우에도 강제로 오류를 발생시켜주면 좋을텐데 이런 경우에도 사용할 수 있습니다.

 

예외 처리에 대한 값이 필요한데 타입스크립트이므로 interface에 form태그에 대한 값을 추가하고, jsx에서 오류처리를 하던 부분에 동일하게 처리를 해주면 됩니다.

다만 interface에서 해당 extra값은 필수값은 아니므로 '?'처리를 해줍니다.

 

-비밀번호와 비밀번호 확인이 다른 경우 오류 발생처리(Full ver_Join.tsx)  & 강제 오류발생으로 메시지 띄우기 소스

import { useForm } from "react-hook-form";

interface IForm{
    userId: string;
    extraError?: string;
}

function Join(){
    const { 
        register, 
        handleSubmit, 
        formState:{errors},
        setError
    } = useForm<IForm>({
        defaultValues: {
            phone: "010"
        }
    });

    //handleSubmit은 2개의 인자를 받는데 첫번째는 유효한 데이터인 경우 호출되는 함수다.
    //두번째 인자는 유효하지 않는 경우 호출되는 함수로 첫번째 함수만 필수값이다.
    const onValid = (data:IForm) => {
        if(data.userPw !== data.userPwConfirm){
            setError("userPwConfirm", {message: "비밀번호가 일치하지 않습니다."});
        }
        setError("extraError", {message: "알 수 없는 오류가 발생했습니다."});
    }
    
    return (
        <div>
            <form 
                style={{display:"flex", flexDirection:"column"}} 
                onSubmit={handleSubmit(onValid)} >
                <input {...register("userId", {required: "회원 아이디를 입력하세요."})} placeholder="아이디"/>
                <span>{errors?.userId?.message}</span>
                <input {...register("userPw", {required: "비밀번호를 입력하세요.", minLength: 10})} placeholder="비밀번호"/>
                <span>{errors?.userPw?.message}</span>
                <input {...register("userPwConfirm", {required: "비밀번호 확인을 입력하세요.", minLength: 10})} placeholder="비밀번호확인"/>
                <span>{errors?.userPwConfirm?.message}</span>
                <input {...register("nickName", {required: "닉네임을 입력하세요.", minLength: 10})} placeholder="닉네임"/>
                <span>{errors?.nickName?.message}</span>
                <input {
                    ...register("email", {
                        required: "이메일을 입력하세요.",
                        pattern: {
                            value: /^[a-zA-Z0-9+-\_.]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/,
                            message: "이메일 형식이 아닙니다."
                        },
                    })}
                    placeholder="이메일"/>
                <span>{errors?.email?.message}</span>
                <input {...register("address", {required: "주소를 입력하세요."})} placeholder="주소"/>
                <span>{errors?.address?.message}</span>
                <input {...register("phone", {required: "전화번호를 입력하세요."})} placeholder="전화번호"/>
                <span>{errors?.phone?.message}</span>
                <button>회원가입</button>
                <span>{errors?.extraError?.message}</span>
            </form>
        </div>
    );
}

export default Join;

 

submit동작 후 강제 에러가 발생된 것을 볼 수 있습니다.

 

 

7. setValue

setValue를 통해 강제로 값을 변경할 수 있다.

입력 후 특정 값을 비워야하거나 자동 맞춤을 해주거나 할 경우 필요한 기능으로 2개의 필수 파라미터와 세번째는 옵션값을 갖는다.

첫번째 파라미터는 대상(항목)이며, 두번째 파라미터는 처리할 값이다.

처리할 값은 타입스크립트라면 정의해놓은 값으로 넣어주면 된다. string > string, number > number

사용 예시

setValue("toDo", ""); //toDo항목의 값을 비워준다.
반응형