From 5d1ff46c3ebd940aebcfd456cee17044b080dab7 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Sat, 15 Mar 2025 16:18:10 +0000 Subject: [PATCH] Refactor RegisterForm component Splits the RegisterForm component into smaller, more manageable components to improve code readability and maintainability. --- src/components/auth/EmailConfirmation.tsx | 55 ++++ src/components/auth/RegisterForm.tsx | 280 +++++++-------------- src/components/auth/RegisterFormFields.tsx | 120 +++++++++ src/components/auth/types.ts | 21 ++ 4 files changed, 283 insertions(+), 193 deletions(-) create mode 100644 src/components/auth/EmailConfirmation.tsx create mode 100644 src/components/auth/RegisterFormFields.tsx create mode 100644 src/components/auth/types.ts diff --git a/src/components/auth/EmailConfirmation.tsx b/src/components/auth/EmailConfirmation.tsx new file mode 100644 index 0000000..def39f4 --- /dev/null +++ b/src/components/auth/EmailConfirmation.tsx @@ -0,0 +1,55 @@ + +import React from "react"; +import { useNavigate } from "react-router-dom"; +import { Mail, InfoIcon } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; + +interface EmailConfirmationProps { + email: string; + onBackToForm: () => void; +} + +const EmailConfirmation: React.FC = ({ email, onBackToForm }) => { + const navigate = useNavigate(); + + return ( +
+
+ +

이메일 인증이 필요합니다

+

+ {email}로 인증 링크가 포함된 이메일을 보냈습니다. + 이메일을 확인하고 링크를 클릭하여 계정 등록을 완료해주세요. +

+ + + + 인증 이메일이 보이지 않나요? + + 스팸 폴더를 확인해보세요. 또는 다른 이메일 주소로 다시 시도하실 수 있습니다. + + + +
+ + +
+
+
+ ); +}; + +export default EmailConfirmation; diff --git a/src/components/auth/RegisterForm.tsx b/src/components/auth/RegisterForm.tsx index ff8d72e..b795a25 100644 --- a/src/components/auth/RegisterForm.tsx +++ b/src/components/auth/RegisterForm.tsx @@ -1,34 +1,18 @@ import React, { useState } from "react"; import { useNavigate } from "react-router-dom"; -import { Label } from "@/components/ui/label"; -import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; -import { ArrowRight, Mail, KeyRound, User, Eye, EyeOff } from "lucide-react"; +import { ArrowRight } from "lucide-react"; import { useToast } from "@/hooks/useToast.wrapper"; import { verifyServerConnection } from "@/contexts/auth/auth.utils"; -import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; -import { InfoIcon } from "lucide-react"; +import { ServerStatus, SignUpResponse } from "./types"; +import EmailConfirmation from "./EmailConfirmation"; +import RegisterFormFields from "./RegisterFormFields"; interface RegisterFormProps { - signUp: (email: string, password: string, username: string) => Promise<{ - error: any; - user: any; - redirectToSettings?: boolean; - emailConfirmationRequired?: boolean; - }>; - serverStatus: { - checked: boolean; - connected: boolean; - message: string; - }; - setServerStatus: React.Dispatch< - React.SetStateAction<{ - checked: boolean; - connected: boolean; - message: string; - }> - >; + signUp: (email: string, password: string, username: string) => Promise; + serverStatus: ServerStatus; + setServerStatus: React.Dispatch>; setRegisterError: React.Dispatch>; } @@ -49,10 +33,50 @@ const RegisterForm: React.FC = ({ const { toast } = useToast(); const navigate = useNavigate(); - const handleRegister = async (e: React.FormEvent) => { - e.preventDefault(); - setRegisterError(null); + const validateForm = (): boolean => { + if (!username || !email || !password || !confirmPassword) { + toast({ + title: "입력 오류", + description: "모든 필드를 입력해주세요.", + variant: "destructive", + }); + return false; + } + if (password !== confirmPassword) { + toast({ + title: "비밀번호 불일치", + description: "비밀번호와 비밀번호 확인이 일치하지 않습니다.", + variant: "destructive", + }); + return false; + } + + // 비밀번호 강도 검사 + if (password.length < 8) { + toast({ + title: "비밀번호 강도 부족", + description: "비밀번호는 최소 8자 이상이어야 합니다.", + variant: "destructive", + }); + return false; + } + + // 이메일 형식 검사 + const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if (!emailPattern.test(email)) { + toast({ + title: "이메일 형식 오류", + description: "유효한 이메일 주소를 입력해주세요.", + variant: "destructive", + }); + return false; + } + + return true; + }; + + const checkServerConnectivity = async (): Promise => { // 서버 연결 상태 재확인 if (!serverStatus.connected) { try { @@ -69,7 +93,7 @@ const RegisterForm: React.FC = ({ description: "서버에 연결할 수 없습니다. 네트워크 또는 서버 상태를 확인하세요.", variant: "destructive" }); - return; + return false; } } catch (error: any) { toast({ @@ -77,48 +101,22 @@ const RegisterForm: React.FC = ({ description: error.message || "서버 연결 확인 중 오류가 발생했습니다.", variant: "destructive" }); - return; + return false; } } + return true; + }; + + const handleRegister = async (e: React.FormEvent) => { + e.preventDefault(); + setRegisterError(null); - if (!username || !email || !password || !confirmPassword) { - toast({ - title: "입력 오류", - description: "모든 필드를 입력해주세요.", - variant: "destructive", - }); - return; - } + // 서버 연결 확인 + const isServerConnected = await checkServerConnectivity(); + if (!isServerConnected) return; - if (password !== confirmPassword) { - toast({ - title: "비밀번호 불일치", - description: "비밀번호와 비밀번호 확인이 일치하지 않습니다.", - variant: "destructive", - }); - return; - } - - // 비밀번호 강도 검사 - if (password.length < 8) { - toast({ - title: "비밀번호 강도 부족", - description: "비밀번호는 최소 8자 이상이어야 합니다.", - variant: "destructive", - }); - return; - } - - // 이메일 형식 검사 - const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; - if (!emailPattern.test(email)) { - toast({ - title: "이메일 형식 오류", - description: "유효한 이메일 주소를 입력해주세요.", - variant: "destructive", - }); - return; - } + // 폼 유효성 검사 + if (!validateForm()) return; setIsLoading(true); @@ -203,140 +201,36 @@ const RegisterForm: React.FC = ({ // 이메일 인증 안내 화면 (인증 메일이 발송된 경우) if (emailConfirmationSent) { - return ( -
-
- -

이메일 인증이 필요합니다

-

- {email}로 인증 링크가 포함된 이메일을 보냈습니다. - 이메일을 확인하고 링크를 클릭하여 계정 등록을 완료해주세요. -

- - - - 인증 이메일이 보이지 않나요? - - 스팸 폴더를 확인해보세요. 또는 다른 이메일 주소로 다시 시도하실 수 있습니다. - - - -
- - -
-
-
- ); + return setEmailConfirmationSent(false)} + />; } // 일반 회원가입 양식 return (
-
-
- -
- - setUsername(e.target.value)} - className="pl-10 neuro-pressed" - /> -
-
- -
- -
- - setEmail(e.target.value)} - className="pl-10 neuro-pressed" - /> -
-
- -
- -
- - setPassword(e.target.value)} - className="pl-10 neuro-pressed" - /> - -
- {password && password.length > 0 && password.length < 8 && ( -

비밀번호는 최소 8자 이상이어야 합니다.

- )} -
- -
- -
- - setConfirmPassword(e.target.value)} - className="pl-10 neuro-pressed" - /> -
- {confirmPassword && password !== confirmPassword && ( -

비밀번호가 일치하지 않습니다.

- )} -
- - - - 이메일 인증 필요 - - 회원가입 후 이메일로 인증 링크가 발송됩니다. - 이메일 인증을 완료해야 로그인이 가능합니다. - - - - -
+ + +
); diff --git a/src/components/auth/RegisterFormFields.tsx b/src/components/auth/RegisterFormFields.tsx new file mode 100644 index 0000000..572d8ac --- /dev/null +++ b/src/components/auth/RegisterFormFields.tsx @@ -0,0 +1,120 @@ + +import React, { useState } from "react"; +import { Label } from "@/components/ui/label"; +import { Input } from "@/components/ui/input"; +import { User, Mail, KeyRound, Eye, EyeOff, InfoIcon } from "lucide-react"; +import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; + +interface RegisterFormFieldsProps { + username: string; + setUsername: (value: string) => void; + email: string; + setEmail: (value: string) => void; + password: string; + setPassword: (value: string) => void; + confirmPassword: string; + setConfirmPassword: (value: string) => void; + showPassword: boolean; + setShowPassword: (value: boolean) => void; +} + +const RegisterFormFields: React.FC = ({ + username, + setUsername, + email, + setEmail, + password, + setPassword, + confirmPassword, + setConfirmPassword, + showPassword, + setShowPassword +}) => { + return ( +
+
+ +
+ + setUsername(e.target.value)} + className="pl-10 neuro-pressed" + /> +
+
+ +
+ +
+ + setEmail(e.target.value)} + className="pl-10 neuro-pressed" + /> +
+
+ +
+ +
+ + setPassword(e.target.value)} + className="pl-10 neuro-pressed" + /> + +
+ {password && password.length > 0 && password.length < 8 && ( +

비밀번호는 최소 8자 이상이어야 합니다.

+ )} +
+ +
+ +
+ + setConfirmPassword(e.target.value)} + className="pl-10 neuro-pressed" + /> +
+ {confirmPassword && password !== confirmPassword && ( +

비밀번호가 일치하지 않습니다.

+ )} +
+ + + + 이메일 인증 필요 + + 회원가입 후 이메일로 인증 링크가 발송됩니다. + 이메일 인증을 완료해야 로그인이 가능합니다. + + +
+ ); +}; + +export default RegisterFormFields; diff --git a/src/components/auth/types.ts b/src/components/auth/types.ts new file mode 100644 index 0000000..fc92dd6 --- /dev/null +++ b/src/components/auth/types.ts @@ -0,0 +1,21 @@ + +// 서버 연결 상태 타입 +export interface ServerStatus { + checked: boolean; + connected: boolean; + message: string; +} + +// 회원가입 응답 타입 +export interface SignUpResponse { + error: any; + user: any; + redirectToSettings?: boolean; + emailConfirmationRequired?: boolean; +} + +// 회원가입 폼 공통 props +export interface RegisterFormCommonProps { + serverStatus: ServerStatus; + setRegisterError: React.Dispatch>; +}