반응형
실제로는 더 많은 입력값과 검증이 필요하겠지만, 검증함수의 로직을 파악할 용도로 간단하게 작성하였다.
전체코드는 아래와 같다.
import { useState } from "react";
import { useDispatch } from "react-redux";
import { loginPostAsync } from "../../slices/loginSlice";
import { useNavigate } from "react-router-dom";
import useCustomRegister from "../../hooks/useCustomRegister";
import { Link } from "react-router-dom";
import AddressInput from '../common/AddressInput';
const RegisterComponent = () => {
const initState = {
email: '',
pw: '',
confirmPw: '', // 비밀번호 확인 필드 추가
username: '',
zonecode: '', // 우편번호
address: '', // 주소
detailedAddress: '' // 상세주소
}
const [registerParam, setRegisterParam] = useState({...initState})
const {doRegister, moveToPath} = useCustomRegister()
const [isOpen, setIsOpen] = useState(false) // 주소찾기 모달 열기/닫기 상태
const [errors, setErrors] = useState({
email: '',
pw: '',
confirmPw: '',
username: '',
address: '',
detailedAddress: ''
})
const validateField = (name, value, pwValue) => {
let error = ''
switch (name) {
case 'email':
if (!value) {
error = '이메일은 필수 입력항목입니다.'
} else if (!/\S+@\S+\.\S+/.test(value)) {
error = '유효한 이메일 주소를 입력해주세요.'
}
break
case 'pw':
if (!value) {
error = '비밀번호는 필수 입력항목입니다.'
} else if (value.length < 6) {
error = '비밀번호는 최소 6자 이상이어야 합니다.'
}
break
case 'confirmPw':
if (value && value !== pwValue) {
error = '비밀번호가 일치하지 않습니다.'
}
break
case 'address':
if (!value) {
error = '주소는 필수 입력항목입니다.'
}
break
case 'detailedAddress':
if(!value) {
error = '상세 주소는 필수 입력항목입니다.'
}
break
default:
break
}
return error
}
// const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const handleChange = (e) => {
const {name, value} = e.target
setRegisterParam({
...registerParam,
[name]: value
});
let error
if (name === 'confirmPw') { // confirmPw 필드에 입력할 때마다 현재 pw 필드의 값과 비교하여 에러 체크
error = validateField(name, value, registerParam.pw)
} else { // confirm 필드가 비어있을 때는 에러 메시지를 표시하지 않음
error = validateField(name, value)
}
setErrors({
...errors,
[name]: error
})
// pw가 변경되었을 때도 confirmPw 필드의 값을 재검증하여 에러 상태 업데이트
if(name === 'pw' && registerParam.confirmPw) {
const confirmPwError = validateField('confirmPw', registerParam.confirmPw, value)
setErrors(prev => ({
...prev,
confirmPw: confirmPwError
}))
}
}
const handleAddressComplete = (data) => {
console.log("주소 선택 완료:", data);
setRegisterParam({
...registerParam,
zonecode: data.zonecode,
address: data.address
});
setIsOpen(false); // 주소찾기 모달 닫기
}
const toggleModal = () => {
console.log("토글 모달 호출됨, 현재 상태:", isOpen);
setIsOpen(!isOpen); // 주소찾기 모달 열기/닫기 토글
}
const handleClickRegister = () => {
const newErrors ={}
Object.keys(registerParam).forEach(key => {
const error = validateField(key, registerParam[key])
if(error) {
newErrors[key] = error
}
})
if(Object.keys(newErrors).length > 0) {
setErrors(newErrors)
return
}
// 회원가입 처리 로직
doRegister(registerParam)
}
return (
<div className="border-2 border-sky-200 mt-10 m-2 p-4 overflow-auto" style={{maxHeight: '100vh'}}>
<div className="flex justify-center">
<div className="text-4xl m-4 p-4 font-extrabold text-blue-500">
회원가입
</div>
</div>
<div className="flex justify-center">
<div className="relative mb-4 flex w-full flex-wrap items-stretch">
<div className="w-1/5 py-3 pr-3 text-left font-bold">Email</div>
<input className="w-4/5 p-3 rounded-r border
border-solid border-neutral-500 shadow-md"
name="email"
type={'text'}
placeholder="이메일주소"
value={registerParam.email}
onChange={handleChange}>
</input>
<div className="w-1/5 pr-3 text-left"></div>
{errors.email && <div className="w-4/5 text-red-600 mt-1">{errors.email}</div>}
</div>
</div>
<div className="flex justify-center">
<div className="relative mb-4 flex w-full flex-wrap items-stretch">
<div className="w-1/5 py-3 pr-3 text-left font-bold">
Password
</div>
<input className="w-4/5 p-3 rounded-r border border-solid
border-neutral-500 shadow-md"
name="pw"
type={'password'}
placeholder="비밀번호는 최소 6자리이상 입력"
value={registerParam.pw}
onChange={handleChange}>
</input>
<div className="w-1/5 pr-3 text-left"></div>
{errors.pw && <div className="w-4/5 text-red-600 mt-1">{errors.pw}</div>}
</div>
</div>
<div className="flex justify-center">
<div className="relative mb-4 flex w-full flex-wrap items-stretch">
<div className="w-1/5 py-0 pr-3 text-left font-bold">
Confirm Password
</div>
<input
className="w-4/5 p-3 rounded-r border border-solid border-neutral-500 shadow-md"
name="confirmPw"
type="password"
placeholder="비밀번호를 한번더 입력해 주세요"
value={registerParam.confirmPw}
onChange={handleChange}>
</input>
<div className="w-1/5 pr-3 text-left"></div>
{errors.confirmPw && <div className="w-4/5 text-red-600 mt-1">{errors.confirmPw}</div>}
</div>
</div>
<div className="flex justify-center">
<div className="relateve mb-4 flex w-full flex-wrap items-stretch">
<div className="w-1/5 py-3 pr-3 text-left font-bold">
Username
</div>
<input className="w-4/5 p-3 rounded-r border border-solid
border-neutral-500 shadow-md"
name="username"
type={'text'}
placeholder="닉네임"
value={registerParam.username}
onChange={handleChange}>
</input>
<div className="w-1/5 pr-3 text-left"></div>
{errors.username && <div className="w-4/5 text-red-600 mt-1">{errors.username}</div>}
</div>
</div>
<div className="flex justify-center">
<div className="relateve mb-4 flex w-full flex-wrap items-stretch">
<div className="w-1/5 py-3 pr-3 text-left font-bold">
Address
</div>
<div className="w-4/5 flex">
<input className="mr-3 p-3 rounded-r border border-solid
border-neutral-500 shadow-md"
name="zonecode"
type={'text'}
placeholder="우편번호"
value={registerParam.zonecode}
readOnly>
</input>
<button
className="w-36 rounded bg-green-600 text-xl text-white p-2"
type="button"
onClick={toggleModal}
>
주소찾기
</button>
<AddressInput
isOpen={isOpen}
onComplete={handleAddressComplete}
toggleHandler={toggleModal}
/>
</div>
</div>
</div>
<div className="flex justify-center">
<div className="relateve mb-4 flex w-full flex-wrap items-stretch">
<div className="w-1/5 p-3 text-left font-bold">
</div>
<input className="w-4/5 p-3 rounded-r border border-solid
border-neutral-500 shadow-md"
name="address"
placeholder="주소"
type={'text'}
value={registerParam.address}
readOnly>
</input>
<div className="w-1/5 pr-3 text-left"></div>
{errors.address && <div className="w-4/5 text-red-600 mt-1">{errors.address}</div>}
</div>
</div>
<div className="flex justify-center">
<div className="relateve mb-4 flex w-full flex-wrap items-stretch">
<div className="w-1/5 p-3 text-left font-blod">
</div>
<input className="w-4/5 p-3 rounded-r border border-solid
border-neutral-500 shadow-md"
name="detailedAddress"
type={'text'}
placeholder="상세주소"
value={registerParam.detailedAddress}
onChange={handleChange}>
</input>
<div className="w-1/5 pr-3 text-left"></div>
{errors.detailedAddress && <div className="w-4/5 text-red-600 mt-1">{errors.detailedAddress}</div>}
</div>
</div>
<div className="flex justify-center mt-8">
<div className="relative mb-1 flex w-full justify-center">
<div className="w-1/5 p-3 text-left font-blod">
</div>
<div className="w-4/5 flex justify-center font-bold">
<button className="rounded p-4 w-full bg-blue-500 text-xl text-white"
onClick={handleClickRegister}>
가입하기
</button>
</div>
</div>
</div>
<div className="flex justify-center">
<div className="w-4/5 p-6 flex justify-center font-bold">
<Link to="/member/login" className="text-blue-500">
로그인 페이지로 이동
</Link>
</div>
</div>
</div>
)
}
export default RegisterComponent;
1. 상태 추가: 먼저, 각 필드의 에러 메시지를 저장할 상태를 추가합니다.
const [errors, setErrors] = useState({
email: '',
pw: '',
confirmPw: '',
username: '',
detailedAddress: ''
});
2. 검증 함수 생성: 입력값을 검증하는 함수를 만듭니다.
const validateField = (name, value, pwValue) => {
let error = '';
switch (name) {
case 'email':
if (!value) {
error = '이메일은 필수 입력항목입니다.';
} else if (!/\S+@\S+\.\S+/.test(value)) {
error = '유효한 이메일 주소를 입력해주세요.';
}
break;
case 'pw':
if (!value) {
error = '비밀번호는 필수 입력항목입니다.';
} else if (value.length < 6) {
error = '비밀번호는 최소 6자 이상이어야 합니다.';
}
break;
case 'confirmPw':
if (value && value !== pwValue) {
error = '비밀번호가 일치하지 않습니다.';
}
break;
case 'username':
if (!value) {
error = '사용자 이름은 필수 입력항목입니다.';
}
break;
case 'detailedAddress':
if (!value) {
error = '상세 주소는 필수 입력항목입니다.';
}
break;
default:
break;
}
return error;
};
3. handleChange 함수 수정: 입력값이 변경될 때마다 검증을 수행하도록 수정합니다.
- const handleChange = (e) => { ... }:
- handleChange는 화살표 함수로 정의된 이벤트 핸들러입니다.
- e는 이벤트 객체(event object)로, 이 객체는 폼 입력 필드의 변경 이벤트에 대한 정보를 담고 있습니다.
- const {name, value} = e.target:
- 구조 분해 할당(destructuring assignment)을 사용하여 e.target 객체로부터 name과 value 속성을 추출합니다.
- e.target은 이벤트가 발생한 요소(이 경우 입력 필드)를 가리킵니다.
- name은 입력 필드의 이름 속성값이고, value는 입력된 값입니다.
- setRegisterParam({ ...registerParam, [name]: value }):
- setRegisterParam은 상태 업데이트 함수로, useState 훅에 의해 생성되었습니다.
- 새로운 상태 객체를 생성하여 registerParam 상태를 업데이트합니다.
- ...registerParam은 기존 상태 객체를 펼쳐서 복사합니다. 즉, 현재 상태의 모든 속성을 새로운 객체에 복사합니다.
- [name]: value는 계산된 속성 이름(computed property name)을 사용하여 동적으로 속성명을 설정합니다. 여기서 name은 입력 필드의 이름이고, value는 해당 필드의 입력값입니다.
- 이를 통해 입력 필드의 이름에 해당하는 상태 속성을 입력값으로 업데이트합니다.
const handleChange = (e) => {
const {name, value} = e.target
setRegisterParam({
...registerParam,
[name]: value
});
let error
if (name === 'confirmPw') { // confirmPw 필드에 입력할 때마다 현재 pw 필드의 값과 비교하여 에러 체크
error = validateField(name, value, registerParam.pw)
} else { // confirm 필드가 비어있을 때는 에러 메시지를 표시하지 않음
error = validateField(name, value)
}
setErrors({
...errors,
[name]: error
})
// pw가 변경되었을 때도 confirmPw 필드의 값을 재검증하여 에러 상태 업데이트
if(name === 'pw' && registerParam.confirmPw) {
const confirmPwError = validateField('confirmPw', registerParam.confirmPw, value)
setErrors(prev => ({
...prev,
confirmPw: confirmPwError
}))
}
}
4. 폼 제출 시 전체 검증: 폼 제출 시 모든 필드를 한 번 더 검증합니다.
const handleClickRegister = () => {
const newErrors = {};
Object.keys(registerParam).forEach(key => {
const error = validateField(key, registerParam[key]);
if (error) {
newErrors[key] = error;
}
});
if (Object.keys(newErrors).length > 0) {
setErrors(newErrors);
return;
}
// 회원가입 처리 로직
doRegister(registerParam);
};
5. 에러 메시지 표시: 각 입력 필드 아래에 에러 메시지를 표시합니다. 예를 들어
<div className="flex justify-center">
<div className="relative mb-4 flex w-full flex-wrap items-stretch">
<div className="w-1/5 py-3 pr-3 text-left font-bold">
Password
</div>
<input className="w-4/5 p-3 rounded-r border border-solid
border-neutral-500 shadow-md"
name="pw"
type={'password'}
placeholder="비밀번호는 최소 6자리이상 입력"
value={registerParam.pw}
onChange={handleChange}>
</input>
<div className="w-1/5 pr-3 text-left"></div>
{errors.pw && <div className="w-4/5 text-red-600 mt-1">{errors.pw}</div>}
</div>
</div>
반응형