From fb0a0067218beb9cee295fc448545838d53b890e Mon Sep 17 00:00:00 2001 From: Yang ga-hyeon Date: Thu, 20 Feb 2025 18:42:00 +0900 Subject: [PATCH 1/8] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?= =?UTF-8?q?=EC=8B=9C=20=EC=9C=A0=EC=A0=80=20=EC=A0=95=EB=B3=B4=20=EB=B6=88?= =?UTF-8?q?=EC=9D=BC=EC=B9=98=20alert?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/LoginForm.tsx | 9 +-------- src/services/apiClient.ts | 11 +++++------ src/services/authService.ts | 10 ++++++---- 3 files changed, 12 insertions(+), 18 deletions(-) diff --git a/src/features/LoginForm.tsx b/src/features/LoginForm.tsx index 928bd8fb..3e824be3 100644 --- a/src/features/LoginForm.tsx +++ b/src/features/LoginForm.tsx @@ -53,19 +53,12 @@ export default function LoginForm() { ); router.replace("/"); router.reload(); - } catch (error) { console.error("유저 정보 가져오기 실패", error); } }, onError: (error: any) => { - if (error.response) { - alert(error.message); - } else if (error.request) { - console.error(error.request); - } else { - console.error(error.message); - } + alert(error.message); }, }); diff --git a/src/services/apiClient.ts b/src/services/apiClient.ts index 328d0ec9..19edfb4d 100644 --- a/src/services/apiClient.ts +++ b/src/services/apiClient.ts @@ -29,14 +29,13 @@ apiClient.interceptors.response.use( async (error) => { if (error.response && error.response?.status === 401) { try { - const response = await authService.refreshToken(); - const accessToken = response; - setAccessToken(accessToken); - - error.config.headers["Authorization"] = `Bearer ${accessToken}`; + const newAccessToken = await authService.refreshToken(); + alert("새 토큰 발급!"); + error.config.headers["Authorization"] = `Bearer ${newAccessToken}`; return apiClient(error.config); } catch (error: any) { - alert(error.message); + console.error("토큰 갱신 실패", error); + return Promise.reject(error); } } diff --git a/src/services/authService.ts b/src/services/authService.ts index cda4a9e4..cd962489 100644 --- a/src/services/authService.ts +++ b/src/services/authService.ts @@ -103,15 +103,17 @@ const authService = { throw new Error("네이버 로그인에 실패했습니다."); } }, - refreshToken: async (): Promise => { + refreshToken: async () => { try { const response: RefreshTokenResponse = await api.post("/auth/refresh/token", true); - return response.accessToken; + const newAccessToken = response.accessToken; + setAccessToken(newAccessToken); + return newAccessToken; } catch (error: any) { if (error.response && error.response.status === UNAUTHORIZED) { - throw new Error("리프레시 토큰이 없거나 만료되었습니다."); + console.error("토큰 갱신 실패:", error); + throw new Error("로그인이 필요합니다."); } - throw new Error("토큰 발급 중 오류가 발생했습니다."); } }, }; From 694073adf1d61912ccd7fbbba80711e51e1991bd Mon Sep 17 00:00:00 2001 From: Yang ga-hyeon Date: Fri, 21 Feb 2025 09:16:54 +0900 Subject: [PATCH 2/8] =?UTF-8?q?feat:=20=EB=A6=AC=ED=94=84=EB=A0=88?= =?UTF-8?q?=EC=8B=9C=20=ED=86=A0=ED=81=B0=20=EC=BD=94=EB=93=9C=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/DetailMaker.tsx | 2 +- src/features/LoginForm.tsx | 2 -- src/services/apiClient.ts | 9 +++++++-- src/services/authService.ts | 15 +++++++++------ src/stores/useAuthStore.tsx | 6 +----- 5 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/features/DetailMaker.tsx b/src/features/DetailMaker.tsx index 09694a6f..06a020af 100644 --- a/src/features/DetailMaker.tsx +++ b/src/features/DetailMaker.tsx @@ -221,7 +221,7 @@ export default function RequestDetailDreamer() { {` @media (min-width: 744px) and (max-width: 2700px) { .main-container { - padding: 0 24px; + padding: 0 60px; } } `} diff --git a/src/features/LoginForm.tsx b/src/features/LoginForm.tsx index 3e824be3..1d135fbe 100644 --- a/src/features/LoginForm.tsx +++ b/src/features/LoginForm.tsx @@ -41,7 +41,6 @@ export default function LoginForm() { onSuccess: async () => { try { const userInfo = await getUserInfo(); - const profileInfo = await getProfileInfo(); setLogin( userInfo.nickName, @@ -49,7 +48,6 @@ export default function LoginForm() { userInfo.coconut, userInfo.email, userInfo.phoneNumber, - profileInfo.image, ); router.replace("/"); router.reload(); diff --git a/src/services/apiClient.ts b/src/services/apiClient.ts index 19edfb4d..70f874a7 100644 --- a/src/services/apiClient.ts +++ b/src/services/apiClient.ts @@ -1,6 +1,8 @@ import axios from "axios"; -import { getAccessToken, setAccessToken } from "@/utils/tokenUtils"; +import { getAccessToken } from "@/utils/tokenUtils"; import authService from "./authService"; +import router from "next/router"; +import useAuthStore from "@/stores/useAuthStore"; const apiClient = axios.create({ baseURL: process.env.NEXT_PUBLIC_API_URL, @@ -27,6 +29,7 @@ apiClient.interceptors.request.use( apiClient.interceptors.response.use( (response) => response, async (error) => { + const { setLogout } = useAuthStore(); if (error.response && error.response?.status === 401) { try { const newAccessToken = await authService.refreshToken(); @@ -35,7 +38,9 @@ apiClient.interceptors.response.use( return apiClient(error.config); } catch (error: any) { console.error("토큰 갱신 실패", error); - + setLogout(); + router.push("/login"); + alert("새로 로그인해주세요!"); return Promise.reject(error); } } diff --git a/src/services/authService.ts b/src/services/authService.ts index cd962489..27bb77c5 100644 --- a/src/services/authService.ts +++ b/src/services/authService.ts @@ -105,15 +105,18 @@ const authService = { }, refreshToken: async () => { try { - const response: RefreshTokenResponse = await api.post("/auth/refresh/token", true); + const response: RefreshTokenResponse = await api.post("/auth/refresh/token", true); //withCrediential const newAccessToken = response.accessToken; - setAccessToken(newAccessToken); + + if (!newAccessToken) { + throw new Error("서버에서 새로운 accessToken을 받지 못했습니다."); + } + console.log(newAccessToken); + return newAccessToken; } catch (error: any) { - if (error.response && error.response.status === UNAUTHORIZED) { - console.error("토큰 갱신 실패:", error); - throw new Error("로그인이 필요합니다."); - } + console.error("토큰 갱신 실패", error); + throw error; } }, }; diff --git a/src/stores/useAuthStore.tsx b/src/stores/useAuthStore.tsx index 7a106de0..6ab56bcb 100644 --- a/src/stores/useAuthStore.tsx +++ b/src/stores/useAuthStore.tsx @@ -10,14 +10,12 @@ interface AuthState { coconut: number; email?: string; phoneNumber?: string; - profileImage?: string; setLogin: ( userName: string, role: Role, coconut: number, email?: string, phoneNumber?: string, - profileImage?: string, ) => void; setLogout: () => void; setCoconut: (newCoconut: number) => void; @@ -39,8 +37,7 @@ const useAuthStore = create()( coconut: number, email?: string, phoneNumber?: string, - profileImage?: string, - ) => set({ isLoggedIn: true, nickName, role, coconut, email, phoneNumber, profileImage }), + ) => set({ isLoggedIn: true, nickName, role, coconut, email, phoneNumber }), setLogout: () => set({ isLoggedIn: false, @@ -49,7 +46,6 @@ const useAuthStore = create()( coconut: 0, email: "", phoneNumber: "", - profileImage: "", }), setCoconut: (newCoconut: number) => set({ coconut: newCoconut }), }), From dd14090bc2bcda2ede63bd26fdd39573786046bd Mon Sep 17 00:00:00 2001 From: Yang ga-hyeon Date: Fri, 21 Feb 2025 09:24:08 +0900 Subject: [PATCH 3/8] =?UTF-8?q?feat:=20useAuthStore=20=EB=90=98=EB=8F=8C?= =?UTF-8?q?=EB=A6=AC=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/stores/useAuthStore.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/stores/useAuthStore.tsx b/src/stores/useAuthStore.tsx index 6ab56bcb..7a106de0 100644 --- a/src/stores/useAuthStore.tsx +++ b/src/stores/useAuthStore.tsx @@ -10,12 +10,14 @@ interface AuthState { coconut: number; email?: string; phoneNumber?: string; + profileImage?: string; setLogin: ( userName: string, role: Role, coconut: number, email?: string, phoneNumber?: string, + profileImage?: string, ) => void; setLogout: () => void; setCoconut: (newCoconut: number) => void; @@ -37,7 +39,8 @@ const useAuthStore = create()( coconut: number, email?: string, phoneNumber?: string, - ) => set({ isLoggedIn: true, nickName, role, coconut, email, phoneNumber }), + profileImage?: string, + ) => set({ isLoggedIn: true, nickName, role, coconut, email, phoneNumber, profileImage }), setLogout: () => set({ isLoggedIn: false, @@ -46,6 +49,7 @@ const useAuthStore = create()( coconut: 0, email: "", phoneNumber: "", + profileImage: "", }), setCoconut: (newCoconut: number) => set({ coconut: newCoconut }), }), From 28d42ab1d0a33914e8f73727d9b3cb1111e4b44b Mon Sep 17 00:00:00 2001 From: Yang ga-hyeon Date: Fri, 21 Feb 2025 09:43:24 +0900 Subject: [PATCH 4/8] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20alert?= =?UTF-8?q?=20=EB=AC=B8=EA=B5=AC=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/LoginForm.tsx | 2 ++ src/services/apiClient.ts | 5 ++--- src/services/authService.ts | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/features/LoginForm.tsx b/src/features/LoginForm.tsx index 1d135fbe..3e824be3 100644 --- a/src/features/LoginForm.tsx +++ b/src/features/LoginForm.tsx @@ -41,6 +41,7 @@ export default function LoginForm() { onSuccess: async () => { try { const userInfo = await getUserInfo(); + const profileInfo = await getProfileInfo(); setLogin( userInfo.nickName, @@ -48,6 +49,7 @@ export default function LoginForm() { userInfo.coconut, userInfo.email, userInfo.phoneNumber, + profileInfo.image, ); router.replace("/"); router.reload(); diff --git a/src/services/apiClient.ts b/src/services/apiClient.ts index 70f874a7..1a84d87d 100644 --- a/src/services/apiClient.ts +++ b/src/services/apiClient.ts @@ -1,5 +1,5 @@ import axios from "axios"; -import { getAccessToken } from "@/utils/tokenUtils"; +import { getAccessToken, removeAccessToken } from "@/utils/tokenUtils"; import authService from "./authService"; import router from "next/router"; import useAuthStore from "@/stores/useAuthStore"; @@ -29,7 +29,6 @@ apiClient.interceptors.request.use( apiClient.interceptors.response.use( (response) => response, async (error) => { - const { setLogout } = useAuthStore(); if (error.response && error.response?.status === 401) { try { const newAccessToken = await authService.refreshToken(); @@ -38,7 +37,7 @@ apiClient.interceptors.response.use( return apiClient(error.config); } catch (error: any) { console.error("토큰 갱신 실패", error); - setLogout(); + removeAccessToken(); router.push("/login"); alert("새로 로그인해주세요!"); return Promise.reject(error); diff --git a/src/services/authService.ts b/src/services/authService.ts index 27bb77c5..ce254c59 100644 --- a/src/services/authService.ts +++ b/src/services/authService.ts @@ -65,7 +65,7 @@ const authService = { return response; } catch (error: any) { if (error.response && error.response.status === BAD_REQUEST) { - throw new Error("유저 정보가 일치하지 않습니다."); + throw new Error("이메일과 비밀번호를 확인해주세요."); } throw new Error("로그인 중 오류가 발생했습니다."); } From 06bfe41418f2ad0037f11a535d127467581a22a9 Mon Sep 17 00:00:00 2001 From: Yang ga-hyeon Date: Fri, 21 Feb 2025 10:58:51 +0900 Subject: [PATCH 5/8] =?UTF-8?q?feat:=20=EB=93=9C=EB=A6=AC=EB=A8=B8=20?= =?UTF-8?q?=ED=94=84=EB=A1=9C=ED=95=84=20=EB=93=B1=EB=A1=9D=EC=8B=9C=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=EC=A4=91=EC=9C=BC=EB=A1=9C=20=EB=B2=84?= =?UTF-8?q?=ED=8A=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/ProfileDreamer.tsx | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/features/ProfileDreamer.tsx b/src/features/ProfileDreamer.tsx index 0902d15b..18659e39 100644 --- a/src/features/ProfileDreamer.tsx +++ b/src/features/ProfileDreamer.tsx @@ -17,6 +17,7 @@ export default function ProfileDreamer() { const [selectedLocations, setSelectedLocations] = useState([]); const [isOpenImageModal, setIsOpenImageModal] = useState(false); const [profileImg, setProfileImg] = useState(null); + const [isSubmitting, setIsSubmitting] = useState(false); const router = useRouter(); @@ -50,9 +51,15 @@ export default function ProfileDreamer() { onError: (error: any) => { alert(error.message); }, + onSettled: () => { + setIsSubmitting(false); + }, }); const handleSubmit = async () => { + if (isSubmitting) return; + + setIsSubmitting(true); const profileData = { image: profileImg || undefined, tripTypes: selectedServices, @@ -68,7 +75,11 @@ export default function ProfileDreamer() { }; const isButtonDisabled = - selectedServices.length === 0 || selectedLocations.length === 0 || !profileImg || !userData; + selectedServices.length === 0 || + selectedLocations.length === 0 || + !profileImg || + !userData || + isSubmitting; return (
@@ -136,7 +147,7 @@ export default function ProfileDreamer() {