본문 바로가기
IT/React

[React-Hook-Form] react hook form validate debounce 적용기

by 무녈 2022. 8. 7.

React Hook Form 적용하기

react hook form을 통해 input에 대한 실시간 validation을 간편하게 구현하고 있었다.

회원가입을 구현하는 중, 꼭 필요한 기능으로 이메일, 아이디, 휴대폰 번호에 대한 중복검사가 필요하였고 별도의 버튼 클릭 대신 사용자들이 값을 입력했을 경우 서버로 자동으로 request를 날리도록 구현하였다.

하지만 너무 지나친 request는 서버 성능에 문제를 줄 수 있기 때문에 간단하게 debounce 메서드를 만들었다.

// debounce.js

function debounce(callback, limit = 1000) {
  let timeout;
  return function (...args) {
    clearTimeout(timeout);
    timeout = setTimeout(() => {
      callback.apply(this, args);
    }, limit);
  };
}

export default debounce;

그리고 debounce의 첫 번째 인자로 내가 검증하고 싶은 메서드를 넣고 validate에 바로 할당할 경우 자연스럽게 두 번째 인자로 할당한 시간만큼 지연이 되어 마지막 값에 대해서만 요청되도록 하고 싶었다.

const debouncePhoneChange = debounce(
    async (e) =>
      await checkMemberInfo(
        e,
        userApis.PHONE_DUPLICATE_CEHCK_API(e.target.value),
        setDuplicatedPhone,
        'phone',
        REGISTER_MESSAGE.DUPLICATED_PHONE,
      ),
  );

... 중략 ...

<input
  name="phone"
  id="phone"
  type="tel"
  {...register('phone', {
    required: REGISTER_MESSAGE.REQUIRED_PHONE,
    pattern: {
      value: REGEX.PHONE,
      message: REGISTER_MESSAGE.PHONE_STANDARD,
			validate: (value) => onChange(value, debouncePhoneChange),
    },
  })}
  placeholder=" "
  autoComplete="off"
  required
/>

문제 발생

validate에서 debounce를 적용한 함수를 넣었으나, network 확인 결과

pattern에 대한 예외 처리 이후 너무 많은 서버로의 요청이 발생했다는 것을 발견하였다. 애초에 서버에 대한 요청 횟수를 줄이고자 debounce를 적용하였지만, 오히려 더 많은 요청을 하게 된 것이다.

원인을 찾던 중 onChange로 할당하면 된다고 하여 onChange에 할당하였지만 그 역시 마찬가지였다. 우선 react-hook-form에서 onChange의 사용을 자제하도록 권장하고는 있었다.

우선 onChange로 해당 함수를 사용해서는 안 되겠다는 판단이 들어서 최대한 validate에 적용할 수 있도록 구글링을 했지만 삽질의 연속이었다. 어떤 방법을 해도 위와 같이 계속해서 key 입력값만큼 서버로 요청이 발생했다.

해결방법

생각해보니 매번 요청하여 함수가 실행될 때마다 재 렌더링이 발생하는 문제가 있다는 것을 캐치하였다. 그리하여 생각해낸 것이 해당 debounce 함수를 useMemo에 감싸면 해결이 되지 않을까 생각하였다.

useMemo useMemo는 컴포넌트의 성능을 최적화시킬 수 있는 대표적인 react hooks 중 하나이다. useMemo에서 Memo는 Memoization을 뜻하며, memoization이란 기존에 수행한 연산의 결괏값을 어딘가에 저장해 두고 동일한 입력이 들어오면 재활용하는 프로그래밍 기법이다.

지금 동일한 값에 대한 요청이 지속적으로 발생하고 있으므로, 해당 요청을 한 번만 보낼 수 있도록 하면 될 것이라 예상하였다.

그리하여 email 검증을 위한 함수를 먼저 useMemo를 적용하였다.

const debouncedValidateEmail = useMemo(
    (value) => debounce((value) => debounceEmailChange(value), 700),
    [],
  );

const debounceEmailChange = async (value) =>
    await checkMemberInfo(
      value,
      userApis.EMAIL_DUPLICATE_CHECK_API(value),
      setDuplicatedEmail,
      'email',
      REGISTER_MESSAGE.DUPLICATED_EMAIL,
    );

... 중략 ...

<input
    name="email"
    id="email"
    type="email"
    {...register('email', {
      required: REGISTER_MESSAGE.REQUIRED_EMAIL,
      pattern: {
        value: REGEX.EMAIL,
        message: REGISTER_MESSAGE.EMAIL_STANDARD,
      },
      validate: debouncedValidateEmail,
    })}
    placeholder=" "
    required
  />

위와 같이 validate에 해당 함수를 적용한 결과 원하는 대로, limit time에 따라 서버에 request가 발생하였다.

결과적으로 useMemo와 debounce를 활용하여, 원하는 대로 기능을 구현할 수 있었다.

진짜 몇 시간을 삽질했는지… 후

 


참고

https://react-hook-form.com/

 

Home

React hook for form validation without the hassle

react-hook-form.com

https://ko.reactjs.org/docs/hooks-reference.html#usememo

 

Hooks API Reference – React

A JavaScript library for building user interfaces

ko.reactjs.org

 

반응형

댓글