diff --git a/src/apis/postGoogleOauth.ts b/src/apis/postGoogleOauth.ts new file mode 100644 index 00000000..66e9e737 --- /dev/null +++ b/src/apis/postGoogleOauth.ts @@ -0,0 +1,11 @@ +import httpClient from '.'; + +const postGoogleOauth = async (code: string) => { + const response = await httpClient.post('/auth/signIn/GOOGLE', { + redirectUri: process.env.NEXT_PUBLIC_GOOGLE_REDIRECT_URI, + token: code, + }); + return response.data; +}; + +export default postGoogleOauth; diff --git a/src/apis/postNaverOauth.ts b/src/apis/postNaverOauth.ts new file mode 100644 index 00000000..56434bb0 --- /dev/null +++ b/src/apis/postNaverOauth.ts @@ -0,0 +1,12 @@ +import httpClient from '.'; + +const postNaverOauth = async (code: string, state: string) => { + const response = await httpClient.post('/auth/signIn/NAVER', { + state, + redirectUri: process.env.NEXT_PUBLIC_NAVER_REDIRECT_URI, + token: code, + }); + return response.data; +}; + +export default postNaverOauth; diff --git a/src/hooks/useGoogleLogin.ts b/src/hooks/useGoogleLogin.ts new file mode 100644 index 00000000..46045f44 --- /dev/null +++ b/src/hooks/useGoogleLogin.ts @@ -0,0 +1,41 @@ +import postGoogleOauth from '@/apis/postGoogleOauth'; +import { toast } from '@/components/ui/use-toast'; +import { useMutation } from '@tanstack/react-query'; +import { isAxiosError } from 'axios'; +import { useRouter } from 'next/router'; + +const useGoogleLogin = () => { + const router = useRouter(); + + return useMutation({ + mutationFn: async (code: string) => { + const result = await postGoogleOauth(code); + localStorage.setItem('accessToken', result.accessToken); + localStorage.setItem('refreshToken', result.refreshToken); + return result; + }, + onSuccess: () => { + router.push('/epigrams'); + }, + onError: (error) => { + if (isAxiosError(error)) { + const status = error.response?.status; + + if (!status) return; + + if (status === 400) { + toast({ description: '잘못된 요청입니다. 요청을 확인해 주세요.', className: 'bg-state-error text-white font-semibold' }); + router.push('/auth/SignIn'); + return; + } + + if (status >= 500) { + toast({ description: '서버에 문제가 발생했습니다. 잠시 후 다시 시도해 주세요.', className: 'bg-state-error text-white font-semibold' }); + } + } + toast({ description: '알 수 없는 에러가 발생했습니다.', className: 'bg-state-error text-white font-semibold' }); + }, + }); +}; + +export default useGoogleLogin; diff --git a/src/hooks/useKakaoLogin.ts b/src/hooks/useKakaoLogin.ts index 14d8d81b..ea627868 100644 --- a/src/hooks/useKakaoLogin.ts +++ b/src/hooks/useKakaoLogin.ts @@ -15,7 +15,7 @@ const useKakaoLogin = () => { return result; }, onSuccess: () => { - router.push('/'); + router.push('/epigrams'); }, onError: (error) => { if (isAxiosError(error)) { diff --git a/src/hooks/useNaverLogin.ts b/src/hooks/useNaverLogin.ts new file mode 100644 index 00000000..9ff23b36 --- /dev/null +++ b/src/hooks/useNaverLogin.ts @@ -0,0 +1,42 @@ +import postNaverOauth from '@/apis/postNaverOauth'; +import { toast } from '@/components/ui/use-toast'; +import { useMutation } from '@tanstack/react-query'; +import { isAxiosError } from 'axios'; +import { useRouter } from 'next/router'; + +const useNaverLogin = () => { + const router = useRouter(); + + return useMutation({ + mutationFn: async ({ code, state }: { code: string; state: string }) => { + const result = await postNaverOauth(code, state); + localStorage.setItem('accessToken', result.accessToken); + localStorage.setItem('refreshToken', result.refreshToken); + return result; + }, + onSuccess: () => { + router.push('/epigrams'); + }, + onError: (error) => { + if (isAxiosError(error)) { + const status = error.response?.status; + + if (!status) return; + + if (status === 400) { + toast({ description: '잘못된 요청입니다. 요청을 확인해 주세요.', className: 'bg-state-error text-white font-semibold' }); + router.push('/auth/SignIn'); + return; + } + + if (status >= 500) { + toast({ description: '서버에 문제가 발생했습니다. 잠시 후 다시 시도해 주세요.', className: 'bg-state-error text-white font-semibold' }); + } + } + + toast({ description: '알 수 없는 에러가 발생했습니다.', className: 'bg-state-error text-white font-semibold' }); + }, + }); +}; + +export default useNaverLogin; diff --git a/src/hooks/useSignInMutation.ts b/src/hooks/useSignInMutation.ts index 2f4ebb5e..0ed58dc1 100644 --- a/src/hooks/useSignInMutation.ts +++ b/src/hooks/useSignInMutation.ts @@ -2,6 +2,7 @@ import { postSignin } from '@/apis/auth'; import { toast } from '@/components/ui/use-toast'; import { useMutation } from '@tanstack/react-query'; import { useRouter } from 'next/router'; +import { isAxiosError } from 'axios'; const useSigninMutation = () => { const router = useRouter(); @@ -11,10 +12,36 @@ const useSigninMutation = () => { onSuccess: (data) => { localStorage.setItem('accessToken', data.accessToken); localStorage.setItem('refreshToken', data.refreshToken); - router.push('/'); + router.push('/epigrams'); }, - onError: () => { - toast({ description: '이메일 혹은 비밀번호를 확인해주세요.', className: 'border-state-error text-state-error font-semibold' }); + onError: (error) => { + if (!isAxiosError(error)) { + return; + } + + const { status } = error.response || {}; + + if (status === 500) { + toast({ + description: '서버 오류가 발생했습니다. 잠시 후 다시 시도해주세요.', + className: 'border-state-error text-state-error font-semibold', + }); + return; + } + + // NOTE: status값은 항상 있으며 undefined와 숫자를 비교연산 할 수 없어 Number로 설정 + if (Number(status) >= 500) { + toast({ + description: '서버에서 예상치 못한 문제가 발생했습니다. 잠시 후 다시 시도해주세요.', + className: 'border-state-error text-state-error font-semibold', + }); + return; + } + + toast({ + description: '이메일 혹은 비밀번호를 확인해주세요.', + className: 'border-state-error text-state-error font-semibold', + }); }, }); }; diff --git a/src/pages/auth/SignIn.tsx b/src/pages/auth/SignIn.tsx index c1bfd025..8e165591 100644 --- a/src/pages/auth/SignIn.tsx +++ b/src/pages/auth/SignIn.tsx @@ -77,19 +77,24 @@ export default function SignIn() {

회원이 아니신가요?

- +
- + logo-naver - - logo-google - + {/* // FIXME: 구글 간편 로그인 리다이렉트시 500에러가 발생하는 부분으로 주석 처리하였음 */} + {/* */} + logo-google + {/* */} logo-kakao diff --git a/src/pages/auth/redirect/google-callback/index.ts b/src/pages/auth/redirect/google-callback/index.ts new file mode 100644 index 00000000..1f925901 --- /dev/null +++ b/src/pages/auth/redirect/google-callback/index.ts @@ -0,0 +1,21 @@ +import { useEffect } from 'react'; +import { useSearchParams } from 'next/navigation'; +import useGoogleLogin from '@/hooks/useGoogleLogin'; + +export default function Google() { + const searchParams = useSearchParams(); + const code = searchParams.get('code'); + const { mutate: login } = useGoogleLogin(); + + useEffect(() => { + if (code) { + login(code); + } else { + /* eslint-disable no-console */ + console.log(code); // code가 없을 때 콘솔에 출력 + } + }, [code, login]); +} + +// code가 없는 경우의 예시 http://localhost:3000/auth/redirect/kakao +// 토스트로 에러 메시지 띄우고, 로그인 페이지로 리다이렉트 diff --git a/src/pages/auth/redirect/naver/index.ts b/src/pages/auth/redirect/naver/index.ts new file mode 100644 index 00000000..ff844a3d --- /dev/null +++ b/src/pages/auth/redirect/naver/index.ts @@ -0,0 +1,19 @@ +import useNaverLogin from '@/hooks/useNaverLogin'; +import { useSearchParams } from 'next/navigation'; +import { useEffect } from 'react'; + +export default function Naver() { + const searchParams = useSearchParams(); + const code = searchParams.get('code'); + const state = searchParams.get('state'); + const { mutate: login } = useNaverLogin(); + + useEffect(() => { + if (code && state) { + login({ code, state }); + } else { + /* eslint-disable no-console */ + console.log(code, state); // code가 없을 때 콘솔에 출력 + } + }, [code, state, login]); +}