From 8c87ef8cdaad45fa2c7cb42da846ce4a8194cf53 Mon Sep 17 00:00:00 2001 From: Jungu Lee <100949102+jobkaeHenry@users.noreply.github.com> Date: Mon, 13 Nov 2023 02:17:22 +0900 Subject: [PATCH] =?UTF-8?q?New:=20=EA=B8=80=EB=A1=9C=EB=B2=8C=20=EB=A1=9C?= =?UTF-8?q?=EB=94=A9=20=ED=8C=9D=EC=97=85=20=EC=B6=94=EA=B0=80=20(#33)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/package-lock.json | 38 +++++- client/package.json | 3 +- .../app/(protectedRoute)/new-post/page.tsx | 128 +++++++++--------- client/src/app/layout.tsx | 13 +- client/src/components/GlobalLoadingPopup.tsx | 24 ++++ client/src/queries/auth/useLoginMutation.tsx | 8 ++ client/src/queries/auth/useSignupMutation.tsx | 13 +- client/src/store/useGlobalLoadingStore.tsx | 11 ++ 8 files changed, 164 insertions(+), 74 deletions(-) create mode 100644 client/src/components/GlobalLoadingPopup.tsx create mode 100644 client/src/store/useGlobalLoadingStore.tsx diff --git a/client/package-lock.json b/client/package-lock.json index 3d73f59..f66a7b6 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -21,7 +21,8 @@ "react": "^18", "react-dom": "^18", "react-intersection-observer": "^9.5.3", - "sharp": "^0.32.6" + "sharp": "^0.32.6", + "zustand": "^4.4.6" }, "devDependencies": { "@storybook/addon-essentials": "7.5.2", @@ -22113,6 +22114,14 @@ } } }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/util": { "version": "0.12.5", "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", @@ -23230,6 +23239,33 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zustand": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.4.6.tgz", + "integrity": "sha512-Rb16eW55gqL4W2XZpJh0fnrATxYEG3Apl2gfHTyDSE965x/zxslTikpNch0JgNjJA9zK6gEFW8Fl6d1rTZaqgg==", + "dependencies": { + "use-sync-external-store": "1.2.0" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } } } } diff --git a/client/package.json b/client/package.json index 771b6ce..dfd394d 100644 --- a/client/package.json +++ b/client/package.json @@ -26,7 +26,8 @@ "react": "^18", "react-dom": "^18", "react-intersection-observer": "^9.5.3", - "sharp": "^0.32.6" + "sharp": "^0.32.6", + "zustand": "^4.4.6" }, "devDependencies": { "@storybook/addon-essentials": "7.5.2", diff --git a/client/src/app/(protectedRoute)/new-post/page.tsx b/client/src/app/(protectedRoute)/new-post/page.tsx index d56c7ae..b324d4b 100644 --- a/client/src/app/(protectedRoute)/new-post/page.tsx +++ b/client/src/app/(protectedRoute)/new-post/page.tsx @@ -6,9 +6,11 @@ import { Button, ButtonBase, Container, + IconButton, Paper, TextField, Toolbar, + Tooltip, Typography, } from "@mui/material"; import GoBackIcon from "@/assets/icons/GoBackIcon.svg"; @@ -21,6 +23,7 @@ import HOME from "@/const/clientPath"; import CameraIcon from "@/assets/icons/CameraIcon.svg"; import PinIcon from "@/assets/icons/PinIcon.svg"; import getTokenFromLocalStorage from "@/utils/getTokenFromLocalStorage"; +import { useGlobalLoadingStore } from "@/store/useGlobalLoadingStore"; export default function NewpostPage() { const [formValue, setFormValue] = useState({ @@ -35,7 +38,7 @@ export default function NewpostPage() { }: ChangeEvent) => { setFormValue((prev) => ({ ...prev, [target.name]: target.value })); }; - + const { setLoading } = useGlobalLoadingStore(); const token = getTokenFromLocalStorage(); const router = useRouter(); @@ -43,6 +46,7 @@ export default function NewpostPage() { const submitHandler = () => { let userId; let pk; + setLoading(true); axios .get("/user/me", { headers: { Authorization: token } }) .then((res) => { @@ -60,9 +64,7 @@ export default function NewpostPage() { const formData = new FormData(); if (file) { formData.append("image", file); - } - axios - .post(`/attach/resources/POST/${pk}`, formData, { + axios.post(`/attach/resources/POST/${pk}`, formData, { headers: { Authorization: token, "Content-Type": "multipart/form-data", @@ -72,10 +74,10 @@ export default function NewpostPage() { return formData; }, ], - }) - .then(() => { - router.push(HOME); }); + } + setLoading(false); + router.push(HOME); }); }); }; @@ -94,44 +96,42 @@ export default function NewpostPage() { return ( + {/* 최상단 앱바 */} - router.back()}> + router.back()}> - + 포스팅 - + - + + + {/* 검색창 */} - ), + startAdornment: , endAdornment: , }} onChange={changeHadler} + sx={{ px: 0 }} /> )} - - - { - if (e.target.files) { - setFile(e.target.files[0]); - } + + - - - - + > + + { + if (e.target.files) { + setFile(e.target.files[0]); + } + }} + /> + + + + + + + diff --git a/client/src/app/layout.tsx b/client/src/app/layout.tsx index 2dd1418..f4d9abb 100644 --- a/client/src/app/layout.tsx +++ b/client/src/app/layout.tsx @@ -8,6 +8,7 @@ import NavigationBar from "~/components/NavigationBar"; import "./globals.css"; import CustomQueryClientProvider from "@/components/queryClient/CustomQueryClientProvider"; import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; +import GlobalLoadingPopup from "./../components/GlobalLoadingPopup"; export const metadata: Metadata = { title: `${nameOfApp} | ${oneLineMessage}`, @@ -21,20 +22,20 @@ export const viewport: Viewport = { themeColor: "black", }; -export default function RootLayout({ - children, - Modal, -}: { +interface RootLayoutInterface { children: React.ReactNode; Modal: React.ReactNode; -}) { +} + +export default function RootLayout({ children, Modal }: RootLayoutInterface) { return ( - {Modal} + + {Modal} { + const { isLoading } = useGlobalLoadingStore(); + return ( + + + + ); +}; + +export default GlobalLoadingPopup; diff --git a/client/src/queries/auth/useLoginMutation.tsx b/client/src/queries/auth/useLoginMutation.tsx index b10a9eb..91bec8d 100644 --- a/client/src/queries/auth/useLoginMutation.tsx +++ b/client/src/queries/auth/useLoginMutation.tsx @@ -7,16 +7,21 @@ import { useRouter } from "next/navigation"; import HOME from "@/const/clientPath"; import errorHandler from "@/utils/errorHandler"; import { AxiosError } from "axios"; +import { useGlobalLoadingStore } from "@/store/useGlobalLoadingStore"; const useLoginMutation = () => { const { loginHandler } = useLogin(); const queryClient = useQueryClient(); const router = useRouter(); + const { setLoading } = useGlobalLoadingStore(); return useMutation({ mutationKey: LoginMuataionKey.all, mutationFn: async ({ id, password }: SigninRequirement) => await loginHandler({ id, password }), + onMutate: () => { + setLoading(true); + }, // 로그인에 성공한 경우, 토큰을 로컬스토리지에 저장, 이전 로그인 쿼리를 인벨리데이트 onSuccess: async ({ token }) => { localStorage?.setItem("accessToken", token); @@ -26,6 +31,9 @@ const useLoginMutation = () => { }, onError: (error: AxiosError<{ detailMessage: string }>) => errorHandler(error.response?.data.detailMessage ?? "에러가 발생했니다"), + onSettled: () => { + setLoading(false); + }, }); }; diff --git a/client/src/queries/auth/useSignupMutation.tsx b/client/src/queries/auth/useSignupMutation.tsx index 79df611..26f1561 100644 --- a/client/src/queries/auth/useSignupMutation.tsx +++ b/client/src/queries/auth/useSignupMutation.tsx @@ -1,15 +1,14 @@ -// import { SIGNIN } from "@/const/clientPath"; +"use client"; import { SIGNUP_API_PATH } from "@/const/serverPath"; import axios from "@/libs/axios"; import { SignupRequirement } from "@/types/auth/signupRequirement"; import { useMutation } from "@tanstack/react-query"; -import { useRouter } from "next/navigation"; import useLoginMutation from "./useLoginMutation"; +import { useGlobalLoadingStore } from "@/store/useGlobalLoadingStore"; const useSignupMutation = () => { - const router = useRouter(); const { mutate: loginHandler } = useLoginMutation(); - + const { setLoading } = useGlobalLoadingStore(); return useMutation({ mutationKey: signupMuataionKey.all, mutationFn: async (formData: SignupRequirement) => { @@ -17,9 +16,15 @@ const useSignupMutation = () => { await signupHandler(formData); return { id, password }; }, + onMutate: () => { + setLoading(true); + }, onSuccess: async ({ id, password }) => { loginHandler({ id, password }); }, + onSettled: () => { + setLoading(false); + }, }); }; diff --git a/client/src/store/useGlobalLoadingStore.tsx b/client/src/store/useGlobalLoadingStore.tsx new file mode 100644 index 0000000..f35bfbf --- /dev/null +++ b/client/src/store/useGlobalLoadingStore.tsx @@ -0,0 +1,11 @@ +import { create } from "zustand"; + +interface GlobalLoadingStore { + isLoading: boolean; + setLoading: (val: boolean) => void; +} + +export const useGlobalLoadingStore = create((set) => ({ + isLoading: false, + setLoading: (val) => set(() => ({ isLoading: val })), +}));