diff --git a/apps/admin/src/hooks/index.ts b/apps/admin/src/hooks/index.ts index 4eb03a2b..d9125912 100644 --- a/apps/admin/src/hooks/index.ts +++ b/apps/admin/src/hooks/index.ts @@ -4,5 +4,14 @@ import useAnswerInput from './useAnswerInput'; import useNavigation from './useNavigation'; import useAuth from './useAuth'; import useProblemEssentialInput from './useProblemEssentialInput'; +import useInvalidate from './useInvalidate'; -export { useModal, useSelectTag, useAnswerInput, useNavigation, useAuth, useProblemEssentialInput }; +export { + useModal, + useSelectTag, + useAnswerInput, + useNavigation, + useAuth, + useProblemEssentialInput, + useInvalidate, +}; diff --git a/apps/admin/src/hooks/useInvalidate.ts b/apps/admin/src/hooks/useInvalidate.ts new file mode 100644 index 00000000..436e4fcd --- /dev/null +++ b/apps/admin/src/hooks/useInvalidate.ts @@ -0,0 +1,47 @@ +import { $api } from '@apis'; +import { useQueryClient } from '@tanstack/react-query'; + +const useInvalidate = () => { + const queryClient = useQueryClient(); + + const invalidateAll = () => { + queryClient.invalidateQueries(); + }; + + const invalidateProblemSet = (problemSetId: number) => { + return Promise.all([ + queryClient.invalidateQueries({ + queryKey: $api.queryOptions('get', '/api/v1/problemSet/{problemSetId}', { + params: { + path: { + problemSetId, + }, + }, + }).queryKey, + }), + queryClient.invalidateQueries({ + queryKey: $api.queryOptions('get', '/api/v1/problemSet/search').queryKey, + }), + queryClient.invalidateQueries({ + queryKey: $api.queryOptions('get', '/api/v1/problemSet/confirm/search').queryKey, + }), + ]); + }; + + const invalidatePublish = (year: number, month: number) => { + queryClient.invalidateQueries({ + queryKey: $api.queryOptions('get', '/api/v1/publish/{year}/{month}', { + params: { + path: { + year, + month, + }, + }, + }).queryKey, + }); + }; + + return { invalidateAll, invalidateProblemSet, invalidatePublish }; +}; + +export default useInvalidate; diff --git a/apps/admin/src/main.tsx b/apps/admin/src/main.tsx index 4dcbbbed..23a783e6 100644 --- a/apps/admin/src/main.tsx +++ b/apps/admin/src/main.tsx @@ -8,7 +8,14 @@ import { routeTree } from './routeTree.gen'; import './styles/globals.css'; -const queryClient = new QueryClient(); +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + staleTime: Infinity, + gcTime: Infinity, + }, + }, +}); // Set up a Router instance const router = createRouter({ diff --git a/apps/admin/src/routes/_GNBLayout/problem-set/$problemSetId/index.tsx b/apps/admin/src/routes/_GNBLayout/problem-set/$problemSetId/index.tsx index 00b6c6ff..75850ea7 100644 --- a/apps/admin/src/routes/_GNBLayout/problem-set/$problemSetId/index.tsx +++ b/apps/admin/src/routes/_GNBLayout/problem-set/$problemSetId/index.tsx @@ -1,10 +1,4 @@ -import { - $api, - deleteProblemSet, - getProblemSetById, - putConfirmProblemSet, - putProblemSet, -} from '@apis'; +import { deleteProblemSet, getProblemSetById, putConfirmProblemSet, putProblemSet } from '@apis'; import { Button, ComponentWithLabel, @@ -20,9 +14,8 @@ import { Tag, TwoButtonModalTemplate, } from '@components'; -import { useModal } from '@hooks'; +import { useInvalidate, useModal } from '@hooks'; import { components } from '@schema'; -import { useQueryClient } from '@tanstack/react-query'; import { createFileRoute, useRouter } from '@tanstack/react-router'; import { useEffect, useState } from 'react'; import { useForm } from 'react-hook-form'; @@ -50,7 +43,7 @@ type ErrorResponse = components['schemas']['ErrorResponse']; function RouteComponent() { const { problemSetId } = Route.useParams(); const { navigate } = useRouter(); - const queryClient = useQueryClient(); + const { invalidateProblemSet } = useInvalidate(); const [problemSummaries, setProblemSummaries] = useState([]); const [currentProblemIndex, setCurrentProblemIndex] = useState(0); @@ -116,15 +109,7 @@ function RouteComponent() { }, { onSuccess: (data) => { - queryClient.invalidateQueries({ - queryKey: $api.queryOptions('get', '/api/v1/problemSet/{problemSetId}', { - params: { - path: { - problemSetId: Number(problemSetId), - }, - }, - }).queryKey, - }); + invalidateProblemSet(Number(problemSetId)); if (data.data === 'CONFIRMED') { toast.success('컨펌이 완료되었습니다'); } else { @@ -150,9 +135,7 @@ function RouteComponent() { }, { onSuccess: () => { - queryClient.invalidateQueries({ - queryKey: $api.queryOptions('get', '/api/v1/problemSet/search').queryKey, - }); + invalidateProblemSet(Number(problemSetId)); navigate({ to: '/problem-set' }); }, } @@ -284,15 +267,7 @@ function RouteComponent() { }, { onSuccess: () => { - queryClient.invalidateQueries({ - queryKey: $api.queryOptions('get', '/api/v1/problemSet/{problemSetId}', { - params: { - path: { - problemSetId: Number(problemSetId), - }, - }, - }).queryKey, - }); + invalidateProblemSet(Number(problemSetId)); toast.success('저장이 완료되었습니다'); setIsSaved(true); }, diff --git a/apps/admin/src/routes/_GNBLayout/problem-set/index.tsx b/apps/admin/src/routes/_GNBLayout/problem-set/index.tsx index 3cc9aec2..06ed3181 100644 --- a/apps/admin/src/routes/_GNBLayout/problem-set/index.tsx +++ b/apps/admin/src/routes/_GNBLayout/problem-set/index.tsx @@ -1,4 +1,4 @@ -import { $api, deleteProblemSet, getSearchProblemSet, postProblemSet } from '@apis'; +import { deleteProblemSet, getSearchProblemSet, postProblemSet } from '@apis'; import { Button, FloatingButton, @@ -10,8 +10,7 @@ import { SectionCard, TwoButtonModalTemplate, } from '@components'; -import { useModal } from '@hooks'; -import { useQueryClient } from '@tanstack/react-query'; +import { useInvalidate, useModal } from '@hooks'; import { createFileRoute, Link, useRouter } from '@tanstack/react-router'; import { getSearchProblemSetParamsType } from '@types'; import { useRef, useState } from 'react'; @@ -22,7 +21,7 @@ export const Route = createFileRoute('/_GNBLayout/problem-set/')({ }); function RouteComponent() { - const queryClient = useQueryClient(); + const { invalidateProblemSet } = useInvalidate(); const { navigate } = useRouter(); const [searchQuery, setSearchQuery] = useState({}); @@ -70,9 +69,7 @@ function RouteComponent() { }, { onSuccess: () => { - queryClient.invalidateQueries({ - queryKey: $api.queryOptions('get', '/api/v1/problemSet/search').queryKey, - }); + invalidateProblemSet(deleteProblemSetId.current ?? 0); closeDeleteModal(); }, } diff --git a/apps/admin/src/routes/_GNBLayout/problem/$problemId/index.tsx b/apps/admin/src/routes/_GNBLayout/problem/$problemId/index.tsx index d2426aa7..7ff08a3c 100644 --- a/apps/admin/src/routes/_GNBLayout/problem/$problemId/index.tsx +++ b/apps/admin/src/routes/_GNBLayout/problem/$problemId/index.tsx @@ -21,7 +21,6 @@ import { createFileRoute, useRouter } from '@tanstack/react-router'; import { Controller, SubmitHandler, useFieldArray, useForm } from 'react-hook-form'; import { produce } from 'immer'; import { - $api, deleteChildProblem, deleteProblems, getConceptTags, @@ -31,8 +30,7 @@ import { } from '@apis'; import { useEffect, useState } from 'react'; import { transformToProblemUpdateRequest } from '@utils'; -import { useQueryClient } from '@tanstack/react-query'; -import { useModal } from '@hooks'; +import { useInvalidate, useModal } from '@hooks'; import { Slide, ToastContainer, toast } from 'react-toastify'; export const Route = createFileRoute('/_GNBLayout/problem/$problemId/')({ @@ -44,7 +42,7 @@ type ProblemUpdateRequest = components['schemas']['ProblemUpdateRequest']; function RouteComponent() { // hooks - const queryClient = useQueryClient(); + const { invalidateAll } = useInvalidate(); const { navigate } = useRouter(); const { problemId } = Route.useParams(); const { isOpen: isTagModalOpen, openModal: openTagModal, closeModal: closeTagModal } = useModal(); @@ -161,13 +159,7 @@ function RouteComponent() { }, { onSuccess: () => { - queryClient.invalidateQueries({ - queryKey: $api.queryOptions('get', `/api/v1/problems/{id}`, { - params: { - path: { id: Number(problemId) }, - }, - }).queryKey, - }); + invalidateAll(); toast.success('저장이 완료되었습니다'); }, } @@ -185,9 +177,7 @@ function RouteComponent() { }, { onSuccess: () => { - queryClient.invalidateQueries({ - queryKey: $api.queryOptions('get', '/api/v1/problems/search').queryKey, - }); + invalidateAll(); navigate({ to: '/problem' }); }, } diff --git a/apps/admin/src/routes/_GNBLayout/problem/index.tsx b/apps/admin/src/routes/_GNBLayout/problem/index.tsx index 9644f21c..a4af7f39 100644 --- a/apps/admin/src/routes/_GNBLayout/problem/index.tsx +++ b/apps/admin/src/routes/_GNBLayout/problem/index.tsx @@ -1,4 +1,4 @@ -import { $api, deleteProblems, getConceptTags, getProblemsSearch } from '@apis'; +import { deleteProblems, getConceptTags, getProblemsSearch } from '@apis'; import { Button, FloatingButton, @@ -11,9 +11,8 @@ import { TagSelectModal, TwoButtonModalTemplate, } from '@components'; -import { useModal } from '@hooks'; +import { useInvalidate, useModal } from '@hooks'; import { IcDown } from '@svg'; -import { useQueryClient } from '@tanstack/react-query'; import { createFileRoute, Link } from '@tanstack/react-router'; import { getProblemsSearchParamsType } from '@types'; import { useRef, useState } from 'react'; @@ -25,7 +24,7 @@ export const Route = createFileRoute('/_GNBLayout/problem/')({ }); function RouteComponent() { - const queryClient = useQueryClient(); + const { invalidateAll } = useInvalidate(); const { isOpen, openModal, closeModal } = useModal(); const { @@ -69,9 +68,7 @@ function RouteComponent() { { onSuccess: () => { closeDeleteModal(); - queryClient.invalidateQueries({ - queryKey: $api.queryOptions('get', '/api/v1/problems/search').queryKey, - }); + invalidateAll(); }, } ); diff --git a/apps/admin/src/routes/_GNBLayout/problem/register/index.tsx b/apps/admin/src/routes/_GNBLayout/problem/register/index.tsx index f43f6bc4..aa701aac 100644 --- a/apps/admin/src/routes/_GNBLayout/problem/register/index.tsx +++ b/apps/admin/src/routes/_GNBLayout/problem/register/index.tsx @@ -1,9 +1,9 @@ -import { $api, postProblems } from '@apis'; +import { postProblems } from '@apis'; import { Button, Header, ProblemEssentialInput } from '@components'; import { createFileRoute, useRouter } from '@tanstack/react-router'; import { Controller, useForm } from 'react-hook-form'; import { components } from '@schema'; -import { useQueryClient } from '@tanstack/react-query'; +import { useInvalidate } from '@hooks'; export const Route = createFileRoute('/_GNBLayout/problem/register/')({ component: RouteComponent, @@ -13,7 +13,7 @@ type ProblemPostRequest = components['schemas']['ProblemPostRequest']; function RouteComponent() { const { navigate } = useRouter(); - const queryClient = useQueryClient(); + const { invalidateAll } = useInvalidate(); const { mutate } = postProblems(); const { @@ -40,9 +40,7 @@ function RouteComponent() { }, { onSuccess: (data) => { - queryClient.invalidateQueries({ - queryKey: $api.queryOptions('get', '/api/v1/problems/search').queryKey, - }); + invalidateAll(); const { id } = data.data; navigate({ to: `/problem/${id}` }); }, diff --git a/apps/admin/src/routes/_GNBLayout/publish/index.tsx b/apps/admin/src/routes/_GNBLayout/publish/index.tsx index ffd8e60b..d3d50946 100644 --- a/apps/admin/src/routes/_GNBLayout/publish/index.tsx +++ b/apps/admin/src/routes/_GNBLayout/publish/index.tsx @@ -3,9 +3,8 @@ import { IconButton, Modal, PlusButton, TwoButtonModalTemplate } from '@componen import { HTMLAttributes, useState } from 'react'; import { IcDeleteSm } from '@svg'; import { Link } from '@tanstack/react-router'; -import { $api, deletePublish, getPublish } from '@apis'; -import { useModal } from '@hooks'; -import { useQueryClient } from '@tanstack/react-query'; +import { deletePublish, getPublish } from '@apis'; +import { useInvalidate, useModal } from '@hooks'; import dayjs from 'dayjs'; import 'dayjs/locale/ko'; @@ -21,7 +20,7 @@ interface DayProps extends HTMLAttributes { } const Day = ({ fullDate, day, dayOfWeek, publishId, title, setId }: DayProps) => { - const queryClient = useQueryClient(); + const { invalidatePublish } = useInvalidate(); const { isOpen: isDeleteModalOpen, openModal: openDeleteModal, @@ -57,16 +56,7 @@ const Day = ({ fullDate, day, dayOfWeek, publishId, title, setId }: DayProps) => }, { onSuccess: () => { - queryClient.invalidateQueries({ - queryKey: $api.queryOptions('get', '/api/v1/publish/{year}/{month}', { - params: { - path: { - year: dayjs(fullDate).year(), - month: dayjs(fullDate).month() + 1, - }, - }, - }).queryKey, - }); + invalidatePublish(dayjs(fullDate).year(), dayjs(fullDate).month() + 1); closeDeleteModal(); }, } diff --git a/apps/admin/src/routes/_GNBLayout/publish/register/$publishDate/index.tsx b/apps/admin/src/routes/_GNBLayout/publish/register/$publishDate/index.tsx index dce8fc3d..dcbe067b 100644 --- a/apps/admin/src/routes/_GNBLayout/publish/register/$publishDate/index.tsx +++ b/apps/admin/src/routes/_GNBLayout/publish/register/$publishDate/index.tsx @@ -1,4 +1,4 @@ -import { $api, getConfirmProblemSet, postPublish } from '@apis'; +import { getConfirmProblemSet, postPublish } from '@apis'; import { Button, FloatingButton, @@ -8,7 +8,7 @@ import { SearchInput, SectionCard, } from '@components'; -import { useQueryClient } from '@tanstack/react-query'; +import { useInvalidate } from '@hooks'; import { createFileRoute, Link, useRouter } from '@tanstack/react-router'; import { getSearchProblemSetParamsType } from '@types'; import { useState } from 'react'; @@ -19,7 +19,7 @@ export const Route = createFileRoute('/_GNBLayout/publish/register/$publishDate/ }); function RouteComponent() { - const queryClient = useQueryClient(); + const { invalidatePublish } = useInvalidate(); const { navigate } = useRouter(); const { publishDate } = Route.useParams(); const dateArr = publishDate.split('-'); @@ -62,16 +62,7 @@ function RouteComponent() { }, { onSuccess: () => { - queryClient.invalidateQueries({ - queryKey: $api.queryOptions('get', '/api/v1/publish/{year}/{month}', { - params: { - path: { - year: Number(year), - month: Number(month), - }, - }, - }).queryKey, - }); + invalidatePublish(Number(year), Number(month)); navigate({ to: '/publish' }); }, } diff --git a/apps/service/src/app/problem/solve/[publishId]/[problemId]/child-problem/[childProblemId]/page.tsx b/apps/service/src/app/problem/solve/[publishId]/[problemId]/child-problem/[childProblemId]/page.tsx index 424536e9..03f0c32e 100644 --- a/apps/service/src/app/problem/solve/[publishId]/[problemId]/child-problem/[childProblemId]/page.tsx +++ b/apps/service/src/app/problem/solve/[publishId]/[problemId]/child-problem/[childProblemId]/page.tsx @@ -2,7 +2,7 @@ import { useState } from 'react'; import { useParams, useRouter } from 'next/navigation'; import { SubmitHandler, useForm } from 'react-hook-form'; -import { getChildProblemById, TanstackQueryClient } from '@apis'; +import { getChildProblemById } from '@apis'; import { putChildProblemSubmit, putChildProblemSkip } from '@apis'; import { AnswerInput, @@ -15,9 +15,8 @@ import { TwoButtonModalTemplate, AnswerModalTemplate, } from '@components'; -import { useModal } from '@hooks'; +import { useInvalidate, useModal } from '@hooks'; import { components } from '@schema'; -import { useQueryClient } from '@tanstack/react-query'; import { useChildProblemContext } from '@/hooks/problem'; @@ -31,7 +30,7 @@ const Page = () => { }>(); const router = useRouter(); const { childProblemLength, mainProblemImageUrl, onPrev, onNext } = useChildProblemContext(); - const queryClient = useQueryClient(); + const { invalidateAll } = useInvalidate(); const { isOpen, openModal, closeModal } = useModal(); const { @@ -76,7 +75,7 @@ const Page = () => { const handleSubmitAnswer: SubmitHandler<{ answer: string }> = async ({ answer }) => { const { data } = await putChildProblemSubmit(publishId, childProblemId, answer); const resultData = data?.data; - queryClient.invalidateQueries(); + invalidateAll(); setResult(resultData); if (resultData) { @@ -91,7 +90,7 @@ const Page = () => { const handleSkip = async () => { await putChildProblemSkip(publishId, childProblemId); - queryClient.invalidateQueries(); + invalidateAll(); onNext(); }; diff --git a/apps/service/src/app/problem/solve/[publishId]/[problemId]/main-problem/page.tsx b/apps/service/src/app/problem/solve/[publishId]/[problemId]/main-problem/page.tsx index 4ea14d11..87a214f1 100644 --- a/apps/service/src/app/problem/solve/[publishId]/[problemId]/main-problem/page.tsx +++ b/apps/service/src/app/problem/solve/[publishId]/[problemId]/main-problem/page.tsx @@ -2,7 +2,7 @@ import { useState } from 'react'; import { useParams, useRouter } from 'next/navigation'; import { SubmitHandler, useForm } from 'react-hook-form'; -import { getProblemById, putProblemSubmit, TanstackQueryClient } from '@apis'; +import { getProblemById, putProblemSubmit } from '@apis'; import { AnswerInput, Button, @@ -14,9 +14,8 @@ import { SmallButton, NavigationFooter, } from '@components'; -import { useModal } from '@hooks'; +import { useInvalidate, useModal } from '@hooks'; import { ProblemStatus } from '@types'; -import { useQueryClient } from '@tanstack/react-query'; import { useChildProblemContext } from '@/hooks/problem'; @@ -38,7 +37,7 @@ const Page = () => { const { publishId, problemId } = useParams<{ publishId: string; problemId: string }>(); const router = useRouter(); const { childProblemLength } = useChildProblemContext(); - const queryClient = useQueryClient(); + const { invalidateAll } = useInvalidate(); const { isOpen, openModal, closeModal } = useModal(); const [result, setResult] = useState(); @@ -73,7 +72,7 @@ const Page = () => { const handleSubmitAnswer: SubmitHandler<{ answer: string }> = async ({ answer }) => { const { data } = await putProblemSubmit(publishId, problemId, answer); const resultData = data?.data; - queryClient.invalidateQueries(); + invalidateAll(); setResult(resultData); if (resultData) { diff --git a/apps/service/src/hooks/common/index.ts b/apps/service/src/hooks/common/index.ts index 94a1b81c..6f25f49e 100644 --- a/apps/service/src/hooks/common/index.ts +++ b/apps/service/src/hooks/common/index.ts @@ -1,3 +1,4 @@ import useModal from './useModal'; +import useInvalidate from './useInvalidate'; -export { useModal }; +export { useModal, useInvalidate }; diff --git a/apps/service/src/hooks/common/useInvalidate.ts b/apps/service/src/hooks/common/useInvalidate.ts new file mode 100644 index 00000000..4cfcd8cf --- /dev/null +++ b/apps/service/src/hooks/common/useInvalidate.ts @@ -0,0 +1,13 @@ +import { useQueryClient } from '@tanstack/react-query'; + +const useInvalidate = () => { + const queryClient = useQueryClient(); + + const invalidateAll = () => { + queryClient.invalidateQueries(); + }; + + return { invalidateAll }; +}; + +export default useInvalidate;