From c913c2d1f6b98d88661f9ad288da0e065b9e4af2 Mon Sep 17 00:00:00 2001 From: wallace Date: Thu, 6 Nov 2025 19:27:27 +0900 Subject: [PATCH] =?UTF-8?q?=ED=9A=8C=EC=9B=90=EA=B0=80=EC=9E=85=20?= =?UTF-8?q?=ED=99=94=EB=A9=B4=20=EC=8B=A0=EC=84=A4=EC=84=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/login/page.tsx | 2 +- app/register/page.tsx | 645 +++++++++++++++++++++++------------------- 2 files changed, 362 insertions(+), 285 deletions(-) diff --git a/app/login/page.tsx b/app/login/page.tsx index 0956d4e..5ff4151 100644 --- a/app/login/page.tsx +++ b/app/login/page.tsx @@ -194,7 +194,7 @@ export default function LoginPage() { {/* 하단 링크 버튼들 */}
회원가입 diff --git a/app/register/page.tsx b/app/register/page.tsx index 4f992b5..39bd25c 100644 --- a/app/register/page.tsx +++ b/app/register/page.tsx @@ -2,37 +2,48 @@ import { useState } from 'react'; import Link from 'next/link'; +import { useRouter } from 'next/navigation'; + +const imgRiCheckboxCircleLine = "http://localhost:3845/assets/e4c498605e2559d2764a3112ae9a9019e6ad798e.svg"; +const imgFormkitRadio = "http://localhost:3845/assets/ea30a9a80d95ced4bfb1174d3a8475a4a1dbbabb.svg"; +const imgAkarIconsRadio = "http://localhost:3845/assets/d772bd292f6dfddfcbd42cc1f22aa796ed671b11.svg"; +const imgLsiconDownFilled = "http://localhost:3845/assets/1c65a7143b6e9a0eee4b0878c33198a22da091cf.svg"; export default function RegisterPage() { + const router = useRouter(); const [formData, setFormData] = useState({ name: '', + phone: '', email: '', password: '', passwordConfirm: '', - phone: '', gender: 'male', - birthDate: '', - verificationCode: '', + birthYear: '', + birthMonth: '', + birthDay: '', + }); + + const [agreements, setAgreements] = useState({ + all: false, + age14: false, + terms: false, + privacy: false, }); const [errors, setErrors] = useState({ name: '', + phone: '', email: '', password: '', passwordConfirm: '', - phone: '', - birthDate: '', - verificationCode: '', }); - // 입력 필드 변경 핸들러 const handleChange = (e: React.ChangeEvent) => { const { name, value } = e.target; setFormData((prev) => ({ ...prev, [name]: value, })); - // 에러 초기화 if (errors[name as keyof typeof errors]) { setErrors((prev) => ({ ...prev, @@ -41,95 +52,64 @@ export default function RegisterPage() { } }; - // 이메일 유효성 검사 + const handlePhoneChange = (e: React.ChangeEvent) => { + let value = e.target.value.replace(/[^\d]/g, ''); + if (value.length > 11) value = value.slice(0, 11); + setFormData((prev) => ({ + ...prev, + phone: value, + })); + if (errors.phone) { + setErrors((prev) => ({ + ...prev, + phone: '', + })); + } + }; + + const handleAgreementChange = (key: keyof typeof agreements) => { + if (key === 'all') { + const newValue = !agreements.all; + setAgreements({ + all: newValue, + age14: newValue, + terms: newValue, + privacy: newValue, + }); + } else { + const newAgreements = { + ...agreements, + [key]: !agreements[key], + }; + setAgreements({ + ...newAgreements, + all: newAgreements.age14 && newAgreements.terms && newAgreements.privacy, + }); + } + }; + + const handleGenderChange = (gender: 'male' | 'female') => { + setFormData((prev) => ({ + ...prev, + gender, + })); + }; + const validateEmail = (email: string) => { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return emailRegex.test(email); }; - // 비밀번호 유효성 검사 (최소 8자, 영문/숫자 조합) const validatePassword = (password: string) => { - return password.length >= 8 && /[a-zA-Z]/.test(password) && /[0-9]/.test(password); + return password.length >= 8 && password.length <= 16 && /[a-zA-Z]/.test(password) && /[0-9]/.test(password); }; - // 전화번호 유효성 검사 (하이픈 없이) const validatePhone = (phone: string) => { - if (phone === '') return true; // 선택 항목 + if (phone === '') return true; const phoneRegex = /^010\d{8}$/; return phoneRegex.test(phone.replace(/[^\d]/g, '')); }; - // 폼 제출 전 유효성 검사 - const validateForm = () => { - const newErrors = { - name: '', - email: '', - password: '', - passwordConfirm: '', - phone: '', - }; - - let isValid = true; - - // 이름 검사 - if (formData.name.trim() === '') { - newErrors.name = '이름을 입력해주세요.'; - isValid = false; - } - - // 이메일 검사 - if (formData.email.trim() === '') { - newErrors.email = '이메일을 입력해주세요.'; - isValid = false; - } else if (!validateEmail(formData.email)) { - newErrors.email = '올바른 이메일 형식이 아닙니다.'; - isValid = false; - } - - // 비밀번호 검사 - if (formData.password === '') { - newErrors.password = '비밀번호를 입력해주세요.'; - isValid = false; - } else if (!validatePassword(formData.password)) { - newErrors.password = '비밀번호는 8자 이상, 영문과 숫자를 포함해야 합니다.'; - isValid = false; - } - - // 비밀번호 확인 검사 - if (formData.passwordConfirm === '') { - newErrors.passwordConfirm = '비밀번호 확인을 입력해주세요.'; - isValid = false; - } else if (formData.password !== formData.passwordConfirm) { - newErrors.passwordConfirm = '비밀번호가 일치하지 않습니다.'; - isValid = false; - } - - // 전화번호 검사 (선택 항목) - if (formData.phone && !validatePhone(formData.phone)) { - newErrors.phone = '올바른 전화번호 형식이 아닙니다. (예: 01012345678)'; - isValid = false; - } - - setErrors({ - ...newErrors, - birthDate: '', - verificationCode: '', - }); - return isValid; - }; - - // 전화번호 자동 포맷팅 (하이픈 없이) - const handlePhoneChange = (e: React.ChangeEvent) => { - let value = e.target.value.replace(/[^\d]/g, ''); - if (value.length > 11) value = value.slice(0, 11); - - setFormData((prev) => ({ - ...prev, - phone: value, - })); - }; - - // 인증번호 전송 핸들러 const handleSendVerificationCode = () => { if (!formData.email || !validateEmail(formData.email)) { setErrors((prev) => ({ @@ -142,269 +122,366 @@ export default function RegisterPage() { alert('인증번호가 전송되었습니다.'); }; - // 생년월일 핸들러 - const handleBirthDateChange = (e: React.ChangeEvent) => { - setFormData((prev) => ({ - ...prev, - birthDate: e.target.value, - })); + const validateForm = () => { + const newErrors = { + name: '', + phone: '', + email: '', + password: '', + passwordConfirm: '', + }; + + let isValid = true; + + if (formData.name.trim() === '') { + newErrors.name = '이름을 입력해주세요.'; + isValid = false; + } + + if (formData.phone && !validatePhone(formData.phone)) { + newErrors.phone = '올바른 전화번호 형식이 아닙니다.'; + isValid = false; + } + + if (formData.email.trim() === '') { + newErrors.email = '이메일을 입력해주세요.'; + isValid = false; + } else if (!validateEmail(formData.email)) { + newErrors.email = '올바른 이메일 형식이 아닙니다.'; + isValid = false; + } + + if (formData.password === '') { + newErrors.password = '비밀번호를 입력해주세요.'; + isValid = false; + } else if (!validatePassword(formData.password)) { + newErrors.password = '비밀번호는 8~16자의 영문/숫자를 포함해야 합니다.'; + isValid = false; + } + + if (formData.passwordConfirm === '') { + newErrors.passwordConfirm = '비밀번호 확인을 입력해주세요.'; + isValid = false; + } else if (formData.password !== formData.passwordConfirm) { + newErrors.passwordConfirm = '비밀번호가 일치하지 않습니다.'; + isValid = false; + } + + setErrors(newErrors); + return isValid; }; - // 폼 제출 핸들러 const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); + if (!agreements.age14 || !agreements.terms || !agreements.privacy) { + alert('필수 약관에 동의해주세요.'); + return; + } if (validateForm()) { - // 회원가입 완료 페이지로 이동 - // 실제로는 API 호출을 하여 회원가입 처리를 해야 합니다 - window.location.href = '/registercomplete'; + router.push('/registercomplete'); } }; - // 필수 항목 입력 여부 확인 - const canProceed = + const canProceed = formData.name.trim() !== '' && formData.email.trim() !== '' && formData.password !== '' && - formData.passwordConfirm !== ''; + formData.passwordConfirm !== '' && + agreements.age14 && + agreements.terms && + agreements.privacy; return ( -
-
- {/* 제목 */} -
-

회원가입

-
+
+ {/* 제목 */} +

+ 회원가입 +

- {/* 단계 표시 */} -
-
-
-

01

-

약관 동의

-
- > -
-

02

-

회원정보입력

-
- > -
-

03

-

회원가입완료

-
-
-
- - {/* 회원정보 입력 폼 */} -
- {/* 이름 */} -
- + {/* 회원정보 입력 폼 */} +
+ {/* 이름 */} +
+

+ 이름 +

+
- {errors.name && ( -

{errors.name}

- )}
+
- {/* 휴대폰 */} -
- + {/* 휴대폰 */} +
+

+ 휴대폰 +

+
- {errors.phone && ( -

{errors.phone}

- )}
+
- {/* 아이디(이메일) */} -
- -
- - -
- {errors.email && ( -

{errors.email}

- )} + {/* 이메일(아이디) */} +
+

+ 이메일(아이디) +

+
+
+ +
- {/* 비밀번호 */} -
- + {/* 비밀번호 */} +
+

+ 비밀번호 +

+
- {errors.password && ( -

{errors.password}

- )}
+
- {/* 비밀번호 확인 */} -
- + {/* 비밀번호 확인 */} +
+

+ 비밀번호 확인 +

+
- {errors.passwordConfirm && ( -

{errors.passwordConfirm}

- )}
+
- {/* 성별 */} -
- -
- - + {/* 성별 */} +
+

+ 성별 +

+
+
handleGenderChange('male')}> +
+ {formData.gender === 'male' ? ( + + ) : ( + + )} +
+

남성

+
+
handleGenderChange('female')}> +
+ {formData.gender === 'female' ? ( + + ) : ( + + )} +
+

여성

+
+
+
+ + {/* 생년월일 */} +
+

+ 생년월일 +

+
+
+

+ {formData.birthYear || '년도'} +

+
+ +
+
+
+

+ {formData.birthMonth || '월'} +

+
+ +
+
+
+

+ {formData.birthDay || '일'} +

+
+ +
+
+
+
+
+ + {/* 약관 동의 */} +
+
+ {/* 전체 동의 */} +
+
handleAgreementChange('all')}> +
+ {agreements.all ? ( +
+
+
+ ) : ( +
+ )} +
+

+ 모든 항목에 동의합니다. +

- {/* 생년월일 */} -
- -
- - - - + {/* 개별 약관 */} +
+
+
handleAgreementChange('age14')}> +
+ {agreements.age14 ? ( +
+
+
+ ) : ( +
+ )} +
+

+ 만 14세 이상입니다. (필수) +

+
+
+

+ 전체 +

+
- {errors.birthDate && ( -

{errors.birthDate}

- )} -
- {/* 버튼 영역 */} -
- - 이전 - - +
+
handleAgreementChange('terms')}> +
+ {agreements.terms ? ( +
+
+
+ ) : ( +
+ )} +
+

+ 이용 약관 동의 (필수) +

+
+
+

+ 전체 +

+
+
+ +
+
handleAgreementChange('privacy')}> +
+ {agreements.privacy ? ( +
+
+
+ ) : ( +
+ )} +
+

+ 개인정보 수집 및 이용 동의 (필수) +

+
+
+

+ 전체 +

+
+
- +
+
+ + {/* 버튼 영역 */} +
+ +

+ 돌아기기 +

+ +
{/* 카피라이트 */} -
-

Copyright ⓒ 2025 XL LMS. All rights reserved

+
+

+ Copyright ⓒ 2025 XL LMS. All rights reserved +

); } -