유효성 검사

  • 유효성 검사가 필요한 페이지
    • 회원가입, 로그인, 유저의 마이페이지, 약사의 마이페이지

회원가입시 유효성 검사

유효성 검사 내용

  • 이메일 : 5글자 이상으로 이메일 형식으로 입력해야 회원가입 가능
  • 비밀번호 : 문자, 숫자, 특수문자 조합으로 8자 이상으로 조합해야 회원가입 가능
  • 닉네임 : 닉네임에는 공백이 들어가면 안되게 구현
  • input창이 하나라도 비워져 있으면 회원가입 안되게 구현
  • 이용약관에 동의(체크박스에 체크)하지 않으면 회원가입이 안되게 구현
  • 약사회원의 경우 바로 회원가입이 되지 않고, 자신의 약국주소가 실제로 약국주소와 동일해야 회원가입이 되도록 구현
  • 또한 약사회원의 경우는 바로 회원가입이 되지않고, 사업자등록증 사진과 약사면허증사징을 관리자가 보고, 회원가입을 허용할 경우 회원가입이 되도록 구현할 계획

화면 구현

  • 해당 input창을 클릭하면 focus-within 을 사용해 보라색으로 box-shadow를 주고, 유효성검사로 인해 에러가 나면 에러메세지가 input창 아래에 빨간 글씨로 에러메세지와 focus-within 을 사용해 빨간색으로 box-shadow를 주었다.
  • input창이 하나라도 비워져 있다면, '모든 항목을 입력해주세요'를
    input창에 하나라도 유효성검사에 통과하지 못했다면, '항목을 다시 확인해주세요'를
    이용약관에 동의(체크박스에 체크)하지 않고 회원가입 버튼을 누르면 '위치정보 사용에 동의해주세요'를
    react-toastify를 사용해 alert창을 띄우게 구현하였다.

유효성 검사 전체 코드

  • 일반회원 유효성검사는 약사회원의 유효성검사에 다 포함되므로 약사회원의 유효성검사만 전체 코드를 작성
//Validation.tsx 
function validateEmail(email: string) {
  const reg = /^[A-Za-z0-9_\.\-]+@[A-Za-z0-9\-]+\.[A-Za-z0-9\-]+/;
  if (email.length >= 5) {
    if (reg.test(email) === false) {
      return true;
    }
  }
  return false;
}

function validatePassword(password: string) {
  const reg = /^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{8,}$/;
  if (password.length >= 4) {
    if (reg.test(password) === false) {
      return true;
    }
  }
  return false;
}

function validateName(nickname: string) {
  const reg = /\s/g;
  if (reg.test(nickname)) {
    return true;
  }
  return false;
}

function validatePasswordCheck(password: string, passwordConfirm: string) {
  if (password.length >= 1 && passwordConfirm.length >= 1) {
    if (password !== passwordConfirm) {
      return true;
    }
  }
  return false;
}

const validators = {
  validateEmail,
  validatePassword,
  validateName,
  validatePasswordCheck,
};
export { validators };

//UserSignUpForms.tsx
//일반회원 회원가입시 유효성검사

//생략

export default function UserSignUpForms() {
  const [signForm, setSignForms] = useState({
    email: "",
    password: "",
    name: "",
    address: "",
  });

  const [error, setError] = useState({
    email: false,
    password: false,
    name: false,
  });

  const [checks, setChecks] = useState(false);

  const FORM_FIELD_NAMES = {
    EMAIL: "email",
    PASSWORD: "password",
    NAME: "name",
    ADDRESS: "address",
  };

  const changeEmailHandler = (e: React.ChangeEvent<HTMLInputElement>) => {
    const { name, value } = e.target;
    setSignForms({
      ...signForm,
      [name]: value,
    });

    let errors;
    if (name === FORM_FIELD_NAMES.EMAIL) {
      errors = validators.validateEmail(value);
    }
    setError({
      ...error,
      [name]: errors,
    });
  };

  const changePasswordHandler = (e: React.ChangeEvent<HTMLInputElement>) => {
    const { name, value } = e.target;
    setSignForms({
      ...signForm,
      [name]: value,
    });
    let errors;
    if (name === FORM_FIELD_NAMES.PASSWORD) {
      errors = validators.validatePassword(value);
    }
    setError({
      ...error,
      [name]: errors,
    });
  };

  const changeNameHandler = (e: React.ChangeEvent<HTMLInputElement>) => {
    const { name, value } = e.target;
    setSignForms({
      ...signForm,
      [name]: value,
    });
    let errors;
    if (name === FORM_FIELD_NAMES.NAME) {
      errors = validators.validateName(value);
    }
    setError({
      ...error,
      [name]: errors,
    });
  };

  const changeAddressHandler = (e: React.ChangeEvent<HTMLInputElement>) => {
    const { name, value } = e.target;
    setSignForms({
      ...signForm,
      [name]: value,
    });
  };
  const checkHandler = (e: React.ChangeEvent<HTMLInputElement>) => {
    setChecks(e.target.checked);
  };
  const navigate = useNavigate();
  const onSubmit: any = (e: { preventDefault: () => void; target: HTMLFormElement | undefined }) => {
    e.preventDefault();
    const formData = new FormData(e.target);
    const email = formData.get(FORM_FIELD_NAMES.EMAIL);
    const password = formData.get(FORM_FIELD_NAMES.PASSWORD);
    const name = formData.get(FORM_FIELD_NAMES.NAME);
    const address = formData.get(FORM_FIELD_NAMES.ADDRESS);

    if (!email || !password || !name || !address) {
      return toast.error("모든 항목을 입력해주세요", {
        position: "top-center",
        autoClose: 2000,
        hideProgressBar: false,
        closeOnClick: true,
        pauseOnHover: true,
        draggable: true,
        progress: undefined,
        theme: "light",
      });
    }
    if (error.email === true || error.password === true || error.name === true) {
      return toast.error("항목을 다시 확인해주세요", {
        position: "top-center",
        autoClose: 2000,
        hideProgressBar: false,
        closeOnClick: true,
        pauseOnHover: true,
        draggable: true,
        progress: undefined,
        theme: "light",
      });
    }
    if (checks === false) {
      return toast.error("위치정보 사용에 동의해주세요", {
        position: "top-center",
        autoClose: 2000,
        hideProgressBar: false,
        closeOnClick: true,
        pauseOnHover: true,
        draggable: true,
        progress: undefined,
        theme: "light",
      });
    }

    const data = {
      name,
      address,
      email,
      password,
    };

    //! POST : 일반회원 회원가입 
    const postSignUp = async () => {
      try {
        await axios({
          url: APIS.POST_USER_SIGNUP_JWT,
          method: "post",
          data: data,
        });
      } catch (error) {
        console.log(error);
      }
    };
    postSignUp();
    navigate("/login");
  };

  return (
    <Container>
      <SignUpForm onSubmit={onSubmit}>
        <InputContainer className={`${error.email ? "red" : null}`}>
          <BsPersonCircle className="inputImage" aria-hidden="true" />
          <label htmlFor="useremail-input"></label>
          <SignUpInput
            type={"email"}
            name={FORM_FIELD_NAMES.EMAIL}
            placeholder={"이메일을 입력하세요."}
            value={signForm.email}
            onChange={changeEmailHandler}
          />
        </InputContainer>
        <ErrorAlert Error={error.email} ErrorText={"이메일 형식이 올바르지 않습니다."} />
        <InputContainer className={`${error.password ? "red" : null}`}>
          <AiOutlineLock className="inputImage" aria-hidden="true" />
          <label htmlFor="userpassword-input"></label>
          <SignUpInput
            type={"password"}
            name={FORM_FIELD_NAMES.PASSWORD}
            placeholder={"비밀번호를 입력하세요."}
            value={signForm.password}
            onChange={changePasswordHandler}
          />
        </InputContainer>
        <ErrorAlert Error={error.password} ErrorText={"문자 숫자 특수문자 조합 8자 이상으로 조합해주세요."} />
        <InputContainer className={`${error.name ? "red" : null}`}>
          <FaUserEdit className="inputImage" aria-hidden="true" />
          <label htmlFor="username-input"></label>
          <SignUpInput
            type={"text"}
            name={FORM_FIELD_NAMES.NAME}
            placeholder={"닉네임을 입력하세요."}
            value={signForm.name}
            onChange={changeNameHandler}
          />
        </InputContainer>
        <ErrorAlert Error={error.name} ErrorText={"이름에는 공백이 들어갈 수 없습니다."} />
        <InputContainer>
          <FaMapMarkerAlt className="inputImage" aria-hidden="true" />
          <label htmlFor="useraddress-input"></label>
          <SignUpInput
            readOnly
            type={"text"}
            name={FORM_FIELD_NAMES.ADDRESS}
            placeholder={"주소를 입력하세요."}
            value={signForm.address}
            onChange={changeAddressHandler}
          />
          <UserAdress setSignForms={setSignForms} />
        </InputContainer>
        <CheckContainer>
          <Check type="checkbox" onChange={checkHandler} checked={checks} />
          <span className="checkbox_content">
            회원가입시, 사용자의 현재 위치를 사용하는 것에 동의하는 것으로 간주됩니다.
          </span>
        </CheckContainer>
        <button className="signup_button" type="submit">
          회원가입
        </button>
      </SignUpForm>
    </Container>
  );
}

//스타일컴포넌트 부분 생략


로그인시 유효성 검사

유효성 검사 내용

  • 이메일 : 5글자 이상으로 이메일 형식으로 입력해야 로그인 가능
  • 비밀번호 : 문자, 숫자, 특수문자 조합으로 8자 이상으로 입력해야 로그인 가능

    추후에 백엔드분들이 관리자 아이디와 비밀번호를 만들때, 전달드린 유효성검사 기준을 고려안하시고 비밀번호를 만드심...
    => 로그인할때는 비밀번호 유효성를 넣지 않음....😢😢😢

화면 구현

  • 해당 input창을 클릭하면 focus-within 을 사용해 보라색으로 box-shadow를 주고, 유효성검사로 인해 에러가 나면 에러메세지가 input창 아래에 빨간 글씨로 에러메세지와 focus-within 을 사용해 빨간색으로 box-shadow를 주었다.
  • input창이 하나라도 비워져 있다면, '모든 항목을 입력해주세요'를
    react-toastify를 사용해 alert창을 띄우게 구현하였다.
  • 로그인할 때 아이디와 비밀번호가 회원가입시 입력했던 값과 다르면 'ID 또는 비밀번호가 일치하지 않습니다.'를 react-toastify를 사용해 alert창을 띄우게 구현하였다.

유효성 검사 전체 코드

//나머지 코드 생략
 const [loginForm, setLoginForms] = useState({
    email: "",
    password: "",
  });

  const [error, setError] = useState({
    email: false,
    password: false,
  });

  const FORM_FIELD_NAMES = {
    EMAIL: "email",
    PASSWORD: "password",
  };

  const changeEmailHandler = (e: React.ChangeEvent<HTMLInputElement>) => {
    const { name, value } = e.target;
    setLoginForms({
      ...loginForm,
      [name]: value,
    });

    let errors;
    if (name === FORM_FIELD_NAMES.EMAIL) {
      errors = validators.validateEmail(value);
    }
    setError({
      ...error,
      [name]: errors,
    });
  };

  const changePasswordHandler = (e: React.ChangeEvent<HTMLInputElement>) => {
    const { name, value } = e.target;
    setLoginForms({
      ...loginForm,
      [name]: value,
    });
  };

  const navigate = useNavigate();
  const dispatch = useAppDispatch();

  const user = useAppSelector((state: any) => {
    return state.userInfo.response;
  });
  const onSubmit: any = (e: { preventDefault: () => void; target: HTMLFormElement | undefined }) => {
    e.preventDefault();
    const formData = new FormData(e.target);
    const email = formData.get(FORM_FIELD_NAMES.EMAIL);
    const password = formData.get(FORM_FIELD_NAMES.PASSWORD);

    if (!email || !password) {
      return toast.error("모든 항목을 입력해주세요", {
        position: "top-center",
        autoClose: 2000,
        hideProgressBar: false,
        closeOnClick: true,
        pauseOnHover: true,
        draggable: true,
        progress: undefined,
        theme: "light",
      });
    }
    if (error.email === true || error.password === true) {
      return toast.error("항목을 다시 확인해주세요", {
        position: "top-center",
        autoClose: 2000,
        hideProgressBar: false,
        closeOnClick: true,
        pauseOnHover: true,
        draggable: true,
        progress: undefined,
        theme: "light",
      });
    }

    //! POST : 로그인 - JWT
    const postLogin = async () => {
      await axios
        .post(APIS.POST_LOGIN_JWT, { email: email, password: password })
        .then((res) => {
          let accessToken = res.headers.authorization;
          let refreshToken = res.headers.refresh;
          setLocalStorage("access_token", accessToken);
          setLocalStorage("refresh_token", refreshToken);
          let token = getLocalStorage("access_token");
          axios.defaults.headers.common.Authorization = token;
          dispatch(get(res.data));
          return res;
        })
        .then((res) => {
          if (res.data.userType == "관리자") {
            return navigate("/admin-reports");
          }
          window.location.replace("/");
        })
        .catch((error) => {
          if (error?.response?.status === 401) {
            toast.error("ID 또는 비밀번호가 일치하지 않습니다.", {
              position: "top-center",
              autoClose: 2000,
              hideProgressBar: false,
              closeOnClick: true,
              pauseOnHover: true,
              draggable: true,
              progress: undefined,
              theme: "light",
            });
          }
          console.log(error);
        });
    };
    postLogin();
  };
profile
함께 일하는 프론트엔드 개발자 이성은입니다🐥

0개의 댓글