본문 바로가기
SPA/React

[React] react-hook-form과 yup으로 동적 배열 유효성 검증하기

by J4J 2022. 8. 9.
300x250
반응형

안녕하세요. J4J입니다.

 

이번 포스팅은 react-hook-form과 yup을 이용하여 동적 배열 유효성 검증하는 방법에 대해 적어보는 시간을 가져보려고 합니다.

 

 

 

기본적인 방법

 

긴말 없이 바로 코드부터 보도록 하겠습니다.

 

동적 배열 유효성 검증 테스트를 위해 다음과 같이 코드를 작성할 수 있었습니다.

 

import * as React from 'react';
import * as yup from 'yup';
import { yupResolver } from '@hookform/resolvers/yup';
import { useForm } from 'react-hook-form';

// obj interface
interface Iobj {
    name: string;
    age: number;
}

// form interface
interface Iform {
    objList: Iobj[];
}

const Base = () => {
    // 유효성 검증 조건
    const schema = yup.object().shape({
        objList: yup.array().of(
            yup.object().shape({
                name: yup.string().required('아이디를 입력해주세요.'), 
                age: yup.number().min(3, '3이상 값을 입력해주세요.').max(10, '10이하 값을 입력해주세요.'),
            })
        )
    })
    
    // useForm
    const { 
        register, 
        handleSubmit, 
        formState: { errors } ,
        setValue,
        getValues,
        watch,
    } = useForm<Iform>({
        resolver: yupResolver(schema),
        defaultValues: {
            objList: []
        }
    })

    // handler
    const handle = {
        // 추가
        clickAdd: () => {
            setValue('objList', [...getValues('objList'), { name: '', age: 0 }]);
        },

        // 삭제
        clickDelete: (index: number) => {
            setValue('objList', getValues('objList').filter((obj, idx) => idx !== index));
        },

        // 검증
        clickCheck: () => {
            alert('Success');
            console.log(watch());
        }
    }

    return (
        <div>
            <div>
                <button onClick={handle.clickAdd}>추가</button>
                <button onClick={handleSubmit(handle.clickCheck)}>검증</button>
            </div>

            <div>
                {watch('objList').map((obj, index) => (
                    <div key={index}>
                        <div>
                            <input type="text" placeholder='input name ...' {...register(`objList.${index}.name`)} />
                            {errors.objList?.[index]?.name && errors.objList?.[index]?.name?.message}
                        </div>

                        <div>
                            <input type="number" placeholder='input age ...' {...register(`objList.${index}.age`)} />
                            {errors.objList?.[index]?.age && errors.objList?.[index]?.age?.message}
                        </div>

                        <button onClick={() => handle.clickDelete(index)}>삭제</button>
                    </div>
                ))}
            </div>
        </div>
    )
}

export default Base;

 

 

반응형

 

 

코드를 실행하면 다음과 같은 화면이 보입니다.

 

실행 결과

 

 

 

추가를 누를 때마다 이름과 나이를 입력하는 input폼이 같이 생성이 되었고, 검증을 누를 경우 현재 배열의 모든 값에 대해 유효성 검증을 해주고 있었습니다.

 

또한 삭제 버튼을 누를 경우 해당 index에 대해 삭제 기능도 정상적으로 수행되었고 당연스럽게 삭제되는 순간 유효성 검증 처리에서 배제되고 있습니다.

 

 

 

useFieldArray 활용 방법

 

위의 방법을 사용해도 문제없이 유효성 검증을 할 수 있지만, 다른 방법중 하나인 useFieldArray를 활용해서 동일한 결과를 만들어 보겠습니다.

 

코드로 작성하면 다음과 같습니다.

 

 

728x90

 

 

import * as React from 'react';
import * as yup from 'yup';
import { yupResolver } from '@hookform/resolvers/yup';
import { useFieldArray, useForm } from 'react-hook-form';

// obj interface
interface Iobj {
    name: string;
    age: number;
}

// form interface
interface Iform {
    objList: Iobj[];
}

const FieldArray = () => {
    // 유효성 검증 조건
    const schema = yup.object().shape({
        objList: yup.array().of(
            yup.object().shape({
                name: yup.string().required('아이디를 입력해주세요.'), 
                age: yup.number().min(3, '3이상 값을 입력해주세요.').max(10, '10이하 값을 입력해주세요.'),
            })
        )
    })
    
    // useForm (* 수정)
    const { 
        register, 
        handleSubmit, 
        formState: { errors } ,
        control,
        watch,
    } = useForm<Iform>({
        resolver: yupResolver(schema),
        defaultValues: {
            objList: []
        }
    })

    // useFieldArray (* 추가)
    const {fields, append, remove } = useFieldArray({
        control,
        name: 'objList',
    })

    // handler
    const handle = {
        // 추가 (* 수정)
        clickAdd: () => {
            append({
                name: '',
                age: 0,
            })
        },

        // 삭제 (* 수정)
        clickDelete: (index: number) => {
            remove(index);
        },

        // 검증
        clickCheck: () => {
            alert('Success');
            console.log(watch());
        }
    }

    return (
        <div>
            <div>
                <button onClick={handle.clickAdd}>추가</button>
                <button onClick={handleSubmit(handle.clickCheck)}>검증</button>
            </div>

            <div>
                {/* (* 수정) */}
                {fields.map((obj, index) => (
                    <div key={index}>
                        <div>
                            <input type="text" placeholder='input name ...' {...register(`objList.${index}.name`)} />
                            {errors.objList?.[index]?.name && errors.objList?.[index]?.name?.message}
                        </div>

                        <div>
                            <input type="number" placeholder='input age ...' {...register(`objList.${index}.age`)} />
                            {errors.objList?.[index]?.age && errors.objList?.[index]?.age?.message}
                        </div>

                        <button onClick={() => handle.clickDelete(index)}>삭제</button>
                    </div>
                ))}
            </div>
        </div>
    )
}

export default FieldArray;

 

 

 

추가 및 수정이 이루어진 부분은 useForm, useFieldArray, handle과 마지막으로 html에 데이터를 뿌려줄 때입니다.

 

저 같은 경우는 useFieldArray를 사용했더니 이전보다 간결하고 깔끔하게 사용되는 것을 느낄 수 있었습니다.

 

하지만 둘 중 무엇을 사용하더라도 단순 취향 차이라고 생각될 것 같으니 보다 편하신 방법을 선택해서 사용해주시면 될 것 같습니다.

 

 

 

 

 

 

 

이상으로 react-hook-form과 yup을 이용하여 동적 배열 유효성 검증하는 방법에 대해 간단하게 알아보는 시간이었습니다.

 

읽어주셔서 감사합니다.

 

 

 

728x90
반응형

댓글