From feb3185a008ea00bea4303214c0b3c611315df53 Mon Sep 17 00:00:00 2001 From: Hyeona01 Date: Tue, 5 Nov 2024 02:10:37 +0900 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=90=9B=20fix:=20=ED=9A=8C=EC=9B=90?= =?UTF-8?q?=EA=B0=80=EC=9E=85=EC=8B=9C=20=EC=A4=91=EB=B3=B5=20=EA=B2=80?= =?UTF-8?q?=EC=82=AC=20=EC=97=90=EB=9F=AC=20=EC=B2=98=EB=A6=AC=20#109?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Signup/EmailInput.tsx | 47 ++++++++------ src/components/Signup/SignupInput.tsx | 88 +++++++++++++++++---------- src/constants/translation.ts | 4 ++ 3 files changed, 88 insertions(+), 51 deletions(-) diff --git a/src/components/Signup/EmailInput.tsx b/src/components/Signup/EmailInput.tsx index 8f42cbab..a24da939 100644 --- a/src/components/Signup/EmailInput.tsx +++ b/src/components/Signup/EmailInput.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import Input from '@/components/Common/Input'; import Button from '@/components/Common/Button'; import { useLocation, useNavigate } from 'react-router-dom'; @@ -21,25 +21,34 @@ const EmailInput = ({ email, onEmailChange, onSubmit }: EmailInputProps) => { const { data: ValidationResponse } = useGetEmailValidation(email); - // ===== handler ===== - const handleEmailChange = async (value: string) => { - onEmailChange(value); + useEffect(() => { + const validateEmailAsync = async () => { + if (!email) return; // 이메일이 없을 경우 바로 반환 - // 이메일 형식 유효성 검사 - if (!validateEmail(value, setEmailError, pathname)) { - setIsValid(false); // 유효성 검사 실패 시 버튼 비활성화 - return; - } + onEmailChange(email); - // 이메일 중복 검사 API 호출 결과 처리 - if (ValidationResponse && ValidationResponse.data.is_valid) { - setEmailError(null); // email 중복 오류 메시지 초기화 - setIsValid(true); // 중복 검사 통과 시 버튼 활성화 - } else { - setEmailError(signInputTranclation.invalidEmail[isEmployer(pathname)]); // email 중복 오류 메시지 - setIsValid(false); // 중복 검사 실패 시 버튼 비활성화 - } - }; + // 이메일 형식 유효성 검사 + if (!validateEmail(email, setEmailError, pathname)) { + setIsValid(false); // 유효성 검사 실패 시 버튼 비활성화 + return; + } + + // 이메일 중복 검사 API 호출 결과 처리 + if (ValidationResponse && ValidationResponse.data.is_valid === false) { + setEmailError( + signInputTranclation.emailAvailability[isEmployer(pathname)], + ); + setIsValid(false); // 중복 검사 실패 시 버튼 비활성화 + } else if (ValidationResponse && ValidationResponse.data.is_valid) { + setEmailError(null); // email 중복 오류 메시지 초기화 + setIsValid(true); // 중복 검사 통과 시 버튼 활성화 + } else { + setIsValid(false); // 예외처리 + } + }; + + validateEmailAsync(); + }, [email, pathname, ValidationResponse, onEmailChange]); const handleSignupClick = () => { if (!isValid) return; @@ -60,7 +69,7 @@ const EmailInput = ({ email, onEmailChange, onSubmit }: EmailInputProps) => { inputType="TEXT" placeholder={signInputTranclation.enterEmail[isEmployer(pathname)]} value={email} - onChange={handleEmailChange} + onChange={onEmailChange} canDelete={false} isInvalid={!isValid} /> diff --git a/src/components/Signup/SignupInput.tsx b/src/components/Signup/SignupInput.tsx index 09a8b252..d9903a02 100644 --- a/src/components/Signup/SignupInput.tsx +++ b/src/components/Signup/SignupInput.tsx @@ -31,44 +31,62 @@ const SignupInput = ({ // // ===== state ===== const [confirmPasswordValue, setConfirmPasswordValue] = useState(''); - const [idError, setIdError] = useState(null); - const [passwordError, setPasswordError] = useState(null); + const [idError, setIdError] = useState(); + const [passwordError, setPasswordError] = useState(); const [confirmPasswordError, setConfirmPasswordError] = useState< string | null >(null); const [isValid, setIsValid] = useState(false); + const [idValid, setIdValid] = useState(false); const { data: validationResponse } = useGetIdValidation(id); - // ===== handler ===== - const handleIdChange = async (value: string) => { - onIdChange(value); + // ID 검사 + useEffect(() => { + if (!id) { + setIdError(null); + setIdValid(false); + return; + } + onIdChange(id); // ID 유효성 검사 - if (validateId(value, setIdError, pathname)) { - // ID 중복 검사 API 호출 결과 처리 - if (validationResponse && validationResponse.data.is_valid) { - setIdError(null); // ID 중복 오류 메시지 초기화 - } else { - setIdError(signInputTranclation.invalidId[isEmployer(pathname)]); // ID 중복 오류 메시지 - } + if (validateId(id, setIdError, pathname)) { + setIdValid(true); } - }; - const handlePasswordChange = (value: string) => { - onPasswordChange(value); - validatePassword(value, setPasswordError, pathname); // 비밀번호 입력 시 유효성 검사 - }; + // ID 중복 검사 + const validateIdAsync = async () => { + if (validationResponse && validationResponse.data.is_valid === false) { + console.log(signInputTranclation.idAvailability[isEmployer(pathname)]); - const handleConfirmPasswordChange = (value: string) => { - setConfirmPasswordValue(value); - validatedConfirmPassword( - password, - value, - setConfirmPasswordError, - pathname, - ); - }; + setIdError(signInputTranclation.idAvailability[isEmployer(pathname)]); + setIdValid(false); + } + }; + + validateIdAsync(); + }, [id, onIdChange, validationResponse, idError, idValid]); + + // password 유효성 검사 + useEffect(() => { + if (password) { + onPasswordChange(password); + validatePassword(password, setPasswordError, pathname); + } + }, [password, pathname, onPasswordChange]); + + // password 일치 유효성 검사 + useEffect(() => { + if (confirmPasswordValue) { + validatedConfirmPassword( + password, + confirmPasswordValue, + setConfirmPasswordError, + pathname, + ); + } + }, [password, confirmPasswordValue, pathname]); // 모든 필드의 유효성 검사 후, Continue 버튼 활성화 useEffect(() => { @@ -81,6 +99,10 @@ const SignupInput = ({ } }, [id, password, confirmPasswordValue]); + const handleConfirmPasswordChange = (value: string) => { + setConfirmPasswordValue(value); + }; + return ( <>
@@ -96,11 +118,13 @@ const SignupInput = ({ inputType="TEXT" placeholder={signInputTranclation.enterId[isEmployer(pathname)]} value={id} - onChange={handleIdChange} + onChange={onIdChange} canDelete={false} - isInvalid={!!idError} + isInvalid={!idValid} /> - {idError &&

{idError}

} + {!idValid && ( +

{idError}

+ )}

@@ -112,9 +136,9 @@ const SignupInput = ({ signInputTranclation.enterPassword[isEmployer(pathname)] } value={password} - onChange={handlePasswordChange} + onChange={onPasswordChange} canDelete={false} - isInvalid={!!passwordError} + isInvalid={passwordError ? true : false} /> {passwordError && (

{passwordError}

@@ -132,7 +156,7 @@ const SignupInput = ({ value={confirmPasswordValue} onChange={handleConfirmPasswordChange} canDelete={false} - isInvalid={!!confirmPasswordError} + isInvalid={confirmPasswordError ? true : false} /> {confirmPasswordError && (

diff --git a/src/constants/translation.ts b/src/constants/translation.ts index accc1749..9484835f 100644 --- a/src/constants/translation.ts +++ b/src/constants/translation.ts @@ -19,6 +19,10 @@ export const signInputTranclation = { ko: '아이디를 입력해주세요', en: 'Invalid id format', }, + idAvailability: { + ko: '아이디가 이미 존재합니다', + en: 'This id already exists', + }, password: { ko: '비밀번호', en: 'Password', From 5e2a83a9cb9c543017cc6ef94c6742ee5f1eb0f5 Mon Sep 17 00:00:00 2001 From: Hyeona01 Date: Tue, 5 Nov 2024 02:15:07 +0900 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=90=9B=20fix:=20=EA=B8=B0=ED=83=80=20?= =?UTF-8?q?=EC=96=B8=EC=96=B4=20=EC=B6=94=EA=B0=80=20=EB=AA=A8=EB=8B=AC=20?= =?UTF-8?q?UI=20=ED=94=BD=EC=8A=A4=20#109?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Language/EtcLevelSection.tsx | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/components/Language/EtcLevelSection.tsx b/src/components/Language/EtcLevelSection.tsx index 23615d91..fe3c328e 100644 --- a/src/components/Language/EtcLevelSection.tsx +++ b/src/components/Language/EtcLevelSection.tsx @@ -23,13 +23,11 @@ const EtcLevelSection = ({ level, setLevel }: EtcLevelSectionProps) => {

{/* 레벨 선택 바텀시트 */} {bottomSheetOpen && ( -
- -
+ )} ); From 6d6bee6034cb1d7f3238735f2c7612fd3baba19f Mon Sep 17 00:00:00 2001 From: Hyeona01 Date: Tue, 5 Nov 2024 02:44:55 +0900 Subject: [PATCH 3/3] =?UTF-8?q?=E2=9C=A8=20feat:=20=ED=9A=8C=EC=9B=90?= =?UTF-8?q?=EA=B0=80=EC=9E=85=20=ED=9B=84=20=EC=9E=90=EB=8F=99=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=20=EB=A1=9C=EC=A7=81=20#109?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Signin/SigninInputSection.tsx | 7 +++++ src/components/Signup/SignupInput.tsx | 2 -- src/hooks/api/useAuth.ts | 28 +++++++++++++++++++- src/store/signup.ts | 18 +++++++++++++ 4 files changed, 52 insertions(+), 3 deletions(-) diff --git a/src/components/Signin/SigninInputSection.tsx b/src/components/Signin/SigninInputSection.tsx index 5bdff13a..0edd48c0 100644 --- a/src/components/Signin/SigninInputSection.tsx +++ b/src/components/Signin/SigninInputSection.tsx @@ -4,6 +4,7 @@ import Button from '@/components/Common/Button'; import { useLocation, useNavigate } from 'react-router-dom'; import { validateId, validatePassword } from '@/utils/signin'; import { useSignIn } from '@/hooks/api/useAuth'; +import { useUserInfoforSigninStore } from '@/store/signup'; const SigninInputSection = () => { const navigate = useNavigate(); @@ -17,6 +18,7 @@ const SigninInputSection = () => { const [isValid, setIsValid] = useState(false); const { mutate: signIn } = useSignIn(); + const { updateId, updatePassword } = useUserInfoforSigninStore(); // ===== handler ===== const handleIdChange = (value: string) => { @@ -36,6 +38,11 @@ const SigninInputSection = () => { formData.append('serial_id', idValue); formData.append('password', passwordValue); + // 전역 상태 업데이트 + updateId(idValue); + updatePassword(passwordValue); + + // api 훅 호출 signIn(formData); }; diff --git a/src/components/Signup/SignupInput.tsx b/src/components/Signup/SignupInput.tsx index d9903a02..66430f11 100644 --- a/src/components/Signup/SignupInput.tsx +++ b/src/components/Signup/SignupInput.tsx @@ -58,8 +58,6 @@ const SignupInput = ({ // ID 중복 검사 const validateIdAsync = async () => { if (validationResponse && validationResponse.data.is_valid === false) { - console.log(signInputTranclation.idAvailability[isEmployer(pathname)]); - setIdError(signInputTranclation.idAvailability[isEmployer(pathname)]); setIdValid(false); } diff --git a/src/hooks/api/useAuth.ts b/src/hooks/api/useAuth.ts index 2c9be1a2..b023a954 100644 --- a/src/hooks/api/useAuth.ts +++ b/src/hooks/api/useAuth.ts @@ -28,7 +28,10 @@ import { } from '@/utils/auth'; import { useNavigate } from 'react-router-dom'; import { useMutation, useQuery } from '@tanstack/react-query'; -import { useEmailTryCountStore } from '@/store/signup'; +import { + useEmailTryCountStore, + useUserInfoforSigninStore, +} from '@/store/signup'; import { useUserStore } from '@/store/user'; import { RESTYPE } from '@/types/api/common'; @@ -162,6 +165,9 @@ export const useTempSignUp = () => { // 2.5 기본 유저 회원가입 훅 export const useSignUp = () => { const navigate = useNavigate(); + const { id, password, updateId, updatePassword } = + useUserInfoforSigninStore(); + return useMutation({ mutationFn: signUp, onSuccess: (data: RESTYPE) => { @@ -169,6 +175,26 @@ export const useSignUp = () => { deleteTemporaryToken(); setAccessToken(data.data.access_token); setRefreshToken(data.data.refresh_token); + + // 회원가입 후 자동 로그인 + const signinData = new FormData(); + signinData.append('serial_id', id); + signinData.append('password', password); + + // 로그인 hook 호출 + const { mutate: signin } = useSignIn(); + signin(signinData, { + onSuccess: () => { + navigate('/splash'); + }, + onError: () => { + navigate('/signin'); + }, + }); + + // id, password 저장소 삭제 + updateId(''); + updatePassword(''); } }, onError: () => { diff --git a/src/store/signup.ts b/src/store/signup.ts index 835cae97..fcb67596 100644 --- a/src/store/signup.ts +++ b/src/store/signup.ts @@ -1,5 +1,6 @@ import { create } from 'zustand'; +// 회원가입시, 이메일 인증 횟수 제한 확인을 위한 전역상태 관리 type EmailTryCountStore = { try_cnt: number; updateTryCnt: (cnt: number) => void; @@ -9,3 +10,20 @@ export const useEmailTryCountStore = create()((set) => ({ try_cnt: 0, updateTryCnt: (try_cnt) => set(() => ({ try_cnt: try_cnt })), })); + +// 회원가입 후 자동 로그인을 위한 전역상태 관리 +type userInfoforSigninStore = { + id: string; + password: string; + updateId: (id: string) => void; + updatePassword: (password: string) => void; +}; + +export const useUserInfoforSigninStore = create()( + (set) => ({ + id: '', + password: '', + updateId: (id) => set(() => ({ id: id })), + updatePassword: (password) => set(() => ({ password: password })), + }), +);