From 60c1bffcd6f7c76edfe88eaabc9f6e0e4187f071 Mon Sep 17 00:00:00 2001 From: suyeonnnnnnn Date: Fri, 10 Nov 2023 22:59:53 +0900 Subject: [PATCH 1/4] Refactor: Refactoring signUp to use React Hook Form --- package-lock.json | 41 ++++++ package.json | 2 + src/components/Login/SignUpModal/index.tsx | 156 ++++++++++++++++----- 3 files changed, 161 insertions(+), 38 deletions(-) diff --git a/package-lock.json b/package-lock.json index c760f027..6149e571 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,9 +21,11 @@ "match-sorter": "^6.3.1", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-hook-form": "^7.48.2", "react-icons": "^4.11.0", "react-router-dom": "^6.18.0", "react-spinners": "^0.13.8", + "recoil": "^0.7.7", "socket.io-client": "^4.7.2", "sort-by": "^1.2.0", "styled-components": "^6.1.0" @@ -5167,6 +5169,11 @@ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, + "node_modules/hamt_plus": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hamt_plus/-/hamt_plus-1.0.2.tgz", + "integrity": "sha512-t2JXKaehnMb9paaYA7J0BX8QQAY8lwfQ9Gjf4pg/mk4krt+cmwmU652HOoWonf+7+EQV97ARPMhhVgU1ra2GhA==" + }, "node_modules/has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -6625,6 +6632,21 @@ } } }, + "node_modules/react-hook-form": { + "version": "7.48.2", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.48.2.tgz", + "integrity": "sha512-H0T2InFQb1hX7qKtDIZmvpU1Xfn/bdahWBN1fH19gSe4bBEqTfmlr7H3XWTaVtiK4/tpPaI1F3355GPMZYge+A==", + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18" + } + }, "node_modules/react-icons": { "version": "4.11.0", "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.11.0.tgz", @@ -6753,6 +6775,25 @@ } } }, + "node_modules/recoil": { + "version": "0.7.7", + "resolved": "https://registry.npmjs.org/recoil/-/recoil-0.7.7.tgz", + "integrity": "sha512-8Og5KPQW9LwC577Vc7Ug2P0vQshkv1y3zG3tSSkWMqkWSwHmE+by06L8JtnGocjW6gcCvfwB3YtrJG6/tWivNQ==", + "dependencies": { + "hamt_plus": "1.0.2" + }, + "peerDependencies": { + "react": ">=16.13.1" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz", diff --git a/package.json b/package.json index 9940d0d5..7b86358f 100644 --- a/package.json +++ b/package.json @@ -25,9 +25,11 @@ "match-sorter": "^6.3.1", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-hook-form": "^7.48.2", "react-icons": "^4.11.0", "react-router-dom": "^6.18.0", "react-spinners": "^0.13.8", + "recoil": "^0.7.7", "socket.io-client": "^4.7.2", "sort-by": "^1.2.0", "styled-components": "^6.1.0" diff --git a/src/components/Login/SignUpModal/index.tsx b/src/components/Login/SignUpModal/index.tsx index 9ae11b9f..ea04d6bd 100644 --- a/src/components/Login/SignUpModal/index.tsx +++ b/src/components/Login/SignUpModal/index.tsx @@ -1,10 +1,10 @@ -import useInput from "../../../hooks/useInput"; -import { Input } from "@chakra-ui/react"; -import { Button } from "@chakra-ui/react"; -import { useEffect, useState } from "react"; +import React, { useState } from "react"; +import { useForm, Controller } from "react-hook-form"; +import { Input, Button, Text } from "@chakra-ui/react"; +import useFetch from "../../../hooks/useFetch"; import axios from "axios"; -interface Data { +interface FormData { id: string; password: string; name: string; @@ -12,46 +12,126 @@ interface Data { } const SignUpModal = () => { - const idInput = useInput(""); - const pwInput = useInput(""); - const nameInput = useInput(""); - const [data, setData] = useState({ - id: "", - password: "", - name: "", + const { + control, + handleSubmit, + formState: { errors }, + } = useForm(); + const signUpFetch = useFetch({ + url: "https://fastcampus-chat.net/signup", + method: "POST", + data: {}, + start: false, }); + const [selectedFile, setSelectedFile] = useState(null); - useEffect(() => { - const copy = { ...data }; - copy.id = idInput.value; - copy.password = pwInput.value; - copy.name = nameInput.value; - - setData(copy); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [idInput.value, pwInput.value, nameInput.value]); - - const handleSignup = () => { - axios - .post("https://fastcampus-chat.net/signup", data, { - headers: { - "content-type": "application/json", - serverId: "6603aca7", + const onSubmit = async (formData: FormData) => { + let pictureAsString: string | undefined; + + const toBase64 = (file: File) => + new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.readAsDataURL(file); + reader.onload = () => resolve(reader.result as string); + reader.onerror = (error) => reject(error); + }); + + if (selectedFile) { + pictureAsString = await toBase64(selectedFile); + } + + const dataToSend = { + ...formData, + picture: pictureAsString, + }; + try { + const response = await axios.post( + "https://fastcampus-chat.net/signup", + dataToSend, + { + headers: { + "content-type": "application/json", + serverId: import.meta.env.VITE_APP_SERVER_ID, + }, }, - }) - .then((res) => console.log(res)); + ); + if (response.status === 200) { + console.log("회원가입 성공:", response.data); + // 성공 처리 로직 구현 예정 + } + } catch (error) { + console.error("회원가입 실패:", error); + // 실패 처리 로직 구현 예정 + } }; + return ( <>
회원가입
- - - - +
+ ( + { + const file = event.target.files?.[0]; + if (file) { + if (file.size > 1024 * 1024) { + console.error("File size exceeds 1MB"); + } else { + setSelectedFile(file); + onChange(file); + } + } + }} + /> + )} + /> + ( + + )} + /> + {errors.id && {errors.id.message}} + + ( + + )} + /> + {errors.password && ( + {errors.password.message} + )} + + ( + + )} + /> + {errors.name && {errors.name.message}} + + + ); }; From 198ef61810a567f0b68243d4fe1b8b9e8c8a7d71 Mon Sep 17 00:00:00 2001 From: suyeonnnnnnn Date: Sat, 11 Nov 2023 02:41:14 +0900 Subject: [PATCH 2/4] Feat: Add duplicate ID check --- src/components/Login/SignUpModal/index.tsx | 26 +++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/components/Login/SignUpModal/index.tsx b/src/components/Login/SignUpModal/index.tsx index ea04d6bd..ed94006e 100644 --- a/src/components/Login/SignUpModal/index.tsx +++ b/src/components/Login/SignUpModal/index.tsx @@ -25,6 +25,21 @@ const SignUpModal = () => { }); const [selectedFile, setSelectedFile] = useState(null); + // ID 중복 체크 함수 + const checkIdDuplication = async (id: string): Promise => { + try { + const response = await axios.post( + "https://fastcampus-chat.net/check/id", + { id }, + ); + const data = response.data; + return !data.isDuplicated; + } catch (error) { + console.error("Error checking ID duplication", error); + return false; + } + }; + const onSubmit = async (formData: FormData) => { let pictureAsString: string | undefined; @@ -93,7 +108,16 @@ const SignUpModal = () => { { + const isNotDuplicated = await checkIdDuplication(id); + if (!isNotDuplicated) { + return "이미 사용중인 ID입니다."; + } + return true; + }, + }} render={({ field }) => ( )} From f59f1e836e706203f0d874b9742724db481cb386 Mon Sep 17 00:00:00 2001 From: suyeonnnnnnn Date: Sat, 11 Nov 2023 05:11:26 +0900 Subject: [PATCH 3/4] Feat: Add validation for ID field to only accept alphanumeric characters --- src/components/Login/SignUpModal/index.tsx | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/components/Login/SignUpModal/index.tsx b/src/components/Login/SignUpModal/index.tsx index ed94006e..75aceb33 100644 --- a/src/components/Login/SignUpModal/index.tsx +++ b/src/components/Login/SignUpModal/index.tsx @@ -25,24 +25,26 @@ const SignUpModal = () => { }); const [selectedFile, setSelectedFile] = useState(null); - // ID 중복 체크 함수 const checkIdDuplication = async (id: string): Promise => { try { const response = await axios.post( "https://fastcampus-chat.net/check/id", { id }, ); - const data = response.data; - return !data.isDuplicated; + // 서버가 ID가 중복되었다고 응답하면 true를 반환 + return response.data.isDuplicated; } catch (error) { console.error("Error checking ID duplication", error); + // 에러 발생 시 안전하게 중복으로 간주할 수 있습니다. return false; } }; + // 회원가입 제출 const onSubmit = async (formData: FormData) => { let pictureAsString: string | undefined; + // 파일을 Base64 문자열로 변환 const toBase64 = (file: File) => new Promise((resolve, reject) => { const reader = new FileReader(); @@ -59,6 +61,7 @@ const SignUpModal = () => { ...formData, picture: pictureAsString, }; + // 회원가입 요청 및 결과 처리 try { const response = await axios.post( "https://fastcampus-chat.net/signup", @@ -110,9 +113,14 @@ const SignUpModal = () => { control={control} rules={{ required: "ID는 필수입니다.", + pattern: { + value: /^[A-Za-z0-9]+$/, + message: "ID는 영문자와 숫자만 사용할 수 있습니다.", + }, validate: async (id) => { - const isNotDuplicated = await checkIdDuplication(id); - if (!isNotDuplicated) { + const isDuplicated = await checkIdDuplication(id); + console.log("ID 중복 확인 결과:", isDuplicated); + if (isDuplicated) { return "이미 사용중인 ID입니다."; } return true; From 75838657782e8cc155a1a08bfe1d0500e58a6efa Mon Sep 17 00:00:00 2001 From: suyeonnnnnnn Date: Sat, 11 Nov 2023 17:28:08 +0900 Subject: [PATCH 4/4] Feat: Add signUp success message, signUp error message --- src/components/Login/SignUpModal/index.tsx | 42 ++++++++++++++++------ 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/src/components/Login/SignUpModal/index.tsx b/src/components/Login/SignUpModal/index.tsx index 75aceb33..7a48eec4 100644 --- a/src/components/Login/SignUpModal/index.tsx +++ b/src/components/Login/SignUpModal/index.tsx @@ -1,6 +1,6 @@ import React, { useState } from "react"; import { useForm, Controller } from "react-hook-form"; -import { Input, Button, Text } from "@chakra-ui/react"; +import { Input, Button, Text, Alert, AlertIcon } from "@chakra-ui/react"; import useFetch from "../../../hooks/useFetch"; import axios from "axios"; @@ -15,6 +15,8 @@ const SignUpModal = () => { const { control, handleSubmit, + setError, + clearErrors, formState: { errors }, } = useForm(); const signUpFetch = useFetch({ @@ -24,6 +26,10 @@ const SignUpModal = () => { start: false, }); const [selectedFile, setSelectedFile] = useState(null); + const [signUpStatus, setSignUpStatus] = useState<{ + type: "success" | "error"; + message: string; + } | null>(null); const checkIdDuplication = async (id: string): Promise => { try { @@ -31,11 +37,9 @@ const SignUpModal = () => { "https://fastcampus-chat.net/check/id", { id }, ); - // 서버가 ID가 중복되었다고 응답하면 true를 반환 return response.data.isDuplicated; } catch (error) { console.error("Error checking ID duplication", error); - // 에러 발생 시 안전하게 중복으로 간주할 수 있습니다. return false; } }; @@ -75,17 +79,26 @@ const SignUpModal = () => { ); if (response.status === 200) { console.log("회원가입 성공:", response.data); - // 성공 처리 로직 구현 예정 + setSignUpStatus({ + type: "success", + message: "회원가입에 성공하였습니다.", + }); } } catch (error) { console.error("회원가입 실패:", error); - // 실패 처리 로직 구현 예정 + setSignUpStatus({ type: "error", message: "회원가입에 실패하였습니다." }); } }; return ( <>
회원가입
+ {signUpStatus && ( + + + {signUpStatus.message} + + )}
{ message: "ID는 영문자와 숫자만 사용할 수 있습니다.", }, validate: async (id) => { - const isDuplicated = await checkIdDuplication(id); - console.log("ID 중복 확인 결과:", isDuplicated); - if (isDuplicated) { - return "이미 사용중인 ID입니다."; + try { + const isDuplicated = await checkIdDuplication(id); + if (isDuplicated) { + setError("id", { + type: "manual", + message: "이미 사용중인 ID입니다.", + }); + return false; + } + clearErrors("id"); + return true; + } catch (error) { + console.error("ID 중복 확인 중 오류 발생:", error); + return "ID 중복 확인 중 오류가 발생했습니다."; } - return true; }, }} render={({ field }) => (