이메일 주소 또는 비밀번호를 입력하고 입력창에서 포커스를 잃으면 바로 유효성 검사가 진행되고 통과하지 못한 경우 경고 문구가 각 입력창 하단에 표시됩니다.
중복된 이메일을 회원가입 폼에서 체크하고 사용 가능한지도 알려주는 일반적인 회원가입 폼이다. 포커스를 잃으면 바로 사용 가능한지 여부를 알려주는 기능을 넣어야 한다. 버튼을 클릭했을 때는 이미 검증완료된 값을 서버에 전달해야 한다.
그래서 onSubmit이 아니라 onBlur에서 HTTP 요청을 보내면 인풋에서 포커스 잃을 때 검증가능하다. 반응이 좀 느린 것 같은 느낌이 든다..?
로그인 폼에서는 onSubmit 속성에서 HTTP 요청을 보냈어서 쉬울 줄 알았는데 API 연결은 한번에 잘 안되는 것 같다.
이메일 input에만 onBlur에 HTTP요청 코드를 줬는데, 비밀번호 input에도 똑같이 적용할까 고민했다. 내 생각에는 보통 비밀번호까지 입력하면 바로 엔터를 누르거나 버튼을 누르기도 하고 처음부터 비밀번호가 몇자 이상이어야 하는지 유저에게 정보를 제공하는 편이 더 나은것 같아서 onBlur를 주지 않았다. 하지만 명세에는 비밀번호 input에도 포커스를 잃으면 검증결과가 나오게 만들어 달라고 나와있다. 어떤 결정이 더 나은지는 회원가입 기능을 더 업그레이드 시키면서 생각해봐야될 것 같다.
import { useRef, useState, useEffect, useContext } from 'react';
import AuthContext from "../../context/AuthProvider";
import axios from 'axios';
import { API_URL } from '../../constants/defaultUrl';
import styled from 'styled-components';
import Button from '../../components/login/Button';
const Join = (props) => {
const { setAuth } = useContext(AuthContext);
const emailRef = useRef();
const errorRef = useRef();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [errorMessage, setErrorMessage] = useState('');
const [success, setSuccess] = useState(false);
const [notMatchError, setNotMatchError] = useState('');
useEffect(() => {
emailRef.current.focus();
}, []);
useEffect(() => {
setErrorMessage('');
}, [email, password]);
const handleSubmit = async (event) => {
event.preventDefault();
try {
const reqData = {
user: {
email: email
}
};
const config = {
headers: {
"Content-Type": "application/json",
},
};
const response = await axios.post(
`${API_URL}/user/emailvalid`,
reqData,
config
);
const accessToken = response?.data?.accessToken;
const roles = response?.data?.roles;
setAuth({ email, password, roles, accessToken });
setEmail('');
setPassword('');
setSuccess(true);
if (response?.data?.status === 422) {
setSuccess(false);
setNotMatchError(response.data.message);
}
} catch (error) {
if (!error?.response) {
setErrorMessage('서버가 응답하지 않습니다.');
} else if (error.response?.status === 400) {
setErrorMessage('이메일 또는 비밀번호가 일치하지 않습니다.');
} else if (error.response?.status === 401) {
setErrorMessage('Unauthorized');
} else {
setErrorMessage('로그인 실패');
}
errorRef.current.focus();
}
};
const handleOnBlur = async (event) => {
try {
const reqData = {
user: {
email: email
}
};
const config = {
headers: {
"Content-Type": "application/json",
},
};
const response = await axios.post(
`${API_URL}/user/emailvalid`,
reqData,
config
);
setSuccess(true);
if (response?.data?.message === "이미 가입된 이메일 주소 입니다.") {
setSuccess(false);
setNotMatchError('*' + response.data.message);
} else if (response?.data?.message === "사용 가능한 이메일 입니다.") {
setSuccess(false);
setNotMatchError('*' + response.data.message);
}
} catch (error) {
console.error(error);
errorRef.current.focus();
}
};
// 버튼 활성상태 관리
const [buttonOn, setButtonOn] = useState(false);
const emailRegex = /^[a-zA-Z0-9+-\_.]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/;
const isPassedJoin = () => {
return emailRegex.test(email) && password.length > 5 ? setButtonOn(true) : setButtonOn(false);
};
return (
<>
<Wrapper>
<Title>이메일로 회원가입</Title>
<Form onSubmit={handleSubmit}>
<div>
<Label htmlFor='email'>이메일
<Input
type='email'
id='email'
ref={emailRef}
autoComplete='off'
onChange={(event) => setEmail(event.target.value)}
required
onKeyUp={isPassedJoin}
onBlur={handleOnBlur}
/>
{(email.length > 5)
&& !emailRegex.test(email)
&& <ErrorMessage>*올바르지 않은 이메일 형식입니다.</ErrorMessage>
}
{notMatchError && <ErrorMessage>{notMatchError}</ErrorMessage>}
</Label>
</div>
<div>
<Label htmlFor='password'>비밀번호
<Input
type='password'
id='password'
onChange={(event) => setPassword(event.target.value)}
require
value={password}
required
onKeyUp={isPassedJoin}
/>
{(password.length < 6) && <ErrorMessage>*비밀번호는 6자 이상이어야 합니다.</ErrorMessage>}
</Label>
</div>
<Button
type='submit'
text='다음'
style={{ backgroundColor: buttonOn ? '#00BCD4' : '#B2EBF2' , border: '0px', fontWeight: '500', fontSize: '14px', color: 'white'}}
/>
</Form>
</Wrapper>
</>
);
}
export default Join;