Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
227 changes: 196 additions & 31 deletions src/app/register/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ export default function RegisterPage() {
description: '',
});

// 유효성 검증 에러 상태
const [userErrors, setUserErrors] = useState<Record<string, string>>({});
const [companyErrors, setCompanyErrors] = useState<Record<string, string>>({});

// 회원가입 스토어에서 상태와 메서드 가져오기
const {
isRegistering,
Expand All @@ -40,22 +44,107 @@ export default function RegisterPage() {
resetRegisterSuccess
} = useRegisterStore();

// 전화번호 형식화 함수
const formatPhoneNumber = (value: string): string => {
// 숫자만 추출
const numbers = value.replace(/[^\d]/g, '');

if (numbers.length <= 3) {
return numbers;
} else if (numbers.length <= 7) {
return `${numbers.slice(0, 3)}-${numbers.slice(3)}`;
} else {
// 지역번호가 2자리인 경우(02)
if (numbers.startsWith('02') && numbers.length <= 10) {
return `${numbers.slice(0, 2)}-${numbers.slice(2, 6)}-${numbers.slice(6)}`;
}
// 일반적인 경우(3-4-4)
else {
return `${numbers.slice(0, 3)}-${numbers.slice(3, 7)}-${numbers.slice(7, 11)}`;
}
}
};

// 사용자 정보 입력 필드 변경 처리
const handleUserInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
const { name, value } = e.target;
setUserFormData((prev) => ({
...prev,
[name]: value
}));

// 전화번호 필드인 경우 형식화 적용
if (name === 'phone') {
const formattedPhone = formatPhoneNumber(value);
setUserFormData((prev) => ({
...prev,
[name]: formattedPhone
}));
} else {
setUserFormData((prev) => ({
...prev,
[name]: value
}));
}

// 오류 상태 초기화 (사용자가 수정 중)
if (userErrors[name]) {
const newErrors = { ...userErrors };
delete newErrors[name];
setUserErrors(newErrors);
}
};

// 회사 정보 입력 필드 변경 처리
const handleCompanyInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
const { name, value } = e.target;
setCompanyFormData((prev) => ({
...prev,
[name]: value
}));

// 전화번호 필드인 경우 형식화 적용
if (name === 'phone') {
const formattedPhone = formatPhoneNumber(value);
setCompanyFormData((prev) => ({
...prev,
[name]: formattedPhone
}));
} else {
setCompanyFormData((prev) => ({
...prev,
[name]: value
}));
}

// 오류 상태 초기화 (사용자가 수정 중)
if (companyErrors[name]) {
const newErrors = { ...companyErrors };
delete newErrors[name];
setCompanyErrors(newErrors);
}
};

// 다음 버튼 클릭 핸들러
const handleNextButtonClick = () => {
// 필수 필드 비어있는지 확인
const newErrors: Record<string, string> = {};

// 각 필수 필드 검사
if (!userFormData.name.trim()) newErrors.name = "이름을 입력해주세요";
if (!userFormData.email.trim()) newErrors.email = "이메일을 입력해주세요";
if (!userFormData.password.trim()) newErrors.password = "비밀번호를 입력해주세요";
if (!userFormData.phone.trim()) newErrors.phone = "전화번호를 입력해주세요";
if (!userFormData.jobTitle.trim()) newErrors.jobTitle = "직책을 입력해주세요";

// 형식 검사 (값이 있는 경우만)
if (userFormData.email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(userFormData.email)) {
newErrors.email = "올바른 이메일 형식이 아닙니다";
}

if (userFormData.phone && !/^\d{2,3}-\d{3,4}-\d{4}$/.test(userFormData.phone)) {
newErrors.phone = "올바른 전화번호 형식이 아닙니다 (예: 010-1234-5678)";
}

// 오류 상태 업데이트
setUserErrors(newErrors);

// 오류가 없을 때만 탭 변경
if (Object.keys(newErrors).length === 0) {
setActiveTab('company');
}
};

// 뒤로가기 처리
Expand All @@ -65,7 +154,10 @@ export default function RegisterPage() {

// 탭 변경 처리
const handleTabChange = (tab: string) => {
setActiveTab(tab);
// 회사 정보 탭에서 사용자 정보 탭으로 이동할 때는 항상 허용
if (tab === 'user') {
setActiveTab(tab);
}
};

// useEffect를 사용하여 등록 성공 시 리다이렉션 처리
Expand All @@ -83,21 +175,58 @@ export default function RegisterPage() {
}
}, [registerSuccess, router]);

// 회사 정보 유효성 검증
const validateCompanyInfo = () => {
const newErrors: Record<string, string> = {};

// 필수 필드는 회사명과 전화번호
if (!companyFormData.name.trim()) newErrors.name = "회사명을 입력해주세요";
if (!companyFormData.phone.trim()) newErrors.phone = "회사 전화번호를 입력해주세요";

// 이메일 형식 검사 (입력된 경우)
if (companyFormData.email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(companyFormData.email)) {
newErrors.email = "올바른 이메일 형식이 아닙니다";
}

// 전화번호 형식 검사
if (companyFormData.phone && !/^\d{2,3}-\d{3,4}-\d{4}$/.test(companyFormData.phone)) {
newErrors.phone = "올바른 전화번호 형식이 아닙니다 (예: 02-1234-5678)";
}

setCompanyErrors(newErrors);
return Object.keys(newErrors).length === 0;
};

// 폼 제출 처리
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();

// 회사 정보 유효성 검증
if (!validateCompanyInfo()) {
return;
}

// 두 탭의 필수 입력값이 모두 채워졌는지 확인
const requestData: RootUserRequest = {
user: userFormData,
company: companyFormData
};

console.log('폼 제출 데이터:', requestData);
console.log('JSON 문자열:', JSON.stringify(requestData));

// 스토어의 registerRootUser 메서드 호출
await registerRootUser(requestData);
try {
// 스토어의 registerRootUser 메서드 호출
await registerRootUser(requestData);
} catch (error: any) {
console.log('회원가입 에러 처리:', error);
// 이메일 중복 오류 처리
if (error?.message === "Email already exists") {
// 사용자 탭으로 전환하고 이메일 필드에 오류 표시
setActiveTab('user');
setUserErrors({
...userErrors,
email: "이미 등록된 이메일입니다. 다른 이메일을 사용해주세요."
});
}
}
};

return (
Expand All @@ -117,7 +246,11 @@ export default function RegisterPage() {
{/* 오류 메시지 */}
{registerError && (
<div className="mb-6 p-4 bg-red-100 border border-red-300 text-red-700 rounded-lg">
<p className="font-medium">{registerError}</p>
<p className="font-medium">
{registerError === "Email already exists"
? "이미 등록된 이메일입니다. 다른 이메일을 사용해주세요."
: registerError}
</p>
</div>
)}

Expand Down Expand Up @@ -168,10 +301,13 @@ export default function RegisterPage() {
value={userFormData.name}
onChange={handleUserInputChange}
required
className={`w-full rounded-lg border ${currentTheme.border} ${currentTheme.inputBg} ${currentTheme.text} px-4 py-2.5 focus:ring-2 focus:ring-blue-500 focus:border-blue-500`}
className={`w-full rounded-lg border ${userErrors.name ? 'border-red-500' : currentTheme.border} ${currentTheme.inputBg} ${currentTheme.text} px-4 py-2.5 focus:ring-2 focus:ring-blue-500 focus:border-blue-500`}
placeholder="이름을 입력하세요"
disabled={isRegistering}
/>
{userErrors.name && (
<p className="mt-1 text-sm text-red-500">{userErrors.name}</p>
)}
</div>

{/* 이메일 */}
Expand All @@ -186,10 +322,13 @@ export default function RegisterPage() {
value={userFormData.email}
onChange={handleUserInputChange}
required
className={`w-full rounded-lg border ${currentTheme.border} ${currentTheme.inputBg} ${currentTheme.text} px-4 py-2.5 focus:ring-2 focus:ring-blue-500 focus:border-blue-500`}
className={`w-full rounded-lg border ${userErrors.email ? 'border-red-500' : currentTheme.border} ${currentTheme.inputBg} ${currentTheme.text} px-4 py-2.5 focus:ring-2 focus:ring-blue-500 focus:border-blue-500`}
placeholder="이메일 주소를 입력하세요"
disabled={isRegistering}
/>
{userErrors.email && (
<p className="mt-1 text-sm text-red-500">{userErrors.email}</p>
)}
</div>

{/* 비밀번호 */}
Expand All @@ -204,10 +343,13 @@ export default function RegisterPage() {
value={userFormData.password}
onChange={handleUserInputChange}
required
className={`w-full rounded-lg border ${currentTheme.border} ${currentTheme.inputBg} ${currentTheme.text} px-4 py-2.5 focus:ring-2 focus:ring-blue-500 focus:border-blue-500`}
className={`w-full rounded-lg border ${userErrors.password ? 'border-red-500' : currentTheme.border} ${currentTheme.inputBg} ${currentTheme.text} px-4 py-2.5 focus:ring-2 focus:ring-blue-500 focus:border-blue-500`}
placeholder="비밀번호를 입력하세요"
disabled={isRegistering}
/>
{userErrors.password && (
<p className="mt-1 text-sm text-red-500">{userErrors.password}</p>
)}
</div>

{/* 전화번호 */}
Expand All @@ -222,10 +364,14 @@ export default function RegisterPage() {
value={userFormData.phone}
onChange={handleUserInputChange}
required
className={`w-full rounded-lg border ${currentTheme.border} ${currentTheme.inputBg} ${currentTheme.text} px-4 py-2.5 focus:ring-2 focus:ring-blue-500 focus:border-blue-500`}
placeholder="연락처를 입력하세요"
maxLength={13}
className={`w-full rounded-lg border ${userErrors.phone ? 'border-red-500' : currentTheme.border} ${currentTheme.inputBg} ${currentTheme.text} px-4 py-2.5 focus:ring-2 focus:ring-blue-500 focus:border-blue-500`}
placeholder="숫자만 입력하세요 (예: 01012345678)"
disabled={isRegistering}
/>
{userErrors.phone && (
<p className="mt-1 text-sm text-red-500">{userErrors.phone}</p>
)}
</div>

{/* 직책 */}
Expand All @@ -240,10 +386,13 @@ export default function RegisterPage() {
value={userFormData.jobTitle}
onChange={handleUserInputChange}
required
className={`w-full rounded-lg border ${currentTheme.border} ${currentTheme.inputBg} ${currentTheme.text} px-4 py-2.5 focus:ring-2 focus:ring-blue-500 focus:border-blue-500`}
className={`w-full rounded-lg border ${userErrors.jobTitle ? 'border-red-500' : currentTheme.border} ${currentTheme.inputBg} ${currentTheme.text} px-4 py-2.5 focus:ring-2 focus:ring-blue-500 focus:border-blue-500`}
placeholder="직책을 입력하세요"
disabled={isRegistering}
/>
{userErrors.jobTitle && (
<p className="mt-1 text-sm text-red-500">{userErrors.jobTitle}</p>
)}
</div>
</div>
</div>
Expand All @@ -262,10 +411,13 @@ export default function RegisterPage() {
value={companyFormData.name}
onChange={handleCompanyInputChange}
required
className={`w-full rounded-lg border ${currentTheme.border} ${currentTheme.inputBg} ${currentTheme.text} px-4 py-2.5 focus:ring-2 focus:ring-blue-500 focus:border-blue-500`}
className={`w-full rounded-lg border ${companyErrors.name ? 'border-red-500' : currentTheme.border} ${currentTheme.inputBg} ${currentTheme.text} px-4 py-2.5 focus:ring-2 focus:ring-blue-500 focus:border-blue-500`}
placeholder="회사명을 입력하세요"
disabled={isRegistering}
/>
{companyErrors.name && (
<p className="mt-1 text-sm text-red-500">{companyErrors.name}</p>
)}
</div>

{/* 주소 */}
Expand All @@ -291,32 +443,40 @@ export default function RegisterPage() {
회사 이메일
</label>
<input
type="text"
type="email"
id="companyEmail"
name="email"
value={companyFormData.email}
onChange={handleCompanyInputChange}
className={`w-full rounded-lg border ${currentTheme.border} ${currentTheme.inputBg} ${currentTheme.text} px-4 py-2.5 focus:ring-2 focus:ring-blue-500 focus:border-blue-500`}
className={`w-full rounded-lg border ${companyErrors.email ? 'border-red-500' : currentTheme.border} ${currentTheme.inputBg} ${currentTheme.text} px-4 py-2.5 focus:ring-2 focus:ring-blue-500 focus:border-blue-500`}
placeholder="회사 이메일을 입력하세요"
disabled={isRegistering}
/>
{companyErrors.email && (
<p className="mt-1 text-sm text-red-500">{companyErrors.email}</p>
)}
</div>

{/* 회사 전화번호 */}
<div>
<label htmlFor="companyPhone" className={`block text-sm font-medium ${currentTheme.text} mb-2`}>
회사 전화번호
회사 전화번호 <span className="text-red-500">*</span>
</label>
<input
type="tel"
id="companyPhone"
name="phone"
value={companyFormData.phone}
onChange={handleCompanyInputChange}
className={`w-full rounded-lg border ${currentTheme.border} ${currentTheme.inputBg} ${currentTheme.text} px-4 py-2.5 focus:ring-2 focus:ring-blue-500 focus:border-blue-500`}
placeholder="회사 전화번호를 입력하세요"
required
maxLength={13}
className={`w-full rounded-lg border ${companyErrors.phone ? 'border-red-500' : currentTheme.border} ${currentTheme.inputBg} ${currentTheme.text} px-4 py-2.5 focus:ring-2 focus:ring-blue-500 focus:border-blue-500`}
placeholder="숫자만 입력하세요 (예: 0212345678)"
disabled={isRegistering}
/>
{companyErrors.phone && (
<p className="mt-1 text-sm text-red-500">{companyErrors.phone}</p>
)}
</div>

{/* 웹사이트 */}
Expand Down Expand Up @@ -352,6 +512,11 @@ export default function RegisterPage() {
disabled={isRegistering}
/>
</div>

<div className="pt-2 text-sm text-gray-500">
<p>* 표시는 필수 입력 항목입니다.</p>
<p>전화번호는 자동으로 하이픈(-)이 추가됩니다.</p>
</div>
</div>
</div>
)}
Expand All @@ -362,7 +527,7 @@ export default function RegisterPage() {
<div className="flex w-full justify-end">
<button
type="button"
onClick={() => handleTabChange('company')}
onClick={handleNextButtonClick}
className="py-3 px-6 rounded-lg bg-blue-600 hover:bg-blue-700 text-white font-medium focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
>
다음: 회사 정보
Expand All @@ -372,15 +537,15 @@ export default function RegisterPage() {
<div className="flex w-full justify-between">
<button
type="button"
onClick={() => handleTabChange('user')}
onClick={() => setActiveTab('user')}
className={`py-3 px-6 rounded-lg border ${currentTheme.border} ${currentTheme.cardBg} font-medium focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2`}
>
이전: 사용자 정보
</button>
<button
type="submit"
disabled={isRegistering}
className={`py-3 px-6 rounded-lg bg-blue-600 hover:bg-blue-700 text-white font-medium focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 ${isRegistering ? 'opacity-70 cursor-not-allowed' : ''}`}
disabled={isRegistering || Object.keys(companyErrors).length > 0}
className={`py-3 px-6 rounded-lg bg-blue-600 hover:bg-blue-700 text-white font-medium focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 ${isRegistering || Object.keys(companyErrors).length > 0 ? 'opacity-70 cursor-not-allowed' : ''}`}
>
{isRegistering ? (
<span className="flex items-center justify-center">
Expand Down
Loading