ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [react-hook-form] form 안에서 yup으로 일부 요소만 검증, 에러처리하기
    📒코드기록 2024. 3. 5. 00:59

    문제점

    form 데이터를 전송할 때 일반적인 상황에 리액트 훅폼을 사용했다. 근데 폼 안에 있는 데이터중 일부만 api통신을 해서 응답값을 받아와야 하는 케이스가 생겼다. 데이터 통신은 문제 없는데 에러, 밸리데이션이 까다로워서 하루종일 걸렸다...그래서 적어본다.

     

    로직 작성 순서

    1. hook form에 있는 값을 객체 형대로 넘김

    const submitData: IOverseasExpectedDeliveryFeeRequest = {
          horizontal: horizontal!,
          vertical: vertical!,
          height: height!,
          weight: weight!,
          };

    여기서 해당 값을 검증한 뒤에 submit하기 때문에 null이 아니라고 단언연산자를 사용했다.

     

     

    2. yup과 hookform을 이용한 밸리데이션(검증)

    그냥 if문에다가 정규식으로 getValues의 값을 하나하나 검증할 수도 있었다. 하지만 hook-form의 밸리데이션을 resolver: yupSchema를 이용해서 하고 있었기 때문에 통일성을 주기 위해서 해당 코드에서도 yup을 사용해서 밸리데이션을 해보았다.

    // 객체 형식으로 작성한 yup schema 
    const productOverseasDeliveryFee = yup.object().shape({
      horizontal: validation.REQUIRED_TWO_DECIMAL_NUMBER,
      vertical: validation.REQUIRED_TWO_DECIMAL_NUMBER,
      height: validation.REQUIRED_TWO_DECIMAL_NUMBER,
      weight: validation.REQUIRED_TWO_DECIMAL_NUMBER,
    });
    
    // 밸리데이션
    await schema.productOverseasDeliveryFee.validate(submitData, { abortEarly: false });
    
    // 밸리데이션 후 api 호출
    const await Get.getOverseasExpectedDeliveryFee(submitData)

    abortEarly라는 옵션을 꺼야 에러를 반환할 때 에러메시지만 오지 않고, 어떤 input에서 발생한 에러인지 등등 정보를 같이 담은 배열로 반환해준다. setError로 인풋 하단에 에러 메시지를 노출시키기 위해서는 이와 같은 정보가 필요했다.

     

     

    3. 에러처리

    const handelClickSubmit = async () => {
        const submitData: IOverseasExpectedDeliveryFeeRequest = {
          horizontal: horizontal!,
          vertical: vertical!,
          height: height!,
          weight: weight!,
        };
        try {
          await schema.productOverseasDeliveryFee.validate(submitData, { abortEarly: false });
    
          const response = await Get.getOverseasExpectedDeliveryFee(submitData)
          
          // 백에서 보내주는 에러 필터링
          if (response.code !== 200) {
            throw new Error();
          }
          // api 응답값 중 필요한 값 매핑
          // ...
          
        } catch (error: any) {
        // 밸리데이션 통과하지 못한 경우 로직
          if (error instanceof ValidationError) {
            error.inner.forEach((err) => {
              err.path && setError(err.path as TProductSizeValue, { type: 'custom', message: err.message }, { shouldFocus: true });
            });
            return;
          }
    	// 백에서 보내주는 에러 로직
          ErrorHandler(error);
        } finally {
          
        }
      };

    catch에서 밸리데이션 에러가 발생한 경우를 보면 error.inner에 에러 정보가 담긴 배열이 담겨있다.

    setError를 이용해서 에러메시지를 해당 name과 일치하는 input 하단에 렌더링 시킨다.

     

     

    번외) 타입스크립트

    type TProductSizeValue = keyof Pick<IAdminStoreTypeProductForm, 'horizontal' | 'vertical' | 'height' | 'weight'>;
    • 해당 form dto의 타입을 정의해놓은 인터페이스에서 필요한 값만 꺼내오는 Pick
    • 인터페이스의 key값. 객체의 속성 이름만 추출해서 타입으로 정의(모든 프로퍼티의 키값을 union 형태로 반환)

     

    여전히 남아있는 개선해야 할 점과 로직 구현 후 든 생각

    error.inner에 모든 인풋의 에러가 한번에 담겨서 오기 때문에 검증을 여러번 하는 yup schema의 경우 하나의 인풋임에도 에러 객체가 여러개가 온다. 그래서 제일 마지막 에러 메시지만 에러 텍스트 영역에 노출되고 있다. 현재 로직 상 필수, 숫자 여부만 검증하고 있어서 큰 문제가 되지 않지만 더 까다로운 밸리데이션이 필요한 경우에는 개선이 필요하겠다.

    아니면 아예 yup을 사용하지 않고 if문에 직접 정규식.test()로 값을 하나하나 검증하는것도 이런 상황해서는 더 좋은 방법인 것 같기도 하다.

    댓글

Designed by Tistory.