Skip to content

Commit

Permalink
feat: 최초가입 로직 추가
Browse files Browse the repository at this point in the history
  • Loading branch information
gyeongza committed Jul 9, 2024
1 parent f462746 commit 7ab7112
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 56 deletions.
7 changes: 6 additions & 1 deletion src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { MSWProvider } from '@/shared/components/MSWProvider';
import GoogleAnalytics from '@/shared/components/GoogleAnalytics';
import { getMetadata } from '@/shared/utils/metadata';
import { Toaster } from '@/shared/components/shadcn/ui/toaster';
import { UserStatusProvider } from '@/features/auth/context/userStatusContext';

const roboto = Roboto({
weight: ['400', '700'],
Expand All @@ -28,9 +29,13 @@ export default function RootLayout({
<html lang="ko">
<body className={roboto.className}>
{process.env.NEXT_PUBLIC_GA_ID && <GoogleAnalytics />}

<MSWProvider>
<QueryProvider>{children}</QueryProvider>
<QueryProvider>
<UserStatusProvider>{children}</UserStatusProvider>
</QueryProvider>
</MSWProvider>

<Toaster />
</body>
</html>
Expand Down
33 changes: 0 additions & 33 deletions src/features/auth/api/oauth.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import { ACCESS_TOKEN_LOCAL_STORAGE_KEY } from '../constants';
import LocalStorage from '@/shared/utils/localStorage';
import instance from '@/shared/api/axios/instance';
import { API_BASE_URL } from '@/shared/api/constants';
import { AxiosError } from 'axios';

export const getAccessToken = () => LocalStorage.getItem(ACCESS_TOKEN_LOCAL_STORAGE_KEY);

Expand All @@ -24,36 +21,6 @@ export const logoutToLoginPage = () => {
window.location.href = '/login?needLogin=true';
};

export const getLoginToken = async (code: string, isFirstUser: string) => {
try {
const response = await instance.get(`${API_BASE_URL}/api/v1/oauth/login/kakao?code=${code}&new=${isFirstUser}`);
const jwt = response.headers?.['authorization'] as string;

saveAccessToken(jwt);
} catch (error) {
alert('로그인 실패:');
console.error(error);
}
};

export const handleLogin = async (code: string, isFirstUser: string) => {
try {
await getLoginToken(code, isFirstUser);
} catch (error) {
if (error instanceof AxiosError) {
if (error.status === 400 && error.code === 'ERR_4010') {
// 최초 로그인 사용자인 경우
alert('최초 가입자입니다. 회원가입 페이지로 이동합니다.');
window.location.href = '/signup';
} else {
alert('로그인 실패:');
console.error(error);
throw error;
}
}
}
};

export const saveAccessToken = (response: string) => {
if (!response) {
removeAccessToken();
Expand Down
20 changes: 10 additions & 10 deletions src/features/auth/components/KakaoCallback.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,40 @@
import { useState, useEffect } from 'react';
import { useRouter, useSearchParams } from 'next/navigation';
import { useToast } from '@/shared/components/shadcn/ui/use-toast';
import { handleLogin } from '@/features/auth/api/oauth';
import { Loading } from '@/shared/components/Loading';
import { useLogin } from '../hooks/useLogin';

export default function KakaoCallback() {
const searchParams = useSearchParams();
const [authCode, setAuthCode] = useState<string | null>(null);
const [isFirstUser, setIsFirstUser] = useState<string | null>(null);
const { handleLogin } = useLogin();

const router = useRouter();
const { toast } = useToast();

useEffect(() => {
const code = searchParams.get('code');
const 최초가입자인가 = searchParams.get('new');
if (code && code !== authCode && 최초가입자인가 && 최초가입자인가 !== isFirstUser) {

if (code && code !== authCode) {
setAuthCode(code);
setIsFirstUser(최초가입자인가);
}
}, [searchParams, authCode, isFirstUser]);
}, [searchParams, authCode]);

useEffect(() => {
if (authCode && isFirstUser !== null) {
handleLogin(authCode, isFirstUser)
if (authCode) {
handleLogin(authCode)
.then(() => {
toast({
title: '로그인 완료!',
});
router.push('/my');
})
.catch((e) => {
.catch((e: unknown) => {
console.error(e);
router.push('/login');
});
}
}, [authCode, router, toast, isFirstUser]);
}, [authCode, router, toast]);

return (
<div className="flex w-full h-[80vh] justify-center items-center">
Expand Down
4 changes: 4 additions & 0 deletions src/features/auth/components/signup/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ import { ChangeEvent, FormEvent, KeyboardEvent, useRef, useState } from 'react';
import { getSignupStatus } from '../../api/getSignupStatus';
import { API_BASE_URL } from '@/shared/api/constants';
import { useRouter } from 'next/navigation';
import { useUserStatus } from '../../context/userStatusContext';

export default function Signup() {
const [phoneNumber, setPhoneNumber] = useState<string[]>(['010', '', '']);
const [isValid, setIsValid] = useState<boolean>(false);
const [error, setError] = useState<string>('');
const { setIsFirstTimeUser } = useUserStatus();
const router = useRouter();

const inputRefs = [useRef<HTMLInputElement>(null), useRef<HTMLInputElement>(null)];
Expand Down Expand Up @@ -60,6 +62,8 @@ export default function Signup() {
router.push('/login');
} else {
alert('최초 가입자입니다. 카카오 로그인으로 이동합니다.');
setIsFirstTimeUser(true);

window.location.href = `${API_BASE_URL}/api/v1/oauth/kakao`;
}
} else {
Expand Down
26 changes: 26 additions & 0 deletions src/features/auth/context/userStatusContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
'use client';

import { createContext, useContext, useState } from 'react';

interface UserStatusContextType {
isFirstTimeUser: boolean;
setIsFirstTimeUser: (value: boolean) => void;
}

const UserStatusContext = createContext<UserStatusContextType | undefined>(undefined);

export const UserStatusProvider = ({ children }: { children: React.ReactNode }) => {
const [isFirstTimeUser, setIsFirstTimeUser] = useState<boolean>(false);

return (
<UserStatusContext.Provider value={{ isFirstTimeUser, setIsFirstTimeUser }}>{children}</UserStatusContext.Provider>
);
};

export const useUserStatus = () => {
const context = useContext(UserStatusContext);
if (context === undefined) {
throw new Error('useUserStatus must be used within a UserStatusProvider');
}
return context;
};
51 changes: 51 additions & 0 deletions src/features/auth/hooks/useLogin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { AxiosError } from 'axios';
import { saveAccessToken } from '../api/oauth';
import { useUserStatus } from '../context/userStatusContext';
import instance from '@/shared/api/axios/instance';
import { API_BASE_URL } from '@/shared/api/constants';

export const useLogin = () => {
const { isFirstTimeUser, setIsFirstTimeUser } = useUserStatus();

const handleLogin = async (code: string) => {
try {
if (isFirstTimeUser) {
await getNewUserLoginToken(code);

return;
}

await getLoginToken(code);
} catch (error: unknown) {
if (error instanceof AxiosError) {
if (error.response?.data?.code === 'ERR_4010') {
// 최초 로그인 사용자인 경우
alert('최초 가입자입니다. 회원가입 페이지로 이동합니다.');
setIsFirstTimeUser(true);
window.location.href = '/signup';
} else {
alert('로그인 실패:');
console.error(error.response?.data);
}
}
}
};

const getLoginToken = async (code: string) => {
const response = await instance.get(`${API_BASE_URL}/api/v1/oauth/login/kakao?code=${code}`);
const jwt = response.headers?.['authorization'] as string;

saveAccessToken(jwt);
setIsFirstTimeUser(false);
};

const getNewUserLoginToken = async (code: string) => {
const response = await instance.get(`${API_BASE_URL}/api/v1/oauth/login/kakao/new?code=${code}`);
const jwt = response.headers?.['authorization'] as string;

saveAccessToken(jwt);
setIsFirstTimeUser(false);
};

return { handleLogin, getLoginToken, getNewUserLoginToken };
};
25 changes: 13 additions & 12 deletions src/shared/api/axios/instance.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import axios from 'axios';
import axios, { AxiosError } from 'axios';
import { API_BASE_URL } from '../constants';
import LocalStorage from '@/shared/utils/localStorage';
import { ACCESS_TOKEN_LOCAL_STORAGE_KEY } from '@/features/auth/constants';
Expand All @@ -24,17 +24,18 @@ instance.interceptors.request.use(
);

instance.interceptors.response.use(
async function (response) {
return response;
},
async function (error) {
const {
response: { status },
} = error;

if (status === 400 || status === 401) {
// 토큰이 없거나 잘못되었을 경우
logoutToLoginPage();
(response) => response,
(error: unknown) => {
if (error instanceof AxiosError && error.response) {
const { status } = error.response;

if (status === 400 || status === 401) {
// 토큰이 없거나 잘못되었을 경우
logoutToLoginPage();
}
} else {
// AxiosError가 아니거나 response가 없는 경우의 처리
console.error('An unexpected error occurred:', error);
}

return Promise.reject(error);
Expand Down

0 comments on commit 7ab7112

Please sign in to comment.