프로젝트 이모저모/HoduMarket 프로젝트

오픈마켓) 회원가입 에러메세지 버그

Ella Seon 2023. 4. 7. 14:01

1️⃣첫번째 버그

0. 버그 현황

- 아이디 input 값에 이미 사용중인 아이디를 입력하고 중복확인 버튼을 클릭했을 때 이미 사용중인 아이디입니다. 라고
에러메세지가 정상적으로 렌더링 된다.
- input 값을 수정해서 또 이미 사용중인 아이디를 입력하고 중복확인 버튼을 클릭했을때는 에러메세지가 렌더링이 되지 않음

1. 버그 코드

- 기존에는 useEffect 를 써서 errorMsg 상태값이 변할 때마다 에러메세지를 렌더링 하는 코드로 짰었음

function JoinForm() {
  const [toggleType, setToggleType] = useState("buyer");
  const [isJoinValid, setIsJoinValid] = useState(false);
  const [idChecked, setIdChecked] = useState(false);
  const [businessChecked, setBusinessChecked] = useState(false);

  const {
    register,
    handleSubmit,
    getValues,
    setError,
    watch,
    trigger,
    formState: { errors },
  } = useForm({ mode: "onChange" });

  const dispatch = useAppDispatch();
  const errorMsg = useAppSelector((state) => state.join.error);
  const successMsg = useAppSelector((state) => state.join.successMsg);

  // id 중복 확인 검증
  const handleCheckId = async (
    id: string,
    e: React.MouseEvent<HTMLButtonElement>
  ) => {
    e.preventDefault();
    const isValid = await trigger("id");
    if (!isValid) {
      return;
    }
    const resultAction = await dispatch(fetchIdValidate(id));
    if (fetchIdValidate.fulfilled.match(resultAction)) {
      setIdChecked(true);
    }
  };


  // id 중복 확인 & 사업자 등록 번호 검증 에러메세지
  useEffect(() => {
    if (errorMsg === "username 필드를 추가해주세요 :)") {
      setError("id", {
        message: errorMsg,
      });
    } else if (errorMsg === "이미 사용 중인 아이디입니다.") {
      setError("id", {
        message: errorMsg,
      });
    } else if (
      errorMsg === "company_registration_number 필드를 추가해주세요 :)"
    ) {
      setError("businessNo", {
        message: errorMsg,
      });
    } else if (errorMsg === "이미 등록된 사업자등록번호입니다.") {
      setError("businessNo", {
        message: errorMsg,
      });
    }
  }, [errorMsg, setError]);

// ... 중략

  return (
    <>
      <ToggleBtn toggleType={toggleType} setToggleType={setToggleType} />
      <form onSubmit={handleSubmit(onSubmit)}>
        <S.JoinSection>
          <JoinInput
            label="아이디"
            forid="id"
            type="text"
            width={346}
            isButton={true}
            onClick={(e) => handleCheckId(getValues("id"), e)}
            {...register("id", {
              required: "필수 정보입니다.",
              pattern: {
                value: regExp.ID_REGEX,
                message:
                  "20자 이내의 영문,소문자, 대문자, 숫자만 사용 가능합니다.",
              },
              onChange: () => {
                if (idChecked) {
                  setIdChecked(false);
                }
              },
            })}
          />
          {idChecked ? (
            <S.SuccessTxt>{successMsg.Success}</S.SuccessTxt>
          ) : (
            RenderErrorMsg(errors.id)
          )}
// ... 중략

        </S.JoinSection>
        <CheckTerm
          register={register("checkbox")}
          children="호두샵의 이용약관 및 개인정보처리방침에 대해 동의합니다"
        />
        <S.JoinBtn type="submit" size="md" disabled={!isJoinValid}>
          가입하기
        </S.JoinBtn>
      </form>
    </>
  );
}

export { JoinForm };

 

2. 해결 코드

- 에러메세지를 렌더링 하는 코드를 useEffect 로 따로 감싸지 말고, 버튼을 클릭할때마다 즉, 

handleCheckId 를 클릭할 때마다 에러메세지가 렌더링 하게 해준다. 

// id 중복 확인 검증
  const handleCheckId = async (
    id: string,
    e: React.MouseEvent<HTMLButtonElement>
  ) => {
    e.preventDefault();
    const isValid = await trigger("id");
    if (!isValid) {
      return;
    }
    const resultAction = await dispatch(fetchIdValidate(id));
    if (fetchIdValidate.fulfilled.match(resultAction)) {
      setIdChecked(true);
    } else if (errorMsg === "이미 사용 중인 아이디입니다.") {
      setError("id", {
        message: errorMsg,
      });
    }
  };

  // 사업자등록 번호 인증
  const handleCheckBusiness = async (
    businessNo: string,
    e: React.MouseEvent<HTMLButtonElement>
  ) => {
    e.preventDefault();
    const isValid = await trigger("businessNo");
    if (!isValid) {
      return;
    }
    const resultAction = await dispatch(fetchBusinessValidate(businessNo));
    if (fetchBusinessValidate.fulfilled.match(resultAction)) {
      setBusinessChecked(true);
    }
  };

  // id 중복 확인 & 사업자 등록 번호 검증 에러메세지
  useEffect(() => {
    if (errorMsg === "company_registration_number 필드를 추가해주세요 :)") {
      setError("businessNo", {
        message: errorMsg,
      });
    } else if (errorMsg === "이미 등록된 사업자등록번호입니다.") {
      setError("businessNo", {
        message: errorMsg,
      });
    }
  }, [errorMsg, setError]);

 

2️⃣두번째 버그

 

0. 버그 현황

- 첫번째 버그에서 버튼을 클릭할때마다 에러메세지가 렌더링 하는걸로 바꾸었는데, 

이번에는 중복확인 버튼을 2번 클릭해야지만 에러메세지가 렌더링 되는걸로 바뀌었다.

홈페이지 새로고침하고 처음 중복확인 버튼을 바로 눌렀을때는 에러메세지가 렌더링 되지않는 문제가 생겼다. 

- 더욱 웃긴것은 글자만 렌더링 되지 않고, input 창 밑에 small 태그가 새로 생기는 현상이 있었다. 

 

1. 버그 코드

 

2. 해결 코드

- 기본값을 설정해야하는 이유는?

코드를 살펴보면, errorMsg는 useAppSelector((state) => state.join.error)를 통해 Redux 상태에서 가져온 값이다.

handleCheckId 함수에서 fetchIdValidate 액션을 디스패치한 후, 반환된 resultAction에 따라 에러 메시지를 설정합니다.

그러나 경우에 따라 errorMsg 값이 아직 업데이트되지 않은 상태일 수 있다. 따라서 setError 함수를 호출할 때 errorMsg가 undefined이거나 빈 문자열이라면 기본값으로 "이미 사용 중인 아이디입니다."를 사용해야한다. 이렇게 하면, errorMsg가 올바르게 설정되지 않은 경우에도 사용자에게 에러 메시지를 표시할 수 있다.

errorMsg || "이미 사용 중인 아이디입니다."는 JavaScript의 논리 연산자인 ||를 사용하여 두 값 중 "참"으로 평가되는 첫 번째 값을 반환합니다. errorMsg가 존재하고 유효한 값이라면 그대로 사용되고, 그렇지 않으면 기본 메시지인 "이미 사용 중인 아이디입니다."가 사용된다. 이렇게 작성하면 예상치 못한 상황에서도 에러메세지가 렌더링 되도록 보장할 수 있다.

 

- errorMsg 값이 업데이트 되지 않은 상태일땐 언제일까?

 setError 호출 시점에 errorMsg 상태가 아직 업데이트되지 않았기 때문일 수 있습니다. 상태 업데이트는 비동기적으로 처리되기 때문에 이러한 현상이 발생할 수 있습니다.

errorMsg 값이 아직 업데이트되지 않은 상태는, Redux 상태가 변경되기 전에 setError 함수가 호출되는 경우이다.
이러한 상황은 다음과 같은 원인으로 발생할 수 있다.

1) 비동기 작업: handleCheckId 함수에서 fetchIdValidate 액션을 디스패치하고, 그 결과를 기다린 후에 에러 메시지를 설정하는 과정이 비동기적으로 진행된다. 따라서, 액션을 디스패치한 후 Redux 상태가 즉시 업데이트되지 않을 수 있습니다.

2) 컴포넌트 렌더링 순서: React는 가상 DOM을 사용하여 컴포넌트를 렌더링합니다. Redux 상태가 변경되면 해당 변경 사항을 가상 DOM에 반영한 후, 실제 DOM에 렌더링합니다. 이 과정에서 컴포넌트가 다시 렌더링되기 전에 setError 함수가 호출될 수 있다.

이러한 이유로 인해 errorMsg 값이 아직 업데이트되지 않은 상태에서 setError 함수가 호출될 수 있습니다. 이 때 기본 메시지를 사용하면 예상치 못한 상황에서도 에러 메시지가 렌더링 되도록 보장받을 수 있다.

- 그런데 console 창에 console.log(errorMsg) 를 표시해보면 이미 사용중인 아이디입니다. 라고 메세지가 뜨는데 왜 렌더링은 안될까?

console.log(errorMsg)가 "이미 사용 중인 아이디입니다."라는 메시지를 표시한다면, errorMsg 값이 업데이트된 상태이다. 그러나 이 메시지가 렌더링되지 않는 이유는 setError 함수가 호출되는 시점과 React 컴포넌트 렌더링 사이의 타이밍 차이 때문일 수 있다.
setError 함수가 호출되면, 에러 메시지가 errors 객체에 저장됩니다. 그러나 React는 상태 변경을 감지한 후에 즉시 컴포넌트를 다시 렌더링하지 않을 수 있습니다. 대신, 여러 상태 변경을 한 번에 묶어서 처리하거나, 성능 최적화를 위해 렌더링을 지연시킬 수 있습니다.
이러한 이유로 인해, console.log(errorMsg)가 메시지를 표시하는 시점과 실제로 컴포넌트가 다시 렌더링되는 시점 사이에 차이가 발생할 수 있습니다. 이 차이 때문에 에러 메시지가 즉시 렌더링되지 않을 수 있습니다.