From 65d3e68452c91c0a8266c3a652b8b156446b41ee Mon Sep 17 00:00:00 2001 From: Jungu Lee <100949102+jobkaeHenry@users.noreply.github.com> Date: Tue, 28 Nov 2023 03:03:54 +0900 Subject: [PATCH] =?UTF-8?q?=ED=86=A0=EC=8A=A4=ED=8A=B8=ED=8C=9D=EC=97=85-?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20(#56)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Refactor : 리렌더 방지 * New : 글로벌팝업 에러핸들링 적용 * Minor : tsx -> ts 확장자 변경 * New : 글로벌팝업 적용 * New : 토큰제거로직 추가 --- client/src/components/ErrorPage.tsx | 6 ++-- client/src/components/GlobalToast.tsx | 12 +++++--- client/src/queries/auth/useLoginMutation.tsx | 10 +++---- client/src/queries/auth/useSignupMutation.tsx | 4 ++- .../queries/newPost/useNewPostMutation.tsx | 3 +- .../src/queries/post/useDeletePostMutation.ts | 9 +++--- .../src/queries/post/useLikePostMutation.tsx | 7 +++-- .../queries/post/useUnLikePostMutation.tsx | 5 ++-- client/src/queries/user/useFollowMutation.ts | 3 +- .../src/queries/user/useUnFollowMutation.ts | 4 ++- ...dingStore.tsx => useGlobalLoadingStore.ts} | 0 client/src/store/useGlobalSnackbarStore.ts | 4 +-- client/src/utils/errorHandler.ts | 30 +++++++++++++------ 13 files changed, 61 insertions(+), 36 deletions(-) rename client/src/store/{useGlobalLoadingStore.tsx => useGlobalLoadingStore.ts} (100%) diff --git a/client/src/components/ErrorPage.tsx b/client/src/components/ErrorPage.tsx index 4eb7e3f..10df301 100644 --- a/client/src/components/ErrorPage.tsx +++ b/client/src/components/ErrorPage.tsx @@ -3,7 +3,7 @@ import { Button, Paper } from "@mui/material"; import hasErrorPage from "@/assets/images/hasError.png"; import Image from "next/image"; import { useEffect } from "react"; -import errorHandler from "@/utils/errorHandler"; +import { useErrorHandler } from "@/utils/errorHandler"; const ErrorPage = ({ error, @@ -12,6 +12,8 @@ const ErrorPage = ({ error: Error & { digest?: string }; reset: () => void; }) => { + const errorHandler = useErrorHandler(); + useEffect(() => { errorHandler(error); }, [error]); @@ -24,7 +26,7 @@ const ErrorPage = ({ flexDirection: "column", alignItems: "center", height: "calc(100vh - 56px)", - gap:2 + gap: 2, }} > 에러임을 알림 diff --git a/client/src/components/GlobalToast.tsx b/client/src/components/GlobalToast.tsx index 371e31c..2931069 100644 --- a/client/src/components/GlobalToast.tsx +++ b/client/src/components/GlobalToast.tsx @@ -8,7 +8,11 @@ import { CheckCircle, Error, Warning } from "@mui/icons-material"; import { Snackbar, SnackbarContent, Stack } from "@mui/material"; const GlobalToast = () => { - const { isOpen, variant, message, closeToast } = useGlobalSnackbarStore(); + const isOpen = useGlobalSnackbarStore((store) => store.isOpen); + const variant = useGlobalSnackbarStore((store) => store.variant); + const message = useGlobalSnackbarStore((store) => store.message); + const closeToast = useGlobalSnackbarStore((store) => store.closeToast); + return ( { switch (variant) { case "danger": - return ; + return ; case "warning": - return ; + return ; default: - return ; + return ; } }; diff --git a/client/src/queries/auth/useLoginMutation.tsx b/client/src/queries/auth/useLoginMutation.tsx index 74ba379..2e0ac5a 100644 --- a/client/src/queries/auth/useLoginMutation.tsx +++ b/client/src/queries/auth/useLoginMutation.tsx @@ -5,8 +5,7 @@ import { useMutation, useQueryClient } from "@tanstack/react-query"; import { MyInfoQueryKeys } from "./useMyInfoQuery"; import { useRouter } from "next/navigation"; import HOME from "@/const/clientPath"; -import errorHandler from "@/utils/errorHandler"; -import { AxiosError } from "axios"; +import { useErrorHandler } from "@/utils/errorHandler"; import { useGlobalLoadingStore } from "@/store/useGlobalLoadingStore"; const useLoginMutation = () => { @@ -14,6 +13,7 @@ const useLoginMutation = () => { const queryClient = useQueryClient(); const router = useRouter(); const { setLoading } = useGlobalLoadingStore(); + const errorHandler = useErrorHandler(); return useMutation({ mutationKey: LoginMuataionKey.all, @@ -29,8 +29,7 @@ const useLoginMutation = () => { router.refresh(); router.push(HOME); }, - onError: (error) => - errorHandler(error), + onError: (error) => errorHandler(error), onSettled: () => { setLoading(false); }, @@ -52,6 +51,7 @@ export const LoginMuataionKey = { * @param id 유저아이디 * @returns 로그인뮤테이션 키 */ - byId: (id: SigninRequirement["id"]) => [...LoginMuataionKey.all, {id}] as const, + byId: (id: SigninRequirement["id"]) => + [...LoginMuataionKey.all, { id }] as const, }; export default useLoginMutation; diff --git a/client/src/queries/auth/useSignupMutation.tsx b/client/src/queries/auth/useSignupMutation.tsx index 509c721..c3dd030 100644 --- a/client/src/queries/auth/useSignupMutation.tsx +++ b/client/src/queries/auth/useSignupMutation.tsx @@ -5,11 +5,13 @@ import { SignupRequirement } from "@/types/auth/signupRequirement"; import { useMutation } from "@tanstack/react-query"; import useLoginMutation from "./useLoginMutation"; import { useGlobalLoadingStore } from "@/store/useGlobalLoadingStore"; -import errorHandler from "@/utils/errorHandler"; +import { useErrorHandler } from "@/utils/errorHandler"; const useSignupMutation = () => { const { mutate: loginHandler } = useLoginMutation(); const { setLoading } = useGlobalLoadingStore(); + const errorHandler = useErrorHandler(); + return useMutation({ mutationKey: signupMuataionKey.all, mutationFn: async (formData: SignupRequirement) => { diff --git a/client/src/queries/newPost/useNewPostMutation.tsx b/client/src/queries/newPost/useNewPostMutation.tsx index 494fd88..12d066f 100644 --- a/client/src/queries/newPost/useNewPostMutation.tsx +++ b/client/src/queries/newPost/useNewPostMutation.tsx @@ -3,9 +3,10 @@ import axios from "@/libs/axios"; import { POST_LIST } from "@/const/serverPath"; import { NewPostRequestInterface } from "@/types/newPost/NewPostInterface"; import getTokenFromLocalStorage from "@/utils/getTokenFromLocalStorage"; -import errorHandler from "@/utils/errorHandler"; +import { useErrorHandler } from "@/utils/errorHandler"; const useNewPostMutation = () => { + const errorHandler = useErrorHandler(); return useMutation({ mutationFn: async (formData: NewPostRequestInterface) => { const data = await usePostNewPostFn(formData); diff --git a/client/src/queries/post/useDeletePostMutation.ts b/client/src/queries/post/useDeletePostMutation.ts index 481767b..2a6cad0 100644 --- a/client/src/queries/post/useDeletePostMutation.ts +++ b/client/src/queries/post/useDeletePostMutation.ts @@ -2,18 +2,19 @@ import { REMOVE_POST } from "@/const/serverPath"; import { axiosPrivate } from "@/libs/axios"; import { useMutation } from "@tanstack/react-query"; import { useInvalidatePostList } from "./useGetPostListInfiniteQuery"; -import errorHandler from "@/utils/errorHandler"; +import { useErrorHandler } from "@/utils/errorHandler"; export const useDeletePostMutation = () => { const invalidatePreviousData = useInvalidatePostList(); + const errorHandler = useErrorHandler(); return useMutation({ mutationFn: (pk: number) => deletePostFn(pk), onSuccess: () => { invalidatePreviousData(); }, - onError:(err)=>{ - errorHandler(err) - } + onError: (err) => { + errorHandler(err); + }, }); }; diff --git a/client/src/queries/post/useLikePostMutation.tsx b/client/src/queries/post/useLikePostMutation.tsx index 09d6ede..f39908d 100644 --- a/client/src/queries/post/useLikePostMutation.tsx +++ b/client/src/queries/post/useLikePostMutation.tsx @@ -12,7 +12,7 @@ import { } from "./useGetPostListInfiniteQuery"; import getTokenFromLocalStorage from "@/utils/getTokenFromLocalStorage"; import { POST_LIKE_URL } from "@/const/serverPath"; -import errorHandler from "@/utils/errorHandler"; +import { useErrorHandler } from "@/utils/errorHandler"; import { PostcardContextInterface } from "@/store/PostCardContext"; import { useOptimisticUpdatePostList } from "@/queries/post/updator/useOptimisticUpdatePostList"; import { useOptimisticUpdatePostDetail } from "./updator/useOptimisticUpdatePostDetail"; @@ -26,7 +26,8 @@ import { postDetailQueryKey } from "./useGetPostDetailQuery"; */ const useLikePostMutation = (context?: PostcardContextInterface) => { const queryClient = useQueryClient(); - + + const errorHandler = useErrorHandler(); const postListUpdator = useOptimisticUpdatePostList({ type: "like" }); const postDetailUpdator = useOptimisticUpdatePostDetail({ type: "like" }); @@ -52,7 +53,7 @@ const useLikePostMutation = (context?: PostcardContextInterface) => { ); // [디테일쿼리] const detailQuerySnapshot = queryClient.getQueryData( - postDetailQueryKey.byId(String(id)), + postDetailQueryKey.byId(String(id)) ); // Optimastic Update // [리스트 쿼리] diff --git a/client/src/queries/post/useUnLikePostMutation.tsx b/client/src/queries/post/useUnLikePostMutation.tsx index ba19ac1..7a46171 100644 --- a/client/src/queries/post/useUnLikePostMutation.tsx +++ b/client/src/queries/post/useUnLikePostMutation.tsx @@ -12,7 +12,7 @@ import { } from "./useGetPostListInfiniteQuery"; import getTokenFromLocalStorage from "@/utils/getTokenFromLocalStorage"; import { POST_UN_LIKE_URL } from "@/const/serverPath"; -import errorHandler from "@/utils/errorHandler"; +import { useErrorHandler } from "@/utils/errorHandler"; import { PostcardContextInterface } from "@/store/PostCardContext"; import { useOptimisticUpdatePostList } from "@/queries/post/updator/useOptimisticUpdatePostList"; import { useOptimisticUpdatePostDetail } from "./updator/useOptimisticUpdatePostDetail"; @@ -26,6 +26,7 @@ import { postDetailQueryKey } from "./useGetPostDetailQuery"; */ const useLikePostMutation = (context?: PostcardContextInterface) => { const queryClient = useQueryClient(); + const errorHandler = useErrorHandler(); const postListUpdator = useOptimisticUpdatePostList({ type: "unlike" }); const postDetailUpdator = useOptimisticUpdatePostDetail({ type: "unlike" }); @@ -53,7 +54,7 @@ const useLikePostMutation = (context?: PostcardContextInterface) => { ); // [디테일쿼리] const detailQuerySnapshot = queryClient.getQueryData( - postDetailQueryKey.byId(String(id)), + postDetailQueryKey.byId(String(id)) ); // Optimastic Update diff --git a/client/src/queries/user/useFollowMutation.ts b/client/src/queries/user/useFollowMutation.ts index 824eb49..027a006 100644 --- a/client/src/queries/user/useFollowMutation.ts +++ b/client/src/queries/user/useFollowMutation.ts @@ -6,10 +6,11 @@ import { UserInfoQueryKey } from "./useUserInfoQuery"; import { UserInfoInterface } from "@/types/user/userInfoInterface"; import { MyInfoQueryKeys } from "../auth/useMyInfoQuery"; import { MyInfoInterface } from "@/types/auth/myInfo"; -import errorHandler from "@/utils/errorHandler"; +import { useErrorHandler } from "@/utils/errorHandler"; const useFollowMutation = () => { const queryClient = useQueryClient(); + const errorHandler = useErrorHandler(); return useMutation({ mutationFn: async (userNo: string) => await followUserMutatuibFn(userNo), /** diff --git a/client/src/queries/user/useUnFollowMutation.ts b/client/src/queries/user/useUnFollowMutation.ts index 4fc9b28..36cdc89 100644 --- a/client/src/queries/user/useUnFollowMutation.ts +++ b/client/src/queries/user/useUnFollowMutation.ts @@ -6,10 +6,12 @@ import { UserInfoQueryKey } from "./useUserInfoQuery"; import { UserInfoInterface } from "@/types/user/userInfoInterface"; import { MyInfoQueryKeys } from "../auth/useMyInfoQuery"; import { MyInfoInterface } from "@/types/auth/myInfo"; -import errorHandler from "@/utils/errorHandler"; +import { useErrorHandler } from "@/utils/errorHandler"; const useUnFollowMutation = () => { const queryClient = useQueryClient(); + const errorHandler = useErrorHandler(); + return useMutation({ mutationFn: async (userNo: string) => await followUserMutationFn(userNo), /** diff --git a/client/src/store/useGlobalLoadingStore.tsx b/client/src/store/useGlobalLoadingStore.ts similarity index 100% rename from client/src/store/useGlobalLoadingStore.tsx rename to client/src/store/useGlobalLoadingStore.ts diff --git a/client/src/store/useGlobalSnackbarStore.ts b/client/src/store/useGlobalSnackbarStore.ts index 74efec2..0552956 100644 --- a/client/src/store/useGlobalSnackbarStore.ts +++ b/client/src/store/useGlobalSnackbarStore.ts @@ -16,6 +16,4 @@ export const useGlobalSnackbarStore = create((set) => ({ fireToast: (message, variant = "neutral") => set({ isOpen: true, message, variant }), closeToast: () => set((prev) => ({ ...prev, message: "", isOpen: false })), -})); - -export const useFireToast =()=> useGlobalSnackbarStore((state)=>state.fireToast) +})); \ No newline at end of file diff --git a/client/src/utils/errorHandler.ts b/client/src/utils/errorHandler.ts index 09a1910..e4c069a 100644 --- a/client/src/utils/errorHandler.ts +++ b/client/src/utils/errorHandler.ts @@ -1,12 +1,24 @@ "use client"; -// import { useGlobalSnackbarStore } from "@/store/useGlobalSnackbarStore"; +import { useGlobalSnackbarStore } from "@/store/useGlobalSnackbarStore"; import { isAxiosError } from "axios"; +import { useCallback } from "react"; -export default function ErrorHandler(error: Error) { - // const { fireToast } = useGlobalSnackbarStore(); - if (isAxiosError(error) && error.response) { - // FIXME : Zustand 사용 연구 - // error.response.status === 401 && fireToast("로그인 후 이용 가능합니다"); - error.response.status === 401 && console.log("로그인 후 이용 가능합니다"); - } -} +/** + * Axios 에러의 status 코드를 판별해 적절한 토스트팝업을 표출해주는 함수를 리런하는 훅 + * @returns errorHandler (error)=>void + */ +export const useErrorHandler = () => { + const fireToast = useGlobalSnackbarStore((state) => state.fireToast); + + const errorHandler = useCallback((error: Error) => { + if (isAxiosError(error) && error.response) { + switch (error.response.status) { + case 401: + fireToast("로그인 후 이용 가능합니다"); + // 토큰이 만료된 경우가 대부분이므로 토큰제거 + localStorage.removeItem('accessToken') + } + } + }, []); + return errorHandler; +};