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/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) => ( { + 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, +}); 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/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; 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; } } }, []);