From 1c5428f8c6d9d40367cf537be1e83d9ec3535d4d Mon Sep 17 00:00:00 2001 From: ajin Date: Tue, 12 Nov 2024 13:01:54 +0900 Subject: [PATCH 1/3] =?UTF-8?q?Feat:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20form=20schema=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/schema/user.ts | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/schema/user.ts diff --git a/src/schema/user.ts b/src/schema/user.ts new file mode 100644 index 0000000..38ac06e --- /dev/null +++ b/src/schema/user.ts @@ -0,0 +1,31 @@ +import { z } from 'zod' + +export const RegisterUserSchema = z + .object({ + name: z + .string() + .min(1, { message: '이름을 입력해주세요' }) + .regex(/^[가-힣]+$/, { message: '공백없이 한글만 입력해주세요' }), + gender: z.enum(['male', 'female']), + year: z.number(), + month: z.number(), + day: z.number(), + }) + .refine( + (data) => { + const { year, month, day } = data + const date = new Date(year, month - 1, day) + + return ( + date.getFullYear() === year && + date.getMonth() === month - 1 && + date.getDate() === day + ) + }, + { + message: '유효한 날짜를 입력해주세요', + path: ['year', 'month', 'day'], + } + ) + +export type RegisterUserFields = z.infer From b5133eb6d782bef9c80430d57c245b9ea24e3537 Mon Sep 17 00:00:00 2001 From: ajin Date: Tue, 12 Nov 2024 22:00:05 +0900 Subject: [PATCH 2/3] =?UTF-8?q?Feat:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EC=A0=95=EB=B3=B4=20=EC=9E=85=EB=A0=A5=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/services/user/login.api.ts | 29 ++- .../LoginRedirectSection/index.tsx | 6 + src/pages/RegisterPage/index.tsx | 220 ++++++++++++++---- src/schema/user.ts | 41 ++-- 4 files changed, 222 insertions(+), 74 deletions(-) diff --git a/src/api/services/user/login.api.ts b/src/api/services/user/login.api.ts index 52ee069..0d85176 100644 --- a/src/api/services/user/login.api.ts +++ b/src/api/services/user/login.api.ts @@ -1,6 +1,6 @@ import { useQuery } from '@tanstack/react-query' -import { fetchInstance } from '@/api/instance' +import { authorizationInstance, fetchInstance } from '@/api/instance' type KakaoLoginParam = { code: string @@ -8,6 +8,7 @@ type KakaoLoginParam = { type KakaoLoginResponse = { userId: number + role: 'USER' | 'TEMP' } const kakaoLogin = async ({ code }: KakaoLoginParam) => { @@ -16,7 +17,7 @@ const kakaoLogin = async ({ code }: KakaoLoginParam) => { ) const accessToken = response.headers.authorization - return { accessToken, userId: response.data.userId } + return { accessToken, userId: response.data.userId, role: response.data.role } } export const useKakaoLogin = ({ code }: KakaoLoginParam) => { @@ -25,3 +26,27 @@ export const useKakaoLogin = ({ code }: KakaoLoginParam) => { queryFn: () => kakaoLogin({ code }), }) } + +export type RegisterUserRequestBody = { + name: string + gender: string + year: number + month: number + day: number +} + +type RegisterUeserResponse = { + userId: number + jwt: string + role: 'USER' +} + +export const registerUser = async (data: RegisterUserRequestBody) => { + const response = await authorizationInstance.post( + '/api/user/information', + data + ) + const accessToken = response.headers.authorization + + return { accessToken, userId: response.data.userId } +} diff --git a/src/pages/LoginRedirectPage/LoginRedirectSection/index.tsx b/src/pages/LoginRedirectPage/LoginRedirectSection/index.tsx index 8edbacf..6c0da33 100644 --- a/src/pages/LoginRedirectPage/LoginRedirectSection/index.tsx +++ b/src/pages/LoginRedirectPage/LoginRedirectSection/index.tsx @@ -19,6 +19,12 @@ export const LoginRedirectSection = ({ code }: LoginRedirectSectionProps) => { useEffect(() => { if (data) { + if (data.role === 'TEMP') { + setAuthToken(data.accessToken) + setMyUserId(data.userId) + navigate('/register') + return + } setAuthToken(data.accessToken) setMyUserId(data.userId) navigate('/') diff --git a/src/pages/RegisterPage/index.tsx b/src/pages/RegisterPage/index.tsx index ffdcc37..2e88547 100644 --- a/src/pages/RegisterPage/index.tsx +++ b/src/pages/RegisterPage/index.tsx @@ -1,16 +1,70 @@ +import { useForm } from 'react-hook-form' +import { useNavigate } from 'react-router-dom' + import { - Box, Button, Flex, Heading, Input, Radio, RadioGroup, - Stack, Text, } from '@chakra-ui/react' +import { zodResolver } from '@hookform/resolvers/zod' +import { useMutation } from '@tanstack/react-query' + +import { + RegisterUserRequestBody, + registerUser, +} from '@/api/services/user/login.api' +import { + Form, + FormControl, + FormField, + FormItem, + FormMessage, +} from '@/components/Form' +import { RegisterUserFields, RegisterUserSchema } from '@/schema/user' +import { useAuthTokenStore } from '@/stores/auth-token' +import { useMyUserIdStore } from '@/stores/my-user-id' export default function RegisterPage() { + const navigate = useNavigate() + + const form = useForm({ + resolver: zodResolver(RegisterUserSchema), + mode: 'onBlur', + defaultValues: { + name: '', + gender: undefined, + year: '', + month: '', + day: '', + }, + }) + + const setAuthToken = useAuthTokenStore((state) => state.setAuthToken) + const setMyUserId = useMyUserIdStore((state) => state.setMyUserId) + + const { mutate } = useMutation({ + mutationFn: (data: RegisterUserRequestBody) => registerUser(data), + onSuccess: (data) => { + setAuthToken(data.accessToken) + setMyUserId(data.userId) + navigate('/') + }, + }) + + const onValid = () => { + mutate({ + name: form.getValues('name'), + gender: form.getValues('gender'), + year: Number(form.getValues('year')), + month: Number(form.getValues('month')), + day: Number(form.getValues('day')), + }) + } + return ( 회원가입 @@ -26,48 +80,124 @@ export default function RegisterPage() { 가입을 통해 더 다양한 서비스를 만나보세요! - - - - 이름 - - - - - - - 성별 - - - - - 남 - - - 여 - - - - - - - - 나이 - - - - - +
+ + + ( + + + + 이름 + + + + + + )} + /> + ( + + + + 성별 + + + + + 남 + + + 여 + + + + + + + )} + /> + + + 생년월일 + + + ( + + + + + + + )} + /> + ( + + + + + + + )} + /> + ( + + + + + + + )} + /> + + + + +
+
) } diff --git a/src/schema/user.ts b/src/schema/user.ts index 38ac06e..03d67fd 100644 --- a/src/schema/user.ts +++ b/src/schema/user.ts @@ -1,31 +1,18 @@ import { z } from 'zod' -export const RegisterUserSchema = z - .object({ - name: z - .string() - .min(1, { message: '이름을 입력해주세요' }) - .regex(/^[가-힣]+$/, { message: '공백없이 한글만 입력해주세요' }), - gender: z.enum(['male', 'female']), - year: z.number(), - month: z.number(), - day: z.number(), - }) - .refine( - (data) => { - const { year, month, day } = data - const date = new Date(year, month - 1, day) - - return ( - date.getFullYear() === year && - date.getMonth() === month - 1 && - date.getDate() === day - ) - }, - { - message: '유효한 날짜를 입력해주세요', - path: ['year', 'month', 'day'], - } - ) +export const RegisterUserSchema = z.object({ + name: z + .string() + .min(1, { message: '이름을 입력해주세요' }) + .regex(/^[가-힣]+$/, { message: '공백없이 한글만 입력해주세요' }), + gender: z.enum(['male', 'female'], { + errorMap: () => ({ message: '성별을 선택해주세요' }), + }), + year: z + .string() + .regex(/^[0-9]{4}$/, { message: '연도는 4자리 숫자로 입력해 주세요' }), + month: z.string().regex(/^[0-9]{1,2}$/, { message: '숫자만 입력해주세요' }), + day: z.string().regex(/^[0-9]{1,2}$/, { message: '숫자만 입력해주세요' }), +}) export type RegisterUserFields = z.infer From c901c055357772cce694daa6963c08a2e8eea0c4 Mon Sep 17 00:00:00 2001 From: ajin Date: Tue, 12 Nov 2024 22:13:53 +0900 Subject: [PATCH 3/3] =?UTF-8?q?Feat:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=A6=AC=EB=8B=A4?= =?UTF-8?q?=EC=9D=B4=EB=A0=89=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/Layout/MainLayout/GlobalErrorFallback/index.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/pages/Layout/MainLayout/GlobalErrorFallback/index.tsx b/src/pages/Layout/MainLayout/GlobalErrorFallback/index.tsx index 56fbac6..2cd69b6 100644 --- a/src/pages/Layout/MainLayout/GlobalErrorFallback/index.tsx +++ b/src/pages/Layout/MainLayout/GlobalErrorFallback/index.tsx @@ -1,3 +1,4 @@ +import { useEffect } from 'react' import { FallbackProps } from 'react-error-boundary' import { useNavigate } from 'react-router-dom' @@ -18,6 +19,12 @@ export const GlobalErrorFallback = ({ const navigate = useNavigate() + useEffect(() => { + if (status === 428) { + navigate('/register') + } + }, [status, navigate]) + return ( }