유저의 편의성을 높이기 위해 버튼 클릭을 최소화한 깔끔한 UI의 회원가입 폼을 만들어보았다!
✔️ 가입하기 버튼을 누르지 않아도 사용자가 입력할 때 마다 유효성 검사 실패 에러메시지가 노출된다.
✔️ 아이디 중복 확인 버튼 없이 사용자가 아이디를 입력하면 이미 사용중인 아이디인지, 사용 가능한 아이디인지 바로 에러메시지가 노출된다.
✔️ 모든 조건을 충족하면 회원가입 요청을 서버에 보내서 회원가입을 완료한다!
const [id, setId] = useState('');
const [password, setPassword] = useState('');
const [confirm, setConfirm] = useState('');
const [idError, setIdError] = useState('');
const [passwordError, setPasswordError] = useState('');
const [confirmError, setConfirmError] = useState('');
const [isIdCheck, setIsIdCheck] = useState(false); // 중복 검사를 했는지 안했는지
const [isIdAvailable, setIsIdAvailable] = useState(false); // 아이디 사용 가능한지 아닌지
아이디, 비밀번호, 비밀번호 확인을 위한 상태값과 각각의 에러메시지를 관리하기 위해 useState를 사용하여 상태를 설정하였다. 또한 중복 검사 여부 상태값 관리를 위한 설정도 해주었다.
const onChangeIdHandler = (e) => {
const idValue = e.target.value;
setId(idValue);
idCheckHandler(idValue);
}
const onChangePasswordHandler = (e) => {
const { name, value } = e.target;
if (name === 'password') {
setPassword(value);
passwordCheckHandler(value, confirm);
} else {
setConfirm(value);
passwordCheckHandler(password, value);
}
}
사용자가 아이디와 비밀번호를 input창에 입력할 때마다 해당 값의 변화를 감지하고 업데이트하는 onChangeHandler를 구현하였다. 여기서 아이디, 비밀번호 유효성 검사 Handler를 호출한다.
const idCheckHandler = async (id) => {
const idRegex = /^[a-z\d]{5,10}$/;
if (id === '') {
setIdError('아이디를 입력해주세요.');
setIsIdAvailable(false);
return false;
} else if (!idRegex.test(id)) {
setIdError('아이디는 5~10자의 영소문자, 숫자만 입력 가능합니다.');
setIsIdAvailable(false);
return false;
}
try {
const responseData = await idDuplicateCheck(id);
if (responseData) {
setIdError('사용 가능한 아이디입니다.');
setIsIdCheck(true);
setIsIdAvailable(true);
return true;
} else {
setIdError('이미 사용중인 아이디입니다.');
setIsIdAvailable(false);
return false;
}
} catch (error) {
alert('서버 오류입니다. 관리자에게 문의하세요.');
console.error(error);
return false;
}
}
먼저 유효성 검사를 실시하고, 유효한 형식의 아이디인 경우 서버에 중복 검사를 요청하여 중복 여부에 따른 에러메시지를 띄워준다.
const passwordCheckHandler = (password, confirm) => {
const passwordRegex = /^[a-z\d!@*&-_]{8,16}$/;
if (password === '') {
setPasswordError('비밀번호를 입력해주세요.');
return false;
} else if (!passwordRegex.test(password)) {
setPasswordError('비밀번호는 8~16자의 영소문자, 숫자, !@*&-_만 입력 가능합니다.');
return false;
} else if (confirm !== password) {
setPasswordError('');
setConfirmError('비밀번호가 일치하지 않습니다.');
return false;
} else {
setPasswordError('');
setConfirmError('');
return true;
}
}
비밀번호 유효성 검사를 실시한다.
const signupHandler = async (e) => {
e.preventDefault();
const idCheckresult = await idCheckHandler(id);
if (idCheckresult) setIdError('');
else return;
if (!isIdCheck || !isIdAvailable) {
alert('아이디 중복 검사를 해주세요.');
return;
}
const passwordCheckResult = passwordCheckHandler(password, confirm);
if (passwordCheckResult) { setPasswordError(''); setConfirmError(''); }
else return;
try {
const responseData = await signup(id, password, confirm);
if (responseData) {
localStorage.setItem('loginId', id);
setOpenModal(true);
} else {
alert('회원가입에 실패하였습니다. 다시 시도해주세요.');
}
} catch (error) {
alert('회원가입에 실패하였습니다. 다시 시도해주세요.');
console.error(error);
}
}
아이디와, 비밀번호 유효성 검사를 실시하는 함수를 호출하고 모든 결과값이 true일 때 회원가입 요청을 서버에 전송한다.
return (
<>
<SignupPageHeader />
<Wrapper>
<Form onSubmit={signupHandler}>
<InputWrapper>
<InputContainer>
<label htmlFor='id'>아이디</label>
<Input
onChange={onChangeIdHandler}
type="text"
id='id'
name='id'
value={id}
placeholder='아이디 입력'
theme='underLine'
maxLength={10}
/>
{idError && <small className={isIdAvailable ? 'idAvailable' : ''}>{idError}</small>}
</InputContainer>
<InputContainer>
<label htmlFor='id'>비밀번호</label>
<Input
onChange={onChangePasswordHandler}
type="password"
id='password'
name='password'
value={password}
placeholder='비밀번호 입력'
theme='underLine'
maxLength={16}
/>
{passwordError && <small>{passwordError}</small>}
<Input
onChange={onChangePasswordHandler}
type="password"
id='confirm'
name='confirm'
value={confirm}
placeholder='비밀번호 확인'
theme='underLine'
maxLength={16}
/>
{confirmError && <small>{confirmError}</small>}
</InputContainer>
</InputWrapper>
<ButtonContainer>
<button type='submit'>가입하기</button>
</ButtonContainer>
</Form>
{setOpenModal ? openModal && (<SignupModal />) : null}
</Wrapper>
</>
)
마지막으로 위와 같이 UI 구현하면 끝!
아래처럼 잘 작동한다😄