focus-within
을 사용해 보라색으로 box-shadow
를 주고, 유효성검사로 인해 에러가 나면 에러메세지가 input창 아래에 빨간 글씨로 에러메세지와 focus-within
을 사용해 빨간색으로 box-shadow
를 주었다.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>
);
}
//스타일컴포넌트 부분 생략
추후에 백엔드분들이 관리자 아이디와 비밀번호를 만들때, 전달드린 유효성검사 기준을 고려안하시고 비밀번호를 만드심...
=> 로그인할때는 비밀번호 유효성를 넣지 않음....😢😢😢
focus-within
을 사용해 보라색으로 box-shadow
를 주고, 유효성검사로 인해 에러가 나면 에러메세지가 input창 아래에 빨간 글씨로 에러메세지와 focus-within
을 사용해 빨간색으로 box-shadow
를 주었다.react-toastify
를 사용해 alert창을 띄우게 구현하였다.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();
};