[React] 회원가입 submit 핸들러 구현하기 (useEffect)

·2024년 2월 23일
0

React

목록 보기
6/8
post-thumbnail

React와 axios로 회원가입 기능을 구현하고 있었다.

문제 상황

명세에 따르면 해당 사용자 전화번호가 이미 존재하는 경우 회원가입을 할 수 없다. 그러나 validation을 위한 api는 별도로 존재하지 않았다. (중복 ID를 확인하는 api는 존재해서 ID는 submit 이전에 걸러낼 수 있었다.) 때문에 submit을 할 때 사용자 전화번호가 이미 존재하는 경우와 존재하지 않는 경우에 대한 컨트롤이 필요했다.

기존 코드

  const [signupForm, setSignupForm] = useState({
    username: '',
    password: '',
    password2: '',
    phone_number: '',
    name: '',
  });
  const [phoneNumberErr, setPhoneNumberErr] = useState('');
  const [phoneNumberIsValid, setPhoneNumberIsValid] = useState(false);

  const inputChangeHandler = (e) => {
    const { name, value } = e.target;
    setSignupForm({
      ...signupForm,
      [name]: value,
    });
  };

const phoneNumberHandler = () => {
    const regExp = /^(01[016789]{1})-?[0-9]{3,4}-?[0-9]{4}$/;
    if (!signupForm.phone_number) {
      setPhoneNumberErr('휴대폰번호는 필수 항목입니다.');
      setPhoneNumberIsValid(false);
    } else if (signupForm.phone_number.length > 11) {
      setPhoneNumberErr('휴대폰번호는 11자리 이하여야 합니다.');
      setPhoneNumberIsValid(false);
    } else if (!regExp.test(signupForm.phone_number)) {
      setPhoneNumberErr(
        '핸드폰 번호는 01*으로 시작하는 11자리 숫자여야 합니다.'
      );
      setPhoneNumberIsValid(false);
    } else {
      setPhoneNumberErr('');
      setPhoneNumberIsValid(true);
    }
  };

  useEffect(() => {
    setPhoneNumberErr();
  }, [signupForm.phone_number]);

const signupHandler = async (userData) => {
    try {
      const res = await postSignup(userData);
      console.log(res);
    } catch (err) {
      if (err.response) {
        console.log(err.response.data);
        console.log(err.response.status);
        console.log(err.response.headers);
      } else {
        console.log(`Error: ${err.message}`);
      }
    }
  };


  const submitHandler = (e) => {
    e.preventDefault();
    const userData = {
      username: signupForm.username,
      password: signupForm.password,
      password2: signupForm.password2,
      phone_number: signupForm.phone_number,
      name: signupForm.name,
    };
    if (
      usernameIsValid &&
      pwIsValid &&
      pwCheckIsValid &&
      nameIsValid &&
      phoneNumberIsValid
    ) {
      signupHandler(userData);
      alert('환영합니다!');
      navigate('/login');
    }
  };

...
      <ContInputForm onSubmit={submitHandler}>
        <div>
          <Input
            label='휴대폰번호'
            type='text'
            name='phone_number'
            placeholder='휴대폰 번호를 입력해주세요.'
            min='11'
            defaultValue={signupForm.phone_number}
            onChange={inputChangeHandler}
            onBlur={phoneNumberHandler}
            message={phoneNumberErr}
          />
        </div>
  <Button
          disabled={
            !signupForm.username ||
            !signupForm.password ||
            !signupForm.password2 ||
            !signupForm.name ||
            !signupForm.phone_number
          }
        >
          {'오픈 마켓 시작하기'}
        </Button>
  </ContInputForm>

phoneNumberHandler를 통해 01*으로 시작하는 11자리 숫자를 올바르게 입력했다면, phoneNumberIsValid은 true이다.
submitHandler에서 signupHandler를 작동시켰을 때 에러가 뜨더라도(해당 사용자 전화번호가 중복인 경우) phoneNumberIsValid가 true였기 때문에 회원가입이 정상적으로 이루어지지 않았음에도 '환영합니다!'와 함께 로그인 화면으로 전환되었다.

이전에 비슷한 문제 상황에서 useState를 사용했던 게 떠올라 이번에도 useState를 이용해 문제를 해결하기로 했다.
전화번호 validation api가 없는 관계로 불가피하게 signupHandler 에러 부분에서 submit을 컨트롤해보기로 했다.

개선된 코드

  const [signupForm, setSignupForm] = useState({
    username: '',
    password: '',
    password2: '',
    phone_number: '',
    name: '',
  });
  const [phoneNumberErr, setPhoneNumberErr] = useState('');
  const [phoneNumberIsValid, setPhoneNumberIsValid] = useState(false);
// submit을 제출할 수 있는 상태(중복 전화번호 여부)를 구분하는 useState를 추가했다.
  const [submit, setSubmit] = useState(false);

  const inputChangeHandler = (e) => {
    const { name, value } = e.target;
    setSignupForm({
      ...signupForm,
      [name]: value,
    });
  };

const phoneNumberHandler = () => {
    const regExp = /^(01[016789]{1})-?[0-9]{3,4}-?[0-9]{4}$/;
    if (!signupForm.phone_number) {
      setPhoneNumberErr('휴대폰번호는 필수 항목입니다.');
      setPhoneNumberIsValid(false);
    } else if (signupForm.phone_number.length > 11) {
      setPhoneNumberErr('휴대폰번호는 11자리 이하여야 합니다.');
      setPhoneNumberIsValid(false);
    } else if (!regExp.test(signupForm.phone_number)) {
      setPhoneNumberErr(
        '핸드폰 번호는 01*으로 시작하는 11자리 숫자여야 합니다.'
      );
      setPhoneNumberIsValid(false);
    } else {
      setPhoneNumberErr('');
      setPhoneNumberIsValid(true);
    }
  };

  useEffect(() => {
    setPhoneNumberErr();
  }, [signupForm.phone_number]);

const signupHandler = async (userData) => {
    try {
	// res가 성공하는 경우 setSubmit(true) 값을 줬다.
      const res = await postSignup(userData);
      setSubmit(true);
    } catch (err) {
      // 에러 메세지로 '해당 사용자 전화번호는 이미 존재합니다.'가 뜨는 경우 setSubmit(false) 값을 줬다.
      if (err.response) {
        console.log(err.response.data);
        if (
          err.response.data.phone_number[0] ===
          '해당 사용자 전화번호는 이미 존재합니다.'
        ) {
          setPhoneNumberErr('해당 사용자 전화번호는 이미 존재합니다.');
          setSubmit(false);
        }
        console.log(err.response.status);
        console.log(err.response.headers);
      } else {
        console.log(`Error: ${err.message}`);
      }
    }
  };


  const submitHandler = (e) => {
    e.preventDefault();
    const userData = {
      username: signupForm.username,
      password: signupForm.password,
      password2: signupForm.password2,
      phone_number: signupForm.phone_number,
      name: signupForm.name,
    };
    if (
      usernameIsValid &&
      pwIsValid &&
      pwCheckIsValid &&
      nameIsValid &&
      phoneNumberIsValid
    ) {
      signupHandler(userData);
    }
  };

// useEffect로 submit 값이 바뀔 때마다 확인하고, 중복 번호가 아니어서 submit을 해도 될 때(true)만 정상적으로 로그인할 수 있으며 로그인 화면으로 전환될 수 있도록 했다.
  useEffect(() => {
    setSubmit();
    if (submit) {
      alert('환영합니다!');
      navigate('/login');
    }
  }, [submit]);

...
      <ContInputForm onSubmit={submitHandler}>
        <div>
          <Input
            label='휴대폰번호'
            type='text'
            name='phone_number'
            placeholder='휴대폰 번호를 입력해주세요.'
            min='11'
            defaultValue={signupForm.phone_number}
            onChange={inputChangeHandler}
            onBlur={phoneNumberHandler}
            message={phoneNumberErr}
          />
        </div>
  <Button
          disabled={
            !signupForm.username ||
            !signupForm.password ||
            !signupForm.password2 ||
            !signupForm.name ||
            !signupForm.phone_number
          }
        >
          {'오픈 마켓 시작하기'}
        </Button>
  </ContInputForm>

문제 없이 회원가입 완료!

로그인 페이지에서도 submit을 제출할 때 회원정보가 없는 경우에 비슷한 문제가 있었는데, 이 방법을 통해 문제를 해결했다.

다른 문제 상황에 직면했을 때 이전에 익힌 useEffect를 바로 적용해서 해결할 수 있게 된 것이 뿌듯했다.

profile
주니어 프론트엔드 웹 개발자 🐛

0개의 댓글