diff --git a/frontend/app/(auth)/register/page.tsx b/frontend/app/(auth)/register/page.tsx deleted file mode 100644 index bd098543..00000000 --- a/frontend/app/(auth)/register/page.tsx +++ /dev/null @@ -1,51 +0,0 @@ -"use client"; - -import { useRegister } from "@/hooks/useRegister"; -import { LoginHeader } from "@/components/auth/LoginHeader"; -import { RegisterForm } from "@/components/auth/RegisterForm"; -import { LoginFooter } from "@/components/auth/LoginFooter"; - -/** - * 회원가입 페이지 컴포넌트 - * 비즈니스 로직은 useRegister 훅에서 처리하고, UI 컴포넌트들을 조합하여 구성 - */ -export default function RegisterPage() { - const { - formData, - isLoading, - error, - validationErrors, - showPassword, - showPasswordConfirm, - isFormValid, - handleInputChange, - handleRegister, - togglePasswordVisibility, - togglePasswordConfirmVisibility, - } = useRegister(); - - return ( -
-
- - - - - -
-
- ); -} - diff --git a/frontend/components/auth/LoginFooter.tsx b/frontend/components/auth/LoginFooter.tsx index 42edf9e2..99b5da16 100644 --- a/frontend/components/auth/LoginFooter.tsx +++ b/frontend/components/auth/LoginFooter.tsx @@ -1,29 +1,11 @@ import { UI_CONFIG } from "@/constants/auth"; -import Link from "next/link"; - -interface LoginFooterProps { - showRegisterLink?: boolean; -} /** * 로그인 페이지 푸터 컴포넌트 */ -export function LoginFooter({ showRegisterLink = true }: LoginFooterProps) { +export function LoginFooter() { return (
- {showRegisterLink && ( -
-

- 계정이 없으신가요?{" "} - - 회원가입 - -

-
- )}

{UI_CONFIG.COPYRIGHT}

{UI_CONFIG.POWERED_BY}

diff --git a/frontend/components/auth/RegisterForm.tsx b/frontend/components/auth/RegisterForm.tsx deleted file mode 100644 index 2fd26432..00000000 --- a/frontend/components/auth/RegisterForm.tsx +++ /dev/null @@ -1,259 +0,0 @@ -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; -import { Eye, EyeOff, Loader2, ArrowLeft } from "lucide-react"; -import { RegisterFormData } from "@/types/auth"; -import { ErrorMessage } from "./ErrorMessage"; -import Link from "next/link"; - -interface RegisterFormProps { - formData: RegisterFormData; - isLoading: boolean; - error: string; - validationErrors: Record; - showPassword: boolean; - showPasswordConfirm: boolean; - isFormValid: boolean; - onInputChange: (e: React.ChangeEvent) => void; - onSubmit: (e: React.FormEvent) => void; - onTogglePassword: () => void; - onTogglePasswordConfirm: () => void; -} - -/** - * 회원가입 폼 컴포넌트 - */ -export function RegisterForm({ - formData, - isLoading, - error, - validationErrors, - showPassword, - showPasswordConfirm, - isFormValid, - onInputChange, - onSubmit, - onTogglePassword, - onTogglePasswordConfirm, -}: RegisterFormProps) { - return ( - - - 회원가입 - 새로운 계정을 생성합니다 - - - - -
- {/* 사용자 ID */} -
- - - {validationErrors.userId && ( -

{validationErrors.userId}

- )} -
- - {/* 비밀번호 */} -
- -
- - -
- {validationErrors.password && ( -

{validationErrors.password}

- )} -
- - {/* 비밀번호 확인 */} -
- -
- - -
- {validationErrors.passwordConfirm && ( -

{validationErrors.passwordConfirm}

- )} -
- - {/* 이름 */} -
- - - {validationErrors.userName && ( -

{validationErrors.userName}

- )} -
- - {/* 면허번호 */} -
- - - {validationErrors.licenseNumber && ( -

{validationErrors.licenseNumber}

- )} -

- 운전면허번호를 하이픈(-)을 포함하여 입력해주세요 -

-
- - {/* 차량 번호 */} -
- - - {validationErrors.vehicleNumber && ( -

{validationErrors.vehicleNumber}

- )} -

- 한국 차량 번호 형식으로 입력해주세요 -

-
- - {/* 휴대폰 번호 */} -
- - - {validationErrors.phoneNumber && ( -

{validationErrors.phoneNumber}

- )} -

- 하이픈(-)을 포함하여 입력해주세요 -

-
- - {/* 버튼 그룹 */} -
- - - - -
-
-
-
- ); -} - diff --git a/frontend/hooks/useRegister.ts b/frontend/hooks/useRegister.ts deleted file mode 100644 index 09ec46ad..00000000 --- a/frontend/hooks/useRegister.ts +++ /dev/null @@ -1,262 +0,0 @@ -import { useState, useEffect } from "react"; -import { useRouter } from "next/navigation"; -import { RegisterFormData } from "@/types/auth"; -import { authApi } from "@/lib/api/auth"; -import { useToast } from "@/hooks/use-toast"; - -/** - * 회원가입 비즈니스 로직 훅 - */ -export function useRegister() { - const router = useRouter(); - const { toast } = useToast(); - const [formData, setFormData] = useState({ - userId: "", - password: "", - passwordConfirm: "", - userName: "", - licenseNumber: "", - vehicleNumber: "", - phoneNumber: "", - }); - const [isLoading, setIsLoading] = useState(false); - const [error, setError] = useState(""); - const [validationErrors, setValidationErrors] = useState>({}); - const [showPassword, setShowPassword] = useState(false); - const [showPasswordConfirm, setShowPasswordConfirm] = useState(false); - const [isFormValid, setIsFormValid] = useState(false); - - /** - * 실시간 폼 유효성 검사 및 에러 메시지 업데이트 - */ - useEffect(() => { - const checkFormValidity = () => { - const errors: Record = {}; - - // 사용자 ID 검사 (입력이 있을 때만) - if (formData.userId.length > 0 && formData.userId.length < 2) { - errors.userId = "사용자 ID는 최소 2자 이상이어야 합니다"; - } - - // 비밀번호 검사 (입력이 있을 때만) - if (formData.password.length > 0 && formData.password.length < 6) { - errors.password = "비밀번호는 최소 6자 이상이어야 합니다"; - } - - // 비밀번호 확인 검사 (입력이 있을 때만) - if (formData.passwordConfirm.length > 0 && formData.password !== formData.passwordConfirm) { - errors.passwordConfirm = "비밀번호가 일치하지 않습니다"; - } - - // 이름 검사 (입력이 있을 때만) - if (formData.userName.length > 0 && formData.userName.trim().length < 2) { - errors.userName = "이름은 최소 2자 이상이어야 합니다"; - } - - // 면허번호 검사 (입력이 있을 때만) - if (formData.licenseNumber.length > 0 && !validateLicenseNumber(formData.licenseNumber)) { - errors.licenseNumber = "올바른 면허번호 형식이 아닙니다 (예: 12-34-567890-12)"; - } - - // 차량 번호 검사 (입력이 있을 때만) - if (formData.vehicleNumber.length > 0 && !validateVehicleNumber(formData.vehicleNumber)) { - errors.vehicleNumber = "올바른 차량 번호 형식이 아닙니다 (예: 12가3456, 123가4567)"; - } - - // 휴대폰 번호 검사 (입력이 있을 때만) - if (formData.phoneNumber.length > 0 && !validatePhoneNumber(formData.phoneNumber)) { - errors.phoneNumber = "올바른 휴대폰 번호 형식이 아닙니다 (예: 010-1234-5678)"; - } - - setValidationErrors(errors); - - // 모든 필드가 채워져 있고 에러가 없는지 확인 - const allFieldsFilled = - formData.userId.trim().length >= 2 && - formData.password.length >= 6 && - formData.passwordConfirm.length > 0 && - formData.userName.trim().length >= 2 && - formData.licenseNumber.length > 0 && - formData.vehicleNumber.length > 0 && - formData.phoneNumber.length > 0; - - const isValid = allFieldsFilled && Object.keys(errors).length === 0; - setIsFormValid(isValid); - }; - - checkFormValidity(); - }, [formData]); - - /** - * 입력값 변경 핸들러 - */ - const handleInputChange = (e: React.ChangeEvent) => { - const { name, value } = e.target; - setFormData((prev) => ({ ...prev, [name]: value })); - - // 전역 에러 초기화 - if (error) setError(""); - }; - - /** - * 비밀번호 표시/숨김 토글 - */ - const togglePasswordVisibility = () => { - setShowPassword((prev) => !prev); - }; - - /** - * 비밀번호 확인 표시/숨김 토글 - */ - const togglePasswordConfirmVisibility = () => { - setShowPasswordConfirm((prev) => !prev); - }; - - /** - * 면허번호 유효성 검사 - * 한국 운전면허 형식: 12-34-567890-12 (지역번호-발급년도-일련번호-체크) - */ - const validateLicenseNumber = (licenseNumber: string): boolean => { - // 하이픈 포함 형식: 12-34-567890-12 - const pattern = /^\d{2}-\d{2}-\d{6}-\d{2}$/; - return pattern.test(licenseNumber); - }; - - /** - * 차량 번호 유효성 검사 - * 한국 차량 번호 형식: 12가3456, 123가4567, 서울12가3456 등 - */ - const validateVehicleNumber = (vehicleNumber: string): boolean => { - // 공백 제거 - const cleanNumber = vehicleNumber.replace(/\s/g, ""); - - // 한국 차량 번호 패턴 - // 1. 구형: 12가3456 (2자리 숫자 + 한글 1자 + 4자리 숫자) - // 2. 신형: 123가4567 (3자리 숫자 + 한글 1자 + 4자리 숫자) - // 3. 지역명 포함: 서울12가3456, 서울123가4567 - const patterns = [ - /^\d{2}[가-힣]{1}\d{4}$/, // 12가3456 - /^\d{3}[가-힣]{1}\d{4}$/, // 123가4567 - /^[가-힣]{2}\d{2}[가-힣]{1}\d{4}$/, // 서울12가3456 - /^[가-힣]{2}\d{3}[가-힣]{1}\d{4}$/, // 서울123가4567 - ]; - - return patterns.some(pattern => pattern.test(cleanNumber)); - }; - - /** - * 휴대폰 번호 유효성 검사 - * 형식: 010-1234-5678, 011-123-4567 등 - */ - const validatePhoneNumber = (phoneNumber: string): boolean => { - // 하이픈 포함 형식: 010-1234-5678, 011-123-4567 - const pattern = /^01[016789]-\d{3,4}-\d{4}$/; - return pattern.test(phoneNumber); - }; - - /** - * 폼 유효성 검사 - */ - const validateForm = (): boolean => { - const errors: Record = {}; - - // 사용자 ID 검사 - if (formData.userId.length < 2) { - errors.userId = "사용자 ID는 최소 2자 이상이어야 합니다"; - } - - // 비밀번호 검사 - if (formData.password.length < 6) { - errors.password = "비밀번호는 최소 6자 이상이어야 합니다"; - } - - // 비밀번호 확인 검사 - if (formData.password !== formData.passwordConfirm) { - errors.passwordConfirm = "비밀번호가 일치하지 않습니다"; - } - - // 이름 검사 - if (formData.userName.trim().length < 2) { - errors.userName = "이름은 최소 2자 이상이어야 합니다"; - } - - // 면허번호 검사 - if (!validateLicenseNumber(formData.licenseNumber)) { - errors.licenseNumber = "올바른 면허번호 형식이 아닙니다 (예: 12-34-567890-12)"; - } - - // 차량 번호 검사 - if (!validateVehicleNumber(formData.vehicleNumber)) { - errors.vehicleNumber = "올바른 차량 번호 형식이 아닙니다 (예: 12가3456, 123가4567)"; - } - - // 휴대폰 번호 검사 - if (!validatePhoneNumber(formData.phoneNumber)) { - errors.phoneNumber = "올바른 휴대폰 번호 형식이 아닙니다 (예: 010-1234-5678)"; - } - - setValidationErrors(errors); - return Object.keys(errors).length === 0; - }; - - /** - * 회원가입 핸들러 - */ - const handleRegister = async (e: React.FormEvent) => { - e.preventDefault(); - - // 유효성 검사 - if (!validateForm()) { - return; - } - - setIsLoading(true); - setError(""); - - try { - const response = await authApi.register({ - userId: formData.userId, - password: formData.password, - userName: formData.userName, - licenseNumber: formData.licenseNumber, - vehicleNumber: formData.vehicleNumber.replace(/\s/g, ""), // 공백 제거 - phoneNumber: formData.phoneNumber, - }); - - if (response.success) { - // 회원가입 성공 - toast 알림 표시 - toast({ - title: "회원가입 완료", - description: "회원가입이 성공적으로 완료되었습니다. 로그인 페이지로 이동합니다.", - }); - - // 로그인 페이지로 이동 - setTimeout(() => { - router.push("/login"); - }, 1500); - } else { - setError(response.message || "회원가입에 실패했습니다"); - } - } catch (err: any) { - console.error("회원가입 오류:", err); - setError(err.message || "회원가입 중 오류가 발생했습니다"); - } finally { - setIsLoading(false); - } - }; - - return { - formData, - isLoading, - error, - validationErrors, - showPassword, - showPasswordConfirm, - isFormValid, - handleInputChange, - handleRegister, - togglePasswordVisibility, - togglePasswordConfirmVisibility, - }; -} - diff --git a/frontend/lib/api/auth.ts b/frontend/lib/api/auth.ts deleted file mode 100644 index 119514bc..00000000 --- a/frontend/lib/api/auth.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { RegisterFormData, RegisterResponse } from "@/types/auth"; - -/** - * 인증 관련 API (임시 mock) - */ -export const authApi = { - /** - * 회원가입 (임시 구현) - */ - async register(data: Omit): Promise { - // TODO: 백엔드 API 연동 필요 - console.log("회원가입 요청:", data); - - // 임시로 성공 응답 반환 - return new Promise((resolve) => { - setTimeout(() => { - resolve({ - success: true, - message: "회원가입이 완료되었습니다", - data: { - userId: data.userId, - }, - }); - }, 1000); - }); - }, -}; - diff --git a/frontend/types/auth.ts b/frontend/types/auth.ts index bc7f4af1..cd8e65b6 100644 --- a/frontend/types/auth.ts +++ b/frontend/types/auth.ts @@ -22,22 +22,3 @@ export interface AuthStatus { isLoggedIn: boolean; isAdmin?: boolean; } - -export interface RegisterFormData { - userId: string; - password: string; - passwordConfirm: string; - userName: string; - licenseNumber: string; - vehicleNumber: string; - phoneNumber: string; -} - -export interface RegisterResponse { - success: boolean; - message?: string; - data?: { - userId: string; - }; - errorCode?: string; -}