From 863872bb4f2466ea49f85f049fd035ed6e004110 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8B=A4=EB=B9=88?= <991012dabin@gmail.com> Date: Wed, 24 Jan 2024 03:42:50 +0900 Subject: [PATCH 01/15] =?UTF-8?q?feat:=20=EC=9D=B4=EB=A9=94=EC=9D=BC=20?= =?UTF-8?q?=EC=A4=91=EB=B3=B5=ED=99=95=EC=9D=B8=20API=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/index.ts | 1 + src/api/lib/getEmailAvailability.ts | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 src/api/lib/getEmailAvailability.ts diff --git a/src/api/index.ts b/src/api/index.ts index 8c830a83..b893067d 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -15,3 +15,4 @@ export { default as getDailyReport } from './lib/getDailyReport'; export { default as getCouponRanking } from './lib/getCouponRanking'; export { default as getLocalCouponUsage } from './lib/getLocalCouponUsage'; export { default as getMonthStatus } from './lib/getMonthStatus'; +export { default as getEmailAvailability } from './lib/getEmailAvailability'; diff --git a/src/api/lib/getEmailAvailability.ts b/src/api/lib/getEmailAvailability.ts new file mode 100644 index 00000000..e9697aff --- /dev/null +++ b/src/api/lib/getEmailAvailability.ts @@ -0,0 +1,18 @@ +import { AxiosError } from 'axios'; + +import { instance } from '..'; + +const getEmailAvailability = async (email: string) => { + try { + const response = await instance.get( + `/v1/member/check/email?email=${email}` + ); + return response.data; + } catch (error) { + if (error instanceof AxiosError) { + return error.response; + } + } +}; + +export default getEmailAvailability; From 4b7540c59eedb7961ba41802e4c33fe6ce0bae0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8B=A4=EB=B9=88?= <991012dabin@gmail.com> Date: Wed, 24 Jan 2024 04:22:15 +0900 Subject: [PATCH 02/15] =?UTF-8?q?feat:=20=EC=9D=B4=EB=A9=94=EC=9D=BC=20?= =?UTF-8?q?=EC=A4=91=EB=B3=B5=ED=99=95=EC=9D=B8=20API=20=EC=9D=91=EB=8B=B5?= =?UTF-8?q?=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/index.ts | 2 +- ...tEmailAvailability.ts => getEmailValid.ts} | 8 +-- src/components/SignUp/SignUpForm/index.tsx | 54 +++++++++++++++---- 3 files changed, 49 insertions(+), 15 deletions(-) rename src/api/lib/{getEmailAvailability.ts => getEmailValid.ts} (57%) diff --git a/src/api/index.ts b/src/api/index.ts index b893067d..4833a7e7 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -15,4 +15,4 @@ export { default as getDailyReport } from './lib/getDailyReport'; export { default as getCouponRanking } from './lib/getCouponRanking'; export { default as getLocalCouponUsage } from './lib/getLocalCouponUsage'; export { default as getMonthStatus } from './lib/getMonthStatus'; -export { default as getEmailAvailability } from './lib/getEmailAvailability'; +export { default as getEmailValid } from './lib/getEmailValid'; diff --git a/src/api/lib/getEmailAvailability.ts b/src/api/lib/getEmailValid.ts similarity index 57% rename from src/api/lib/getEmailAvailability.ts rename to src/api/lib/getEmailValid.ts index e9697aff..8d2d65d7 100644 --- a/src/api/lib/getEmailAvailability.ts +++ b/src/api/lib/getEmailValid.ts @@ -2,12 +2,12 @@ import { AxiosError } from 'axios'; import { instance } from '..'; -const getEmailAvailability = async (email: string) => { +const getEmailValid = async (email: string) => { try { const response = await instance.get( - `/v1/member/check/email?email=${email}` + `/v1/member/register/check/email?email=${email}` ); - return response.data; + return response; } catch (error) { if (error instanceof AxiosError) { return error.response; @@ -15,4 +15,4 @@ const getEmailAvailability = async (email: string) => { } }; -export default getEmailAvailability; +export default getEmailValid; diff --git a/src/components/SignUp/SignUpForm/index.tsx b/src/components/SignUp/SignUpForm/index.tsx index b1f0aa2a..27594deb 100644 --- a/src/components/SignUp/SignUpForm/index.tsx +++ b/src/components/SignUp/SignUpForm/index.tsx @@ -1,3 +1,4 @@ +import { useEffect, useState } from 'react'; import styled from '@emotion/styled'; import { FormProvider, useForm } from 'react-hook-form'; @@ -7,16 +8,49 @@ import { AuthInputNormal, AuthInputPassword } from '@components/Auth'; +import { getEmailValid } from 'src/api'; const SignUpForm = () => { const methods = useForm({ mode: 'all' }); - const { getFieldState, formState } = methods; + const { watch, getValues, getFieldState, formState } = methods; const { errors, isValid } = formState; + const emailValue: string = getValues('user_email'); const isEmailTouched = getFieldState('user_email', formState).isTouched; - const isEmailValid = isEmailTouched ? !errors?.user_email : false; + const isEmailValueValid = isEmailTouched ? !errors?.user_email : false; + + const emailValidInitialValue = { + message: '', + type: '' + }; + + const [emailValidMessage, setEmailValidMessage] = useState( + emailValidInitialValue + ); + + const checkEmailValid = async (event: React.FormEvent) => { + event.preventDefault(); + + const response = await getEmailValid(emailValue); + + if (response?.status === 200) { + setEmailValidMessage({ + message: '사용 가능한 아이디입니다.', + type: 'success' + }); + } else if (response?.status === 400) { + setEmailValidMessage({ + message: '이미 사용 중인 아이디입니다.', + type: 'failure' + }); + } + }; + + useEffect(() => { + setEmailValidMessage(emailValidInitialValue); + }, [watch('user_email')]); return ( @@ -48,22 +82,22 @@ const SignUpForm = () => { /> { - // TODO : 이메일 중복확인 API 요청 로직 - }} + disabled={!isEmailValueValid} + buttonFunc={checkEmailValid} /> + {/* 입력항목 자체 유효성 검사에 따라 나타날 유효성 메세지 */} {errors.user_email && ( {errors?.user_email?.message?.toString()} )} - {!errors.user_email && isEmailValid && ( - - 백엔드 응답에 따라 나타날 유효성 메세지 + {/* API 응답에 따라 나타날 유효성 메세지 */} + {!errors.user_email && isEmailValueValid && !!emailValue && ( + + {emailValidMessage.message} )} From c079341b8acdfece35e29780fd885791f695357f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8B=A4=EB=B9=88?= <991012dabin@gmail.com> Date: Wed, 24 Jan 2024 04:48:26 +0900 Subject: [PATCH 03/15] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20API=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/index.ts | 1 + src/api/lib/postSignUp.ts | 16 ++++++++++++++++ src/types/signUp.ts | 6 ++++++ 3 files changed, 23 insertions(+) create mode 100644 src/api/lib/postSignUp.ts diff --git a/src/api/index.ts b/src/api/index.ts index 4833a7e7..0c9c6ca9 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -16,3 +16,4 @@ export { default as getCouponRanking } from './lib/getCouponRanking'; export { default as getLocalCouponUsage } from './lib/getLocalCouponUsage'; export { default as getMonthStatus } from './lib/getMonthStatus'; export { default as getEmailValid } from './lib/getEmailValid'; +export { default as postSignUp } from './lib/postSignUp'; diff --git a/src/api/lib/postSignUp.ts b/src/api/lib/postSignUp.ts new file mode 100644 index 00000000..467fff67 --- /dev/null +++ b/src/api/lib/postSignUp.ts @@ -0,0 +1,16 @@ +import { AxiosError } from 'axios'; +import { instance } from '..'; +import { SignUpData } from '@/types/signUp'; + +const postSignUp = async (signUpData: SignUpData) => { + try { + const response = await instance.post('/v1/member/register', signUpData); + return response; + } catch (error) { + if (error instanceof AxiosError) { + return error.response; + } + } +}; + +export default postSignUp; diff --git a/src/types/signUp.ts b/src/types/signUp.ts index a0fd1e23..9f6965c6 100644 --- a/src/types/signUp.ts +++ b/src/types/signUp.ts @@ -3,3 +3,9 @@ export type SignUpFormStyleProps = { }; export type SignUpInputValidation = Pick; + +export type SignUpData = { + email: string; + password: string; + name: string; +}; From 7641f8b86a0e032dd0103075116323d3e6019284 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=8B=A4=EB=B9=88?= <991012dabin@gmail.com> Date: Thu, 25 Jan 2024 02:06:58 +0900 Subject: [PATCH 04/15] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20API=20=EC=9D=91=EB=8B=B5=EC=97=90=20=EB=94=B0?= =?UTF-8?q?=EB=A5=B8=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/lib/postSignUp.ts | 1 + src/components/SignUp/SignUpForm/index.tsx | 49 +++++++++++++++++----- src/pages/SignUp/index.tsx | 19 ++++++++- src/types/signUp.ts | 4 ++ 4 files changed, 60 insertions(+), 13 deletions(-) diff --git a/src/api/lib/postSignUp.ts b/src/api/lib/postSignUp.ts index 467fff67..29eb5914 100644 --- a/src/api/lib/postSignUp.ts +++ b/src/api/lib/postSignUp.ts @@ -1,4 +1,5 @@ import { AxiosError } from 'axios'; + import { instance } from '..'; import { SignUpData } from '@/types/signUp'; diff --git a/src/components/SignUp/SignUpForm/index.tsx b/src/components/SignUp/SignUpForm/index.tsx index 27594deb..b5759460 100644 --- a/src/components/SignUp/SignUpForm/index.tsx +++ b/src/components/SignUp/SignUpForm/index.tsx @@ -1,20 +1,29 @@ import { useEffect, useState } from 'react'; import styled from '@emotion/styled'; -import { FormProvider, useForm } from 'react-hook-form'; +import { + FieldValues, + FormProvider, + SubmitHandler, + useForm +} from 'react-hook-form'; -import { SignUpInputValidation } from '@/types/signUp'; +import { + SignUpData, + SignUpFormProps, + SignUpInputValidation +} from '@/types/signUp'; import { AuthButton, AuthInputNormal, AuthInputPassword } from '@components/Auth'; -import { getEmailValid } from 'src/api'; +import { getEmailValid, postSignUp } from 'src/api'; -const SignUpForm = () => { +const SignUpForm = ({ handleModalOpen }: SignUpFormProps) => { const methods = useForm({ mode: 'all' }); - const { watch, getValues, getFieldState, formState } = methods; + const { watch, getValues, getFieldState, formState, handleSubmit } = methods; const { errors, isValid } = formState; const emailValue: string = getValues('user_email'); @@ -48,13 +57,29 @@ const SignUpForm = () => { } }; + const onSubmit: SubmitHandler = async data => { + const formData: SignUpData = { + email: data.user_email, + password: data.user_password, + name: data.user_name + }; + + const response = await postSignUp(formData); + + if (response?.status === 201) { + // 회원가입 완료 페이지로 넘어가기 + } else { + handleModalOpen(); + } + }; + useEffect(() => { setEmailValidMessage(emailValidInitialValue); }, [watch('user_email')]); return ( -
+ { { - // TODO : 회원가입 API 요청 로직 - }} + disabled={!isValid || emailValidMessage.type === 'failure'} + buttonFunc={handleSubmit(onSubmit)} />
diff --git a/src/pages/SignUp/index.tsx b/src/pages/SignUp/index.tsx index e668abfe..cee2a9df 100644 --- a/src/pages/SignUp/index.tsx +++ b/src/pages/SignUp/index.tsx @@ -1,10 +1,19 @@ +import { useState } from 'react'; import styled from '@emotion/styled'; import logo from '@assets/icons/ic-logo.svg'; -import { Footer } from '@components/common'; +import { ErrorModal, Footer } from '@components/common'; import { SignUpForm, SignUpTitle } from '@components/SignUp'; const SignUp = () => { + const modalContent = { + text: '회원가입에 실패했습니다.', + errorText: '다시 시도해주세요.' + }; + const [isModalOpen, setIsModalOpen] = useState(false); + const handleModalClose = () => setIsModalOpen(false); + const handleModalOpen = () => setIsModalOpen(true); + return ( @@ -17,11 +26,17 @@ const SignUp = () => { - +