From c174c57a30fd107dc7667b4858ca77388a707af4 Mon Sep 17 00:00:00 2001 From: Jungu Lee <1zzangjun@gmail.com> Date: Thu, 30 Nov 2023 22:30:25 +0900 Subject: [PATCH 1/4] =?UTF-8?q?Refactor=20:=20=EB=A1=9C=EA=B7=B8=EC=95=84?= =?UTF-8?q?=EC=9B=83=20=ED=95=B8=EB=93=A4=EB=9F=AC=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../(protectedRoute)/user/setting/page.tsx | 15 +++-------- client/src/queries/auth/useLogoutMutation.tsx | 26 +++++++++++++++++++ 2 files changed, 29 insertions(+), 12 deletions(-) create mode 100644 client/src/queries/auth/useLogoutMutation.tsx diff --git a/client/src/app/(protectedRoute)/user/setting/page.tsx b/client/src/app/(protectedRoute)/user/setting/page.tsx index 528016b..a5be0e9 100644 --- a/client/src/app/(protectedRoute)/user/setting/page.tsx +++ b/client/src/app/(protectedRoute)/user/setting/page.tsx @@ -12,11 +12,11 @@ import { styled, } from "@mui/material"; import PostSeeMoreIcon from "@/assets/icons/PostSeeMoreIcon.svg"; -import { axiosBff } from "@/libs/axios"; -import { LOGOUT_BFF } from "@/const/serverPath"; +import useLogoutMutation from "@/queries/auth/useLogoutMutation"; const SettingPage = () => { const { data: myInfo } = useMyInfoQuery(); + const { mutate: logoutHandler } = useLogoutMutation(); return ( <> @@ -67,16 +67,7 @@ const SettingPage = () => { 계정 - diff --git a/client/src/queries/auth/useLogoutMutation.tsx b/client/src/queries/auth/useLogoutMutation.tsx new file mode 100644 index 0000000..0f23249 --- /dev/null +++ b/client/src/queries/auth/useLogoutMutation.tsx @@ -0,0 +1,26 @@ +import HOME from "@/const/clientPath"; +import { LOGOUT_BFF } from "@/const/serverPath"; +import { axiosBff } from "@/libs/axios"; +import { useGlobalSnackbarStore } from "@/store/useGlobalSnackbarStore"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { useRouter } from "next/navigation"; + +const useLogoutMutation = () => { + const router = useRouter(); + const queryClient = useQueryClient(); + + const fireToast = useGlobalSnackbarStore((state) => state.fireToast); + + return useMutation({ + mutationFn: logoutFn, + onSuccess: () => { + localStorage.removeItem("accessToken"); + queryClient.removeQueries(); + router.push(HOME); + fireToast("로그아웃이 완료되었습니다"); + }, + }); +}; +const logoutFn = () => axiosBff.post(LOGOUT_BFF); + +export default useLogoutMutation; From 117415b14fcb5edd7e6fe4a44ed48c68ef977410 Mon Sep 17 00:00:00 2001 From: Jungu Lee <1zzangjun@gmail.com> Date: Thu, 30 Nov 2023 22:31:05 +0900 Subject: [PATCH 2/4] =?UTF-8?q?Refactor=20:=20Axios=20Interceptor=20?= =?UTF-8?q?=ED=99=9C=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/hooks/useAxiosPrivate.ts | 17 +++++++++++++++++ client/src/libs/axios.ts | 16 ++-------------- 2 files changed, 19 insertions(+), 14 deletions(-) create mode 100644 client/src/hooks/useAxiosPrivate.ts diff --git a/client/src/hooks/useAxiosPrivate.ts b/client/src/hooks/useAxiosPrivate.ts new file mode 100644 index 0000000..be402d0 --- /dev/null +++ b/client/src/hooks/useAxiosPrivate.ts @@ -0,0 +1,17 @@ +import axios from "@/libs/axios"; +import getTokenFromLocalStorage from "@/utils/getTokenFromLocalStorage"; + +const useAxiosPrivate = () => { + axios.interceptors.request.use( + (config) => { + if (config.headers) { + config.headers.Authorization = getTokenFromLocalStorage(); + } + return config; + }, + (error) => Promise.reject(error) + ); + return axios; +}; + +export default useAxiosPrivate; diff --git a/client/src/libs/axios.ts b/client/src/libs/axios.ts index 1682032..ede7453 100644 --- a/client/src/libs/axios.ts +++ b/client/src/libs/axios.ts @@ -1,20 +1,8 @@ -import getTokenFromLocalStorage from "@/utils/getTokenFromLocalStorage"; import axios from "axios"; axios.defaults.xsrfCookieName = "csrftoken"; axios.defaults.xsrfHeaderName = "x-CSRFToken"; -/** - * 토큰을 싣고가는 요청 - */ -export const axiosPrivate = axios.create({ - baseURL: process.env.NEXT_PUBLIC_BASE_URL, - headers: { - "Content-Type": "application/json", - Authorization: getTokenFromLocalStorage(), - }, -}); - /** * 쿠키없이 가는 요청 */ @@ -25,5 +13,5 @@ export default axios.create({ baseURL: process.env.NEXT_PUBLIC_BASE_URL }); */ export const axiosBff = axios.create({ baseURL: process.env.NEXT_PUBLIC_CLIENT_BASE_URL, - withCredentials:true -}); \ No newline at end of file + withCredentials: true, +}); From 542c421dc17d2e2026e1894bc62d3ef25055e600 Mon Sep 17 00:00:00 2001 From: Jungu Lee <1zzangjun@gmail.com> Date: Thu, 30 Nov 2023 22:31:33 +0900 Subject: [PATCH 3/4] =?UTF-8?q?Refactor=20:=20=ED=86=A0=ED=81=B0=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EB=A1=9C=EC=A7=81=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../queries/attach/useDeleteAttachMutation.ts | 3 ++- .../src/queries/attach/useNewAttachMutation.ts | 3 ++- client/src/queries/auth/useLoginMutation.tsx | 1 - client/src/queries/auth/useMyInfoQuery.tsx | 7 +++---- .../src/queries/post/useDeletePostMutation.ts | 8 +++++--- .../post/useGetPostListInfiniteQuery.tsx | 5 +++-- .../src/queries/post/useUnLikePostMutation.tsx | 5 +---- .../queries/user/usePatchUserInfoMutation.ts | 3 ++- client/src/utils/errorHandler.ts | 17 +++++++++++++---- 9 files changed, 31 insertions(+), 21 deletions(-) diff --git a/client/src/queries/attach/useDeleteAttachMutation.ts b/client/src/queries/attach/useDeleteAttachMutation.ts index 3d49248..92f8941 100644 --- a/client/src/queries/attach/useDeleteAttachMutation.ts +++ b/client/src/queries/attach/useDeleteAttachMutation.ts @@ -1,5 +1,5 @@ import { REMOVE_FILE } from "@/const/serverPath"; -import { axiosPrivate } from "@/libs/axios"; +import useAxiosPrivate from "@/hooks/useAxiosPrivate"; import { useErrorHandler } from "@/utils/errorHandler"; import { useMutation, useQueryClient } from "@tanstack/react-query"; @@ -20,6 +20,7 @@ const useDeleteAttachMutation = () => { }; export const deleteAttachMutationFn = async (attachNo: string) => { + const axiosPrivate = useAxiosPrivate(); const { data } = await axiosPrivate.delete(REMOVE_FILE(attachNo)); return data; }; diff --git a/client/src/queries/attach/useNewAttachMutation.ts b/client/src/queries/attach/useNewAttachMutation.ts index da3842b..fd5878d 100644 --- a/client/src/queries/attach/useNewAttachMutation.ts +++ b/client/src/queries/attach/useNewAttachMutation.ts @@ -1,11 +1,11 @@ import { useMutation, useQueryClient } from "@tanstack/react-query"; -import { axiosPrivate } from "@/libs/axios"; import { ATTACH_FILE, ATTACH_FILE_ResourceType } from "@/const/serverPath"; import { useErrorHandler } from "./../../utils/errorHandler"; import { getPostListInfiniteQueryKey } from "./../post/useGetPostListInfiniteQuery"; import { postDetailQueryKey } from "../post/useGetPostDetailQuery"; import { MyInfoQueryKeys } from "../auth/useMyInfoQuery"; import { UserInfoQueryKey } from "../user/useUserInfoQuery"; +import useAxiosPrivate from "@/hooks/useAxiosPrivate"; export const useNewAttachMutation = () => { const errorHandler = useErrorHandler(); @@ -59,6 +59,7 @@ export const postImageFn = async ( file: File, { type, pk }: NewAttatchRequestUrl ) => { + const axiosPrivate = useAxiosPrivate() const formData = new FormData(); formData.append("image", file); diff --git a/client/src/queries/auth/useLoginMutation.tsx b/client/src/queries/auth/useLoginMutation.tsx index 2e0ac5a..226a510 100644 --- a/client/src/queries/auth/useLoginMutation.tsx +++ b/client/src/queries/auth/useLoginMutation.tsx @@ -26,7 +26,6 @@ const useLoginMutation = () => { onSuccess: async ({ token }) => { localStorage?.setItem("accessToken", token); queryClient.invalidateQueries({ queryKey: MyInfoQueryKeys.all }); - router.refresh(); router.push(HOME); }, onError: (error) => errorHandler(error), diff --git a/client/src/queries/auth/useMyInfoQuery.tsx b/client/src/queries/auth/useMyInfoQuery.tsx index 73f31e5..c16dada 100644 --- a/client/src/queries/auth/useMyInfoQuery.tsx +++ b/client/src/queries/auth/useMyInfoQuery.tsx @@ -1,6 +1,6 @@ "use client"; import { MY_INFO } from "@/const/serverPath"; -import { axiosPrivate } from "@/libs/axios"; +import useAxiosPrivate from "@/hooks/useAxiosPrivate"; import { MyInfoInterface } from "@/types/auth/myInfo"; import getTokenFromLocalStorage from "@/utils/getTokenFromLocalStorage"; import { useQuery } from "@tanstack/react-query"; @@ -16,9 +16,8 @@ export const getMyInfoByLocalStorage = async () => { if (!accessToken) { return null; } - const { data } = await axiosPrivate.get(MY_INFO, { - headers: { Authorization: accessToken }, - }); + const axiosPrivate = useAxiosPrivate(); + const { data } = await axiosPrivate.get(MY_INFO); return data; }; diff --git a/client/src/queries/post/useDeletePostMutation.ts b/client/src/queries/post/useDeletePostMutation.ts index 2a6cad0..0d58670 100644 --- a/client/src/queries/post/useDeletePostMutation.ts +++ b/client/src/queries/post/useDeletePostMutation.ts @@ -1,8 +1,8 @@ import { REMOVE_POST } from "@/const/serverPath"; -import { axiosPrivate } from "@/libs/axios"; import { useMutation } from "@tanstack/react-query"; import { useInvalidatePostList } from "./useGetPostListInfiniteQuery"; import { useErrorHandler } from "@/utils/errorHandler"; +import useAxiosPrivate from "@/hooks/useAxiosPrivate"; export const useDeletePostMutation = () => { const invalidatePreviousData = useInvalidatePostList(); @@ -18,5 +18,7 @@ export const useDeletePostMutation = () => { }); }; -export const deletePostFn = (pk: number) => - axiosPrivate.delete(REMOVE_POST(pk)); +export const deletePostFn = (pk: number) => { + const axiosPrivate = useAxiosPrivate(); + return axiosPrivate.delete(REMOVE_POST(pk)); +}; diff --git a/client/src/queries/post/useGetPostListInfiniteQuery.tsx b/client/src/queries/post/useGetPostListInfiniteQuery.tsx index 874eeff..b2f7476 100644 --- a/client/src/queries/post/useGetPostListInfiniteQuery.tsx +++ b/client/src/queries/post/useGetPostListInfiniteQuery.tsx @@ -1,9 +1,9 @@ import { useInfiniteQuery, useQueryClient } from "@tanstack/react-query"; -import axios from "@/libs/axios"; import { PostInterface } from "@/types/post/PostInterface"; import { AxiosRequestConfig } from "axios"; import getTokenFromLocalStorage from "@/utils/getTokenFromLocalStorage"; import { POST_LIST } from "@/const/serverPath"; +import useAxiosPrivate from "@/hooks/useAxiosPrivate"; export interface UseGetPostListQueryInterface extends GetPostListOptions { initialData?: AugmentedGetPostListResponse; @@ -81,7 +81,8 @@ export const getPostListQueryFn = async ({ }: GetPostListOptions & { headers?: AxiosRequestConfig["headers"]; }): Promise => { - const { data } = await axios.get(POST_LIST, { + const axiosPrivate = useAxiosPrivate() + const { data } = await axiosPrivate.get(POST_LIST, { baseURL: process.env.NEXT_PUBLIC_BASE_URL, params: { page, size, searchKeyword, searchUserNos }, headers, diff --git a/client/src/queries/post/useUnLikePostMutation.tsx b/client/src/queries/post/useUnLikePostMutation.tsx index 7a46171..abf74a8 100644 --- a/client/src/queries/post/useUnLikePostMutation.tsx +++ b/client/src/queries/post/useUnLikePostMutation.tsx @@ -101,7 +101,4 @@ export const useLikePostMutationFn = async (id: PostInterface["postNo"]) => { return data; }; -export default useLikePostMutation; -function postDetailUpdator(arg0: string) { - throw new Error("Function not implemented."); -} +export default useLikePostMutation; \ No newline at end of file diff --git a/client/src/queries/user/usePatchUserInfoMutation.ts b/client/src/queries/user/usePatchUserInfoMutation.ts index bda40cf..022fecb 100644 --- a/client/src/queries/user/usePatchUserInfoMutation.ts +++ b/client/src/queries/user/usePatchUserInfoMutation.ts @@ -1,10 +1,10 @@ import { PATCH_USER_INFO } from "@/const/serverPath"; -import { axiosPrivate } from "@/libs/axios"; import { UserInfoInterface } from "@/types/user/userInfoInterface"; import { useErrorHandler } from "@/utils/errorHandler"; import { useMutation, useQueryClient } from "@tanstack/react-query"; import { UserInfoQueryKey } from "./useUserInfoQuery"; import { useMyInfoQuery } from "../auth/useMyInfoQuery"; +import useAxiosPrivate from "@/hooks/useAxiosPrivate"; const usePatchUserInfoMutation = () => { const errorHandler = useErrorHandler(); @@ -26,6 +26,7 @@ const usePatchUserInfoMutation = () => { export const patchUserInfoMutateFn = async ( info: Partial ) => { + const axiosPrivate = useAxiosPrivate(); const { data } = await axiosPrivate.patch(PATCH_USER_INFO, { ...info }); return data; }; diff --git a/client/src/utils/errorHandler.ts b/client/src/utils/errorHandler.ts index e4c069a..727cd3e 100644 --- a/client/src/utils/errorHandler.ts +++ b/client/src/utils/errorHandler.ts @@ -2,6 +2,8 @@ import { useGlobalSnackbarStore } from "@/store/useGlobalSnackbarStore"; import { isAxiosError } from "axios"; import { useCallback } from "react"; +import getTokenFromLocalStorage from "./getTokenFromLocalStorage"; +import { useGlobalLoadingStore } from "@/store/useGlobalLoadingStore"; /** * Axios 에러의 status 코드를 판별해 적절한 토스트팝업을 표출해주는 함수를 리런하는 훅 @@ -9,14 +11,21 @@ import { useCallback } from "react"; */ export const useErrorHandler = () => { const fireToast = useGlobalSnackbarStore((state) => state.fireToast); - + const setLoading = useGlobalLoadingStore((state) => state.setLoading); const errorHandler = useCallback((error: Error) => { + setLoading(false); if (isAxiosError(error) && error.response) { switch (error.response.status) { case 401: - fireToast("로그인 후 이용 가능합니다"); - // 토큰이 만료된 경우가 대부분이므로 토큰제거 - localStorage.removeItem('accessToken') + // 토큰이 만료된 경우 + if (getTokenFromLocalStorage()) { + fireToast("세션이 만료되었습니다."); + localStorage.removeItem("accessToken"); + } else fireToast("로그인 후 이용 가능합니다"); + return; + case 403: + fireToast("권한이 없습니다"); + return; } } }, []); From 9f4bbd819d59db2536b39f789fd6ebdd0ceb7085 Mon Sep 17 00:00:00 2001 From: Jungu Lee <1zzangjun@gmail.com> Date: Thu, 30 Nov 2023 22:31:49 +0900 Subject: [PATCH 4/4] =?UTF-8?q?Minor=20:=20=EC=8A=A4=ED=83=80=EC=9D=BC=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/components/ModalWrapper.tsx | 1 - client/src/components/SearchHistory.tsx | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/components/ModalWrapper.tsx b/client/src/components/ModalWrapper.tsx index 28b20a6..d10328e 100644 --- a/client/src/components/ModalWrapper.tsx +++ b/client/src/components/ModalWrapper.tsx @@ -39,7 +39,6 @@ const ModalWrapper = ({ children, disableBox }: ModalInterface) => { p: disableBox ? 0 : 4, maxWidth: "90%", maxHeight: "90%", - overflowY: "auto", }} > <>{children} diff --git a/client/src/components/SearchHistory.tsx b/client/src/components/SearchHistory.tsx index 873ce5b..5b1f382 100644 --- a/client/src/components/SearchHistory.tsx +++ b/client/src/components/SearchHistory.tsx @@ -40,10 +40,11 @@ const SearchHistory = ({ storageKey, onClick }: SearchHistoryProps) => { 전체 삭제 - + {searchHistory.map((keyword) => (