diff --git a/apps/cm/src/pages/auth/login/ui/UserSignupModal.tsx b/apps/cm/src/pages/auth/login/ui/UserSignupModal.tsx index 06193951e..4fb5fdf5d 100644 --- a/apps/cm/src/pages/auth/login/ui/UserSignupModal.tsx +++ b/apps/cm/src/pages/auth/login/ui/UserSignupModal.tsx @@ -3,7 +3,6 @@ import React, { useState, useEffect } from "react"; import { SignupClient } from "@/shared/api/signup-client"; import { BASE_URL, ENDPOINT } from "@/shared/config"; -import type { UserSaveData } from "@/entities/user"; import "./user-signup-modal.css"; interface UserSignupModalProps { @@ -31,8 +30,6 @@ interface UserSignupData { enoCd?: string; } - - // 익명 사용자용 CodeSelect 컴포넌트 interface AnonymousCodeSelectProps { upCd: string; @@ -42,24 +39,32 @@ interface AnonymousCodeSelectProps { placeholder?: string; } -function AnonymousCodeSelect({ upCd, value, onValueChange, disabled, placeholder = "선택" }: AnonymousCodeSelectProps) { - const [codes, setCodes] = useState>([]); +function AnonymousCodeSelect({ + upCd, + value, + onValueChange, + disabled, + placeholder = "선택", +}: AnonymousCodeSelectProps) { + const [codes, setCodes] = useState< + Array<{ lwpoCd: string; lwpoCdNm: string }> + >([]); const [isLoading, setIsLoading] = useState(false); useEffect(() => { const fetchCodes = async () => { if (!upCd) return; - + setIsLoading(true); try { const signupClient = SignupClient.getInstance({ - baseURL: BASE_URL + baseURL: BASE_URL, }); - + const response = await signupClient.get( - `${ENDPOINT.CMS_SERVICE.CODE}/${upCd}` + `${ENDPOINT.CMS_SERVICE.CODE}/${upCd}`, ); - + if (response.data && Array.isArray(response.data)) { setCodes(response.data); } else { @@ -94,7 +99,9 @@ function AnonymousCodeSelect({ upCd, value, onValueChange, disabled, placeholder } function UserSignupModal({ open, onClose, onSuccess }: UserSignupModalProps) { - const [currentStep, setCurrentStep] = useState<'agreement' | 'signup'>('agreement'); + const [currentStep, setCurrentStep] = useState<"agreement" | "signup">( + "agreement", + ); const [agreed, setAgreed] = useState(false); const [formData, setFormData] = useState({ userId: "", @@ -126,7 +133,7 @@ function UserSignupModal({ open, onClose, onSuccess }: UserSignupModalProps) { // 모달이 닫힐 때 초기화 useEffect(() => { if (!open) { - setCurrentStep('agreement'); + setCurrentStep("agreement"); setAgreed(false); setFormData({ userId: "", @@ -159,17 +166,19 @@ function UserSignupModal({ open, onClose, onSuccess }: UserSignupModalProps) { const handleNextStep = () => { if (agreed) { - setCurrentStep('signup'); + setCurrentStep("signup"); } }; - const handleInputChange = (e: React.ChangeEvent) => { + const handleInputChange = ( + e: React.ChangeEvent, + ) => { const { name, value } = e.target; - setFormData(prev => ({ ...prev, [name]: value })); - + setFormData((prev) => ({ ...prev, [name]: value })); + // 에러 초기화 if (errors[name]) { - setErrors(prev => ({ ...prev, [name]: "" })); + setErrors((prev) => ({ ...prev, [name]: "" })); } }; @@ -182,20 +191,27 @@ function UserSignupModal({ open, onClose, onSuccess }: UserSignupModalProps) { if (!formData.userId) newErrors.userId = "아이디를 입력해주세요."; if (!formData.userName) newErrors.userName = "이름을 입력해주세요."; if (!formData.password) newErrors.password = "비밀번호를 입력해주세요."; - if (!formData.confirmPassword) newErrors.confirmPassword = "비밀번호 확인을 입력해주세요."; + if (!formData.confirmPassword) + newErrors.confirmPassword = "비밀번호 확인을 입력해주세요."; if (!formData.pswdHnt) newErrors.pswdHnt = "비밀번호 힌트를 선택해주세요."; if (!formData.laeSeCd) newErrors.laeSeCd = "법인구분을 선택해주세요."; if (!formData.ogdpSeCd) newErrors.ogdpSeCd = "기관구분을 선택해주세요."; // 비밀번호 확인 - if (formData.password && formData.confirmPassword && formData.password !== formData.confirmPassword) { + if ( + formData.password && + formData.confirmPassword && + formData.password !== formData.confirmPassword + ) { newErrors.confirmPassword = "비밀번호가 일치하지 않습니다."; } // 비밀번호 정규식 검증 - const passwordRegex = /^(?=.*[a-zA-Z])(?=.*\d)(?=.*[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]).{8,20}$/; + const passwordRegex = + /^(?=.*[a-zA-Z])(?=.*\d)(?=.*[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]).{8,20}$/; if (formData.password && !passwordRegex.test(formData.password)) { - newErrors.password = "비밀번호는 8~20자, 영문/숫자/특수기호를 모두 포함해야 합니다."; + newErrors.password = + "비밀번호는 8~20자, 영문/숫자/특수기호를 모두 포함해야 합니다."; } // 이메일 형식 검증 @@ -204,8 +220,10 @@ function UserSignupModal({ open, onClose, onSuccess }: UserSignupModalProps) { } // 중복 체크 검증 - if (idAvailable === false) newErrors.userId = "이미 사용 중인 아이디입니다."; - if (emailAvailable === false) newErrors.email = "이미 사용 중인 이메일입니다."; + if (idAvailable === false) + newErrors.userId = "이미 사용 중인 아이디입니다."; + if (emailAvailable === false) + newErrors.email = "이미 사용 중인 이메일입니다."; setErrors(newErrors); const isValid = Object.keys(newErrors).length === 0; @@ -215,7 +233,7 @@ function UserSignupModal({ open, onClose, onSuccess }: UserSignupModalProps) { const checkUserIdDuplicate = async () => { if (!formData.userId) { - setErrors(prev => ({ ...prev, userId: "아이디를 입력해주세요." })); + setErrors((prev) => ({ ...prev, userId: "아이디를 입력해주세요." })); return; } @@ -226,19 +244,25 @@ function UserSignupModal({ open, onClose, onSuccess }: UserSignupModalProps) { }); const response = await signupClient.post( `${ENDPOINT.USER_SERVICE.USERS}/existsUserId`, - { userId: formData.userId } + { userId: formData.userId }, ); const exists = response.data; setIdAvailable(!exists); if (exists) { - setErrors(prev => ({ ...prev, userId: "이미 사용 중인 아이디입니다." })); + setErrors((prev) => ({ + ...prev, + userId: "이미 사용 중인 아이디입니다.", + })); } else { - setErrors(prev => ({ ...prev, userId: "" })); + setErrors((prev) => ({ ...prev, userId: "" })); } } catch (error) { console.error("아이디 중복 체크 오류:", error); - setErrors(prev => ({ ...prev, userId: "아이디 중복 체크 중 오류가 발생했습니다." })); + setErrors((prev) => ({ + ...prev, + userId: "아이디 중복 체크 중 오류가 발생했습니다.", + })); } finally { setIsCheckingId(false); } @@ -246,7 +270,7 @@ function UserSignupModal({ open, onClose, onSuccess }: UserSignupModalProps) { const checkEmailDuplicate = async () => { if (!formData.email) { - setErrors(prev => ({ ...prev, email: "이메일을 입력해주세요." })); + setErrors((prev) => ({ ...prev, email: "이메일을 입력해주세요." })); return; } @@ -257,19 +281,25 @@ function UserSignupModal({ open, onClose, onSuccess }: UserSignupModalProps) { }); const response = await signupClient.post( `${ENDPOINT.USER_SERVICE.USERS}/exists`, - { email: formData.email } + { email: formData.email }, ); const exists = response.data; setEmailAvailable(!exists); if (exists) { - setErrors(prev => ({ ...prev, email: "이미 사용 중인 이메일입니다." })); + setErrors((prev) => ({ + ...prev, + email: "이미 사용 중인 이메일입니다.", + })); } else { - setErrors(prev => ({ ...prev, email: "" })); + setErrors((prev) => ({ ...prev, email: "" })); } } catch (error) { console.error("이메일 중복 체크 오류:", error); - setErrors(prev => ({ ...prev, email: "이메일 중복 체크 중 오류가 발생했습니다." })); + setErrors((prev) => ({ + ...prev, + email: "이메일 중복 체크 중 오류가 발생했습니다.", + })); } finally { setIsCheckingEmail(false); } @@ -279,7 +309,7 @@ function UserSignupModal({ open, onClose, onSuccess }: UserSignupModalProps) { e.preventDefault(); console.log("회원가입 버튼 클릭됨 - handleSubmit"); console.log("현재 폼 데이터:", formData); - + if (!validateForm()) { console.log("폼 검증 실패"); return; @@ -311,23 +341,23 @@ function UserSignupModal({ open, onClose, onSuccess }: UserSignupModalProps) { console.log("SignupClient 생성 및 API 호출:", { baseURL: BASE_URL, endpoint: ENDPOINT.USER_SERVICE.USER_JOIN, - data: signupData + data: signupData, }); - + const signupClient = SignupClient.getInstance({ - baseURL: BASE_URL + baseURL: BASE_URL, }); const response = await signupClient.post( ENDPOINT.USER_SERVICE.USER_JOIN, - signupData + signupData, ); console.log("회원가입 성공:", response.data); - + // 성공 시 모달 닫기 및 콜백 호출 onSuccess(); onClose(); - + // 폼 초기화 setFormData({ userId: "", @@ -351,13 +381,12 @@ function UserSignupModal({ open, onClose, onSuccess }: UserSignupModalProps) { setErrors({}); setIdAvailable(null); setEmailAvailable(null); - } catch (error: unknown) { console.error("회원가입 오류:", error); if (error instanceof Error) { - setErrors(prev => ({ ...prev, submit: error.message })); + setErrors((prev) => ({ ...prev, submit: error.message })); } else { - setErrors(prev => ({ ...prev, submit: "회원가입에 실패했습니다." })); + setErrors((prev) => ({ ...prev, submit: "회원가입에 실패했습니다." })); } } finally { setIsLoading(false); @@ -366,40 +395,56 @@ function UserSignupModal({ open, onClose, onSuccess }: UserSignupModalProps) { if (!open) return null; - return ( -
-
+ return ( +
+
-

{currentStep === 'agreement' ? '개인정보제공동의' : '회원가입'}

- +

+ {currentStep === "agreement" ? "개인정보제공동의" : "회원가입"} +

+
- - {currentStep === 'agreement' ? ( + + {currentStep === "agreement" ? (

개인정보 수집 및 이용 동의

회원가입을 위해 다음과 같이 개인정보를 수집 및 이용합니다.

- +

1. 수집하는 개인정보 항목

    -
  • 필수항목: 아이디, 이름, 비밀번호, 비밀번호 힌트, 법인구분, 기관구분
  • -
  • 선택항목: 이메일, 휴대폰번호, 전화번호, 주소, 기관코드, 상세기관코드, 사업자등록번호
  • +
  • + 필수항목: 아이디, 이름, 비밀번호, 비밀번호 힌트, 법인구분, + 기관구분 +
  • +
  • + 선택항목: 이메일, 휴대폰번호, 전화번호, 주소, 기관코드, + 상세기관코드, 사업자등록번호 +
- +

2. 개인정보의 수집 및 이용목적

  • 회원가입 및 관리
  • 서비스 제공 및 운영
  • 고객상담 및 문의응답
- +

3. 개인정보의 보유 및 이용기간

-

회원탈퇴 시까지 (단, 관련 법령에 따라 보존이 필요한 경우 해당 기간까지)

- +

+ 회원탈퇴 시까지 (단, 관련 법령에 따라 보존이 필요한 경우 해당 + 기간까지) +

+

4. 동의 거부권 및 거부 시 불이익

-

개인정보 수집 및 이용에 대한 동의를 거부할 수 있으며, 동의 거부 시 회원가입이 제한됩니다.

+

+ 개인정보 수집 및 이용에 대한 동의를 거부할 수 있으며, 동의 거부 + 시 회원가입이 제한됩니다. +

- +
- +
- {idAvailable === true && 사용 가능한 아이디입니다.} - {errors.userId && {errors.userId}} + {idAvailable === true && ( + + 사용 가능한 아이디입니다. + + )} + {errors.userId && ( + + {errors.userId} + + )}
@@ -474,7 +527,11 @@ function UserSignupModal({ open, onClose, onSuccess }: UserSignupModalProps) { placeholder="이름을 입력하세요" disabled={isLoading} /> - {errors.userName && {errors.userName}} + {errors.userName && ( + + {errors.userName} + + )}
@@ -500,8 +557,16 @@ function UserSignupModal({ open, onClose, onSuccess }: UserSignupModalProps) { {isCheckingEmail ? "확인중..." : "중복확인"}
- {emailAvailable === true && 사용 가능한 이메일입니다.} - {errors.email && {errors.email}} + {emailAvailable === true && ( + + 사용 가능한 이메일입니다. + + )} + {errors.email && ( + + {errors.email} + + )} @@ -518,7 +583,11 @@ function UserSignupModal({ open, onClose, onSuccess }: UserSignupModalProps) { maxLength={20} disabled={isLoading} /> - {errors.password && {errors.password}} + {errors.password && ( + + {errors.password} + + )} @@ -535,7 +604,11 @@ function UserSignupModal({ open, onClose, onSuccess }: UserSignupModalProps) { maxLength={20} disabled={isLoading} /> - {errors.confirmPassword && {errors.confirmPassword}} + {errors.confirmPassword && ( + + {errors.confirmPassword} + + )} @@ -545,11 +618,17 @@ function UserSignupModal({ open, onClose, onSuccess }: UserSignupModalProps) { setFormData(prev => ({ ...prev, pswdHnt: value }))} + onValueChange={(value: string) => + setFormData((prev) => ({ ...prev, pswdHnt: value })) + } disabled={isLoading} placeholder="비밀번호 힌트를 선택하세요" /> - {errors.pswdHnt && {errors.pswdHnt}} + {errors.pswdHnt && ( + + {errors.pswdHnt} + + )} @@ -566,14 +645,18 @@ function UserSignupModal({ open, onClose, onSuccess }: UserSignupModalProps) { maxLength={100} disabled={isLoading} /> - {errors.pswdAns && {errors.pswdAns}} + {errors.pswdAns && ( + + {errors.pswdAns} + + )}

연락처 정보

- +
@@ -587,7 +670,11 @@ function UserSignupModal({ open, onClose, onSuccess }: UserSignupModalProps) { maxLength={11} disabled={isLoading} /> - {errors.mphn && {errors.mphn}} + {errors.mphn && ( + + {errors.mphn} + + )}
@@ -604,14 +691,18 @@ function UserSignupModal({ open, onClose, onSuccess }: UserSignupModalProps) { maxLength={11} disabled={isLoading} /> - {errors.telno && {errors.telno}} + {errors.telno && ( + + {errors.telno} + + )}

주소 정보

- +
@@ -625,7 +716,11 @@ function UserSignupModal({ open, onClose, onSuccess }: UserSignupModalProps) { maxLength={5} disabled={isLoading} /> - {errors.zip && {errors.zip}} + {errors.zip && ( + + {errors.zip} + + )}
@@ -642,7 +737,11 @@ function UserSignupModal({ open, onClose, onSuccess }: UserSignupModalProps) { maxLength={200} disabled={isLoading} /> - {errors.addr && {errors.addr}} + {errors.addr && ( + + {errors.addr} + + )}
@@ -659,25 +758,35 @@ function UserSignupModal({ open, onClose, onSuccess }: UserSignupModalProps) { maxLength={200} disabled={isLoading} /> - {errors.daddr && {errors.daddr}} + {errors.daddr && ( + + {errors.daddr} + + )}

기관 정보

- +
setFormData(prev => ({ ...prev, laeSeCd: value }))} + onValueChange={(value: string) => + setFormData((prev) => ({ ...prev, laeSeCd: value })) + } disabled={isLoading} placeholder="법인구분을 선택하세요" /> - {errors.laeSeCd && {errors.laeSeCd}} + {errors.laeSeCd && ( + + {errors.laeSeCd} + + )}
@@ -687,11 +796,17 @@ function UserSignupModal({ open, onClose, onSuccess }: UserSignupModalProps) { setFormData(prev => ({ ...prev, ogdpSeCd: value }))} + onValueChange={(value: string) => + setFormData((prev) => ({ ...prev, ogdpSeCd: value })) + } disabled={isLoading} placeholder="기관구분을 선택하세요" /> - {errors.ogdpSeCd && {errors.ogdpSeCd}} + {errors.ogdpSeCd && ( + + {errors.ogdpSeCd} + + )}
@@ -708,7 +823,11 @@ function UserSignupModal({ open, onClose, onSuccess }: UserSignupModalProps) { maxLength={10} disabled={isLoading} /> - {errors.instSeCd && {errors.instSeCd}} + {errors.instSeCd && ( + + {errors.instSeCd} + + )} @@ -725,21 +844,27 @@ function UserSignupModal({ open, onClose, onSuccess }: UserSignupModalProps) { maxLength={10} disabled={isLoading} /> - {errors.dtlInstCd && {errors.dtlInstCd}} + {errors.dtlInstCd && ( + + {errors.dtlInstCd} + + )} {errors.submit && (
-
{errors.submit}
+
+ {errors.submit} +
)}