From fc10e12e9533c941c2b54e8f1de7434a40b8e12e Mon Sep 17 00:00:00 2001 From: Seok93 Date: Sun, 29 Sep 2024 12:07:52 +0900 Subject: [PATCH 1/2] =?UTF-8?q?Refactor:=20#173=20React=20Query=EC=9D=98?= =?UTF-8?q?=20Query=20Key=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/query/useProjectQuery.ts | 5 +- src/hooks/query/useStatusQuery.ts | 21 +++--- src/hooks/query/useTaskQuery.ts | 37 ++++++----- src/hooks/query/useTeamQuery.ts | 39 +++++------ src/utils/queryKeyGenergator.ts | 102 +++++++++++++++++++++++++++++ 5 files changed, 157 insertions(+), 47 deletions(-) create mode 100644 src/utils/queryKeyGenergator.ts diff --git a/src/hooks/query/useProjectQuery.ts b/src/hooks/query/useProjectQuery.ts index a13ed15b..ab6d2d15 100644 --- a/src/hooks/query/useProjectQuery.ts +++ b/src/hooks/query/useProjectQuery.ts @@ -1,4 +1,5 @@ import { useQuery } from '@tanstack/react-query'; +import { generateProjectsQueryKey, generateProjectUsersQueryKey } from '@utils/queryKeyGenergator'; import { getProjectList, getProjectUserRoleList } from '@services/projectService'; import type { Team } from '@/types/TeamType'; @@ -13,7 +14,7 @@ export function useReadProjects(teamId: Team['teamId']) { isError: isProjectError, error: projectError, } = useQuery({ - queryKey: ['teams', teamId, 'projects'], + queryKey: generateProjectsQueryKey(teamId), queryFn: async () => { const { data } = await getProjectList(teamId); return data; @@ -31,7 +32,7 @@ export function useReadProjectUserRoleList(projectId: Project['projectId']) { isError: isErrorProjectUserRole, error: projectUserRoleError, } = useQuery({ - queryKey: ['projects', projectId, 'users'], + queryKey: generateProjectUsersQueryKey(projectId), queryFn: async () => { const { data } = await getProjectUserRoleList(projectId); return data; diff --git a/src/hooks/query/useStatusQuery.ts b/src/hooks/query/useStatusQuery.ts index f666a645..2cbf553f 100644 --- a/src/hooks/query/useStatusQuery.ts +++ b/src/hooks/query/useStatusQuery.ts @@ -3,6 +3,7 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import useToast from '@hooks/useToast'; import { PROJECT_STATUS_COLORS } from '@constants/projectStatus'; import { createStatus, getStatusList, updateStatus, updateStatusesOrder } from '@services/statusService'; +import { generateProjectQueryKey, generateStatusesQueryKey, generateTasksQueryKey } from '@utils/queryKeyGenergator'; import type { Project } from '@/types/ProjectType'; import type { TaskListWithStatus } from '@/types/TaskType'; @@ -58,7 +59,7 @@ export function useReadStatuses(projectId: Project['projectId'], statusId?: Proj isError: isStatusError, error: statusError, } = useQuery({ - queryKey: ['projects', projectId, 'statuses'], + queryKey: generateStatusesQueryKey(projectId), queryFn: async () => { const { data } = await getStatusList(projectId); return data; @@ -103,7 +104,7 @@ export function useCreateStatus(projectId: Project['projectId']) { onSuccess: () => { toastSuccess('프로젝트 상태를 추가하였습니다.'); queryClient.invalidateQueries({ - queryKey: ['projects', projectId], + queryKey: generateProjectQueryKey(projectId), }); }, }); @@ -120,7 +121,7 @@ export function useUpdateStatus(projectId: Project['projectId'], statusId: Proje onSuccess: () => { toastSuccess('프로젝트 상태를 수정했습니다.'); queryClient.invalidateQueries({ - queryKey: ['projects', projectId], + queryKey: generateProjectQueryKey(projectId), }); }, }); @@ -131,8 +132,8 @@ export function useUpdateStatus(projectId: Project['projectId'], statusId: Proje export function useUpdateStatusesOrder(projectId: Project['projectId']) { const { toastError } = useToast(); const queryClient = useQueryClient(); - const queryKeyTasks = ['projects', projectId, 'tasks']; - const queryKeyStatuses = ['projects', projectId, 'statuses']; + const TasksQueryKey = generateTasksQueryKey(projectId); + const statusesQueryKey = generateStatusesQueryKey(projectId); const mutation = useMutation({ mutationFn: (newStatusTaskList: TaskListWithStatus[]) => { @@ -140,19 +141,19 @@ export function useUpdateStatusesOrder(projectId: Project['projectId']) { return updateStatusesOrder(projectId, { statuses: statusOrders }); }, onMutate: async (newStatusTaskList: TaskListWithStatus[]) => { - await queryClient.cancelQueries({ queryKey: queryKeyTasks }); + await queryClient.cancelQueries({ queryKey: TasksQueryKey }); - const previousStatusTaskList = queryClient.getQueryData(queryKeyTasks); - queryClient.setQueryData(queryKeyTasks, newStatusTaskList); + const previousStatusTaskList = queryClient.getQueryData(TasksQueryKey); + queryClient.setQueryData(TasksQueryKey, newStatusTaskList); return { previousStatusTaskList }; }, onError: (error, newStatusTaskList, context) => { toastError('프로젝트 상태 순서 변경에 실패 하였습니다. 잠시후 다시 진행해주세요.'); - queryClient.setQueryData(queryKeyTasks, context?.previousStatusTaskList); + queryClient.setQueryData(TasksQueryKey, context?.previousStatusTaskList); }, onSuccess: () => { - queryClient.invalidateQueries({ queryKey: queryKeyStatuses, type: 'all' }); + queryClient.invalidateQueries({ queryKey: statusesQueryKey, type: 'all' }); }, }); diff --git a/src/hooks/query/useTaskQuery.ts b/src/hooks/query/useTaskQuery.ts index 719c3622..aa5f9f1c 100644 --- a/src/hooks/query/useTaskQuery.ts +++ b/src/hooks/query/useTaskQuery.ts @@ -1,5 +1,10 @@ import { useMemo } from 'react'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { + generateTaskAssigneesQueryKey, + generateTaskFilesQueryKey, + generateTasksQueryKey, +} from '@utils/queryKeyGenergator'; import { addAssignee, createTask, @@ -38,7 +43,7 @@ function getTaskNameList(taskList: TaskListWithStatus[], excludedTaskName?: Task export function useCreateStatusTask(projectId: Project['projectId']) { const { toastError, toastSuccess } = useToast(); const queryClient = useQueryClient(); - const queryKey = ['projects', projectId, 'tasks']; + const tasksQueryKey = generateTasksQueryKey(projectId); const mutation = useMutation({ mutationFn: (formData: TaskCreationForm) => createTask(projectId, formData), @@ -47,7 +52,7 @@ export function useCreateStatusTask(projectId: Project['projectId']) { }, onSuccess: () => { toastSuccess('프로젝트 일정을 등록하였습니다.'); - queryClient.invalidateQueries({ queryKey }); + queryClient.invalidateQueries({ queryKey: tasksQueryKey }); }, }); @@ -76,7 +81,7 @@ export function useReadStatusTasks(projectId: Project['projectId'], taskId?: Tas isError: isTaskError, error: taskError, } = useQuery({ - queryKey: ['projects', projectId, 'tasks'], + queryKey: generateTasksQueryKey(projectId), queryFn: async () => { const { data } = await findTaskList(projectId); return data; @@ -100,7 +105,7 @@ export function useReadStatusTasks(projectId: Project['projectId'], taskId?: Tas export function useUpdateTasksOrder(projectId: Project['projectId']) { const { toastError } = useToast(); const queryClient = useQueryClient(); - const queryKey = ['projects', projectId, 'tasks']; + const tasksQueryKey = generateTasksQueryKey(projectId); const mutation = useMutation({ mutationFn: (newStatusTaskList: TaskListWithStatus[]) => { @@ -113,16 +118,16 @@ export function useUpdateTasksOrder(projectId: Project['projectId']) { return updateTaskOrder(projectId, { tasks: taskOrders }); }, onMutate: async (newStatusTaskList: TaskListWithStatus[]) => { - await queryClient.cancelQueries({ queryKey }); + await queryClient.cancelQueries({ queryKey: tasksQueryKey }); - const previousStatusTaskList = queryClient.getQueryData(queryKey); - queryClient.setQueryData(queryKey, newStatusTaskList); + const previousStatusTaskList = queryClient.getQueryData(tasksQueryKey); + queryClient.setQueryData(tasksQueryKey, newStatusTaskList); return { previousStatusTaskList }; }, onError: (err, newStatusTaskList, context) => { toastError('일정 순서 변경에 실패 하였습니다. 잠시후 다시 진행해주세요.'); - queryClient.setQueryData(queryKey, context?.previousStatusTaskList); + queryClient.setQueryData(tasksQueryKey, context?.previousStatusTaskList); }, }); @@ -137,7 +142,7 @@ export function useReadAssignees(projectId: Project['projectId'], taskId: Task[' error: assigneeError, isError: isAssigneeError, } = useQuery({ - queryKey: ['projects', projectId, 'tasks', taskId, 'assignees'], + queryKey: generateTaskAssigneesQueryKey(projectId, taskId), queryFn: async () => { const { data } = await findAssignees(projectId, taskId); return data; @@ -155,7 +160,7 @@ export function useReadTaskFiles(projectId: Project['projectId'], taskId: Task[' error: taskFileError, isError: isTaskFileError, } = useQuery({ - queryKey: ['projects', projectId, 'tasks', taskId, 'files'], + queryKey: generateTaskFilesQueryKey(projectId, taskId), queryFn: async () => { const { data } = await findTaskFiles(projectId, taskId); return data; @@ -168,14 +173,14 @@ export function useReadTaskFiles(projectId: Project['projectId'], taskId: Task[' export function useUpdateTaskInfo(projectId: Project['projectId'], taskId: Task['taskId']) { const { toastError, toastSuccess } = useToast(); const queryClient = useQueryClient(); - const queryKey = ['projects', projectId, 'tasks']; + const tasksQueryKey = generateTasksQueryKey(projectId); const mutation = useMutation({ mutationFn: (formData: TaskUpdateForm) => updateTaskInfo(projectId, taskId, formData), onError: () => toastError('일정 정보 수정에 실패 했습니다. 잠시후 다시 시도해주세요.'), onSuccess: () => { toastSuccess('일정 정보를 수정 했습니다.'); - queryClient.invalidateQueries({ queryKey }); + queryClient.invalidateQueries({ queryKey: tasksQueryKey }); }, }); @@ -186,7 +191,7 @@ export function useUpdateTaskInfo(projectId: Project['projectId'], taskId: Task[ export function useAddAssignee(projectId: Project['projectId'], taskId: Task['taskId']) { const { toastError, toastSuccess } = useToast(); const queryClient = useQueryClient(); - const queryKey = ['projects', projectId, 'tasks', taskId, 'assignees']; + const taskAssigneesQueryKey = generateTaskAssigneesQueryKey(projectId, taskId); const mutation = useMutation({ mutationFn: (userId: User['userId']) => addAssignee(projectId, taskId, userId), @@ -195,7 +200,7 @@ export function useAddAssignee(projectId: Project['projectId'], taskId: Task['ta }, onSuccess: () => { toastSuccess('수행자를 추가 하였습니다.'); - queryClient.invalidateQueries({ queryKey }); + queryClient.invalidateQueries({ queryKey: taskAssigneesQueryKey }); }, }); @@ -206,7 +211,7 @@ export function useAddAssignee(projectId: Project['projectId'], taskId: Task['ta export function useDeleteAssignee(projectId: Project['projectId'], taskId: Task['taskId']) { const { toastError, toastSuccess } = useToast(); const queryClient = useQueryClient(); - const queryKey = ['projects', projectId, 'tasks', taskId, 'assignees']; + const taskAssigneesQueryKey = generateTaskAssigneesQueryKey(projectId, taskId); const mutation = useMutation({ mutationFn: (userId: User['userId']) => deleteAssignee(projectId, taskId, userId), @@ -215,7 +220,7 @@ export function useDeleteAssignee(projectId: Project['projectId'], taskId: Task[ }, onSuccess: () => { toastSuccess('수행자를 삭제 하였습니다.'); - queryClient.invalidateQueries({ queryKey }); + queryClient.invalidateQueries({ queryKey: taskAssigneesQueryKey }); }, }); diff --git a/src/hooks/query/useTeamQuery.ts b/src/hooks/query/useTeamQuery.ts index 910446c0..53963543 100644 --- a/src/hooks/query/useTeamQuery.ts +++ b/src/hooks/query/useTeamQuery.ts @@ -1,8 +1,9 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; +import { generateTeamsQueryKey } from '@utils/queryKeyGenergator'; import { getTeamList } from '@services/userService'; import { acceptTeamInvitation, declineTeamInvitation, deleteTeam, leaveTeam } from '@services/teamService'; +import useToast from '@hooks/useToast'; import type { TeamListWithApproval } from '@/types/TeamType'; -import useToast from '@/hooks/useToast'; export function useReadTeams() { const { @@ -11,7 +12,7 @@ export function useReadTeams() { isError, error, } = useQuery({ - queryKey: ['teams'], + queryKey: generateTeamsQueryKey(), queryFn: async () => { const { data } = await getTeamList(); return data; @@ -27,12 +28,13 @@ export function useReadTeams() { export function useLeaveTeam() { const queryClient = useQueryClient(); const { toastSuccess, toastError } = useToast(); + const teamsQueryKey = generateTeamsQueryKey(); return useMutation({ mutationFn: (teamId: number) => leaveTeam(teamId), onSuccess: () => { toastSuccess('팀에서 탈퇴했습니다.'); - queryClient.invalidateQueries({ queryKey: ['teams'] }); + queryClient.invalidateQueries({ queryKey: teamsQueryKey }); }, onError: () => { toastError('탈퇴에 실패했습니다. 다시 시도해 주세요.'); @@ -43,19 +45,16 @@ export function useLeaveTeam() { export function useDeleteTeam() { const queryClient = useQueryClient(); const { toastSuccess, toastError } = useToast(); + const teamsQueryKey = generateTeamsQueryKey(); return useMutation({ - mutationFn: async (teamId: number) => { - const response = await deleteTeam(teamId); - - return response; + mutationFn: (teamId: number) => deleteTeam(teamId), + onError: () => { + toastError('팀 삭제를 실패했습니다. 다시 시도해 주세요.'); }, onSuccess: () => { toastSuccess('팀을 삭제하였습니다.'); - queryClient.invalidateQueries({ queryKey: ['teams'] }); - }, - onError: () => { - toastError('팀 삭제를 실패했습니다. 다시 시도해 주세요.'); + queryClient.invalidateQueries({ queryKey: teamsQueryKey }); }, }); } @@ -63,31 +62,33 @@ export function useDeleteTeam() { export function useApproveTeamInvitation() { const queryClient = useQueryClient(); const { toastSuccess, toastError } = useToast(); + const teamsQueryKey = generateTeamsQueryKey(); return useMutation({ mutationFn: (teamId: number) => acceptTeamInvitation(teamId), - onSuccess: () => { - toastSuccess('초대를 수락했습니다.'); - queryClient.invalidateQueries({ queryKey: ['teams'] }); - }, onError: () => { toastError('초대 수락에 실패했습니다. 다시 시도해 주세요.'); }, + onSuccess: () => { + toastSuccess('초대를 수락했습니다.'); + queryClient.invalidateQueries({ queryKey: teamsQueryKey }); + }, }); } export function useRejectTeamInvitation() { const queryClient = useQueryClient(); const { toastSuccess, toastError } = useToast(); + const teamsQueryKey = generateTeamsQueryKey(); return useMutation({ mutationFn: (teamId: number) => declineTeamInvitation(teamId), - onSuccess: () => { - toastSuccess('초대를 거절했습니다.'); - queryClient.invalidateQueries({ queryKey: ['teams'] }); - }, onError: () => { toastError('초대 거절에 실패했습니다. 다시 시도해 주세요.'); }, + onSuccess: () => { + toastSuccess('초대를 거절했습니다.'); + queryClient.invalidateQueries({ queryKey: teamsQueryKey }); + }, }); } diff --git a/src/utils/queryKeyGenergator.ts b/src/utils/queryKeyGenergator.ts new file mode 100644 index 00000000..2770e3a3 --- /dev/null +++ b/src/utils/queryKeyGenergator.ts @@ -0,0 +1,102 @@ +import type { Task } from '@/types/TaskType'; +import type { Team } from '@/types/TeamType'; +import type { Project } from '@/types/ProjectType'; + +export const queryKeys = { + users: 'users', + teams: 'teams', + projects: 'projects', + statuses: 'statuses', + tasks: 'tasks', + files: 'files', + assignees: 'assignees', +}; + +/** + * 유저의 팀 목록 queryKey 생성 함수 + * + * @export + * @returns {(string | number)[]} + */ +export function generateTeamsQueryKey() { + return [queryKeys.teams]; +} + +/** + * 팀의 프로젝트 목록 queryKey 생성 함수 + * + * @export + * @param {Team['teamId']} teamId - 프로젝트 목록을 가져올 팀 ID + * @returns {(string | number)[]} + */ +export function generateProjectsQueryKey(teamId: Team['teamId']) { + return [queryKeys.teams, teamId, queryKeys.projects]; +} + +/** + * 프로젝트의 팀원 목록 queryKey 생성함수 + * + * @export + * @param {Project['projectId']} projectId - 팀원 목록을 가져올 프로젝트 ID + * @returns {(string | number)[]} + */ +export function generateProjectUsersQueryKey(projectId: Project['projectId']) { + return [queryKeys.projects, projectId, queryKeys.users]; +} + +/** + * 프로젝트의 상태 목록 queryKey 생성 함수 + * + * @export + * @param {Project['projectId']} projectId - 상태 목록을 가져올 프로젝트 ID + * @returns {(string | number)[]} + */ +export function generateStatusesQueryKey(projectId: Project['projectId']) { + return [queryKeys.projects, projectId, queryKeys.statuses]; +} + +/** + * 단일 프로젝트 queryKey 생성 + * + * @export + * @param {Project['projectId']} projectId - 프로젝트 ID + * @returns {(string | number)[]} + */ +export function generateProjectQueryKey(projectId: Project['projectId']) { + return [queryKeys.projects, projectId]; +} + +/** + * 프로젝트의 일정 목록 queryKey 생성 함수 + * + * @export + * @param {Project['projectId']} projectId - 일정 목록을 가져올 프로젝트 ID + * @returns {(string | number)[]} + */ +export function generateTasksQueryKey(projectId: Project['projectId']) { + return [queryKeys.projects, projectId, queryKeys.tasks]; +} + +/** + * 일정의 파일 목록 queryKey 생성 함수 + * + * @export + * @param {Project['projectId']} projectId - 일정이 포함된 프로젝트 ID + * @param {Task['taskId']} taskId - 파일 목록을 가져올 일정 ID + * @returns {(string | number)[]} + */ +export function generateTaskFilesQueryKey(projectId: Project['projectId'], taskId: Task['taskId']) { + return [queryKeys.projects, projectId, queryKeys.teams, taskId, queryKeys.files]; +} + +/** + * 일정의 수행자 목록 queryKey 생성 함수 + * + * @export + * @param {Project['projectId']} projectId - 일정이 포함된 프로젝트 ID + * @param {Task['taskId']} taskId - 수행자 목록을 가져올 일정 ID + * @returns {(string | number)[]} + */ +export function generateTaskAssigneesQueryKey(projectId: Project['projectId'], taskId: Task['taskId']) { + return [queryKeys.projects, projectId, queryKeys.tasks, taskId, queryKeys.assignees]; +} From 96b4e9ecf8dabb408eb88ea5b3ff4d18e0a4f2f9 Mon Sep 17 00:00:00 2001 From: Seok93 Date: Sun, 29 Sep 2024 12:22:02 +0900 Subject: [PATCH 2/2] =?UTF-8?q?Fix:=20#173=20TaskFile=20Query=20Key=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/queryKeyGenergator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/queryKeyGenergator.ts b/src/utils/queryKeyGenergator.ts index 2770e3a3..69ba5c70 100644 --- a/src/utils/queryKeyGenergator.ts +++ b/src/utils/queryKeyGenergator.ts @@ -86,7 +86,7 @@ export function generateTasksQueryKey(projectId: Project['projectId']) { * @returns {(string | number)[]} */ export function generateTaskFilesQueryKey(projectId: Project['projectId'], taskId: Task['taskId']) { - return [queryKeys.projects, projectId, queryKeys.teams, taskId, queryKeys.files]; + return [queryKeys.projects, projectId, queryKeys.tasks, taskId, queryKeys.files]; } /**