diff --git a/frontend/src/hooks/useAuthHooks.ts b/frontend/src/hooks/useAuthHooks.ts new file mode 100644 index 0000000..ff52994 --- /dev/null +++ b/frontend/src/hooks/useAuthHooks.ts @@ -0,0 +1,104 @@ +import { useQuery, UseQueryResult, useMutation, useQueryClient, UseMutationResult } from '@tanstack/react-query'; +import { AxiosError } from 'axios'; +import useAuthStore from '@/stores/useAuthStore'; +import { reissueTokenApi, fetchProfileApi } from '@/api/authApi'; +import { useState, useEffect } from 'react'; + +interface TokenResponse { + status: number; + code: number; + message: string; + data: { + accessToken: string; + }; + errors: Array<{ + field: string; + code: string; + message: string; + objectName: string; + }>; + isSuccess: boolean; +} + +interface ProfileResponse { + status: number; + code: number; + message: string; + data: { + id: number; + nickname: string; + profileImage: string; + }; + errors: Array<{ + field: string; + code: string; + message: string; + objectName: string; + }>; + isSuccess: boolean; +} + +interface ErrorResponse { + message: string; +} + +export const useReissueToken = (): UseMutationResult> => { + const queryClient = useQueryClient(); + const { setLoggedIn } = useAuthStore(); + + return useMutation({ + mutationFn: reissueTokenApi, + onSuccess: (data) => { + setLoggedIn(true, data.data.accessToken); + queryClient.invalidateQueries({ queryKey: ['profile'] }); + }, + onError: (error) => { + console.error('토큰 재발급 실패:', error?.response?.data?.message || '알 수 없는 오류'); + }, + }); +}; + +export const useProfile = (): UseQueryResult> => { + const { accessToken, setProfile } = useAuthStore(); + + return useQuery>({ + queryKey: ['profile'], + queryFn: fetchProfileApi, + enabled: !!accessToken, + select: (data) => { + if (data.isSuccess) { + setProfile({ + id: data.data.id, + nickname: data.data.nickname, + profileImage: data.data.profileImage, + }); + } + return data; + }, + }); +}; + +export const useFetchProfile = () => { + const { profile, setProfile } = useAuthStore(); + const [isFetched, setIsFetched] = useState(false); + + useEffect(() => { + if (!profile.id && !isFetched) { + fetchProfileApi() + .then((data) => { + if (data?.isSuccess && data.data) { + setProfile({ + id: data.data.id, + nickname: data.data.nickname, + profileImage: data.data.profileImage, + }); + setIsFetched(true); + } + }) + .catch((error) => { + alert('프로필을 가져오는 중 오류가 발생했습니다. 다시 시도해주세요.'); + console.error('프로필 가져오기 실패:', error); + }); + } + }, [profile.id, setProfile, isFetched]); +}; diff --git a/frontend/src/hooks/useProjectHooks.ts b/frontend/src/hooks/useProjectHooks.ts new file mode 100644 index 0000000..a2f8ff8 --- /dev/null +++ b/frontend/src/hooks/useProjectHooks.ts @@ -0,0 +1,150 @@ +import { useQuery, UseQueryResult, useMutation, UseMutationResult, useQueryClient } from '@tanstack/react-query'; +import { AxiosError } from 'axios'; +import { + getProjectApi, + updateProjectApi, + deleteProjectApi, + getAllProjectsApi, + createProjectApi, + addProjectMemberApi, + removeProjectMemberApi, +} from '@/api/projectApi'; + +interface Project { + id: number; + title: string; + workspaceId: number; + projectType: string; + createdAt: string; + updatedAt: string; +} + +interface ProjectsResponse { + status: number; + code: number; + message: string; + data: { + workspaceResponses: Project[]; + }; + errors: Array<{ + field: string; + code: string; + message: string; + objectName: string; + }>; + isSuccess: boolean; +} + +interface ErrorResponse { + message: string; +} + +export const useGetProject = ( + projectId: number, + memberId: number +): UseQueryResult> => { + return useQuery>({ + queryKey: ['project', projectId], + queryFn: () => getProjectApi(projectId, memberId), + }); +}; + +export const useUpdateProject = (): UseMutationResult< + ProjectsResponse, + AxiosError, + { projectId: number; memberId: number; data: { title: string; projectType: string } } +> => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: ({ projectId, memberId, data }) => updateProjectApi(projectId, memberId, data), + onSuccess: (data) => { + const project = data.data?.workspaceResponses?.[0]; + if (project) { + queryClient.invalidateQueries({ queryKey: ['project', project.id] }); + } else { + console.error('프로젝트 데이터가 없습니다.'); + } + }, + }); +}; + +export const useDeleteProject = (): UseMutationResult< + ProjectsResponse, + AxiosError, + { projectId: number; memberId: number } +> => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: ({ projectId, memberId }) => deleteProjectApi(projectId, memberId), + onSuccess: (data) => { + const project = data.data?.workspaceResponses?.[0]; + if (project) { + queryClient.invalidateQueries({ queryKey: ['project', project.id] }); + } else { + console.error('프로젝트 데이터가 없습니다.'); + } + }, + }); +}; + +export const useGetAllProjects = ( + workspaceId: number, + memberId: number, + lastProjectId?: number, + limit: number = 10 +): UseQueryResult> => { + return useQuery>({ + queryKey: ['projects', workspaceId], + queryFn: () => getAllProjectsApi(workspaceId, memberId, lastProjectId, limit), + }); +}; + +export const useCreateProject = (): UseMutationResult< + ProjectsResponse, + AxiosError, + { workspaceId: number; memberId: number; data: { title: string; projectType: string } } +> => { + return useMutation({ + mutationFn: ({ workspaceId, memberId, data }) => createProjectApi(workspaceId, memberId, data), + }); +}; + +export const useAddProjectMember = (): UseMutationResult< + ProjectsResponse, + AxiosError, + { projectId: number; memberId: number; newMemberId: number; privilegeType: string } +> => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: ({ projectId, memberId, newMemberId, privilegeType }) => + addProjectMemberApi(projectId, memberId, newMemberId, privilegeType), + onSuccess: (data) => { + const project = data.data?.workspaceResponses?.[0]; + if (project) { + queryClient.invalidateQueries({ queryKey: ['project', project.id] }); + } else { + console.error('프로젝트 데이터가 없습니다.'); + } + }, + }); +}; + +export const useRemoveProjectMember = (): UseMutationResult< + ProjectsResponse, + AxiosError, + { projectId: number; memberId: number; targetMemberId: number } +> => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: ({ projectId, memberId, targetMemberId }) => + removeProjectMemberApi(projectId, memberId, targetMemberId), + onSuccess: (data) => { + const project = data.data?.workspaceResponses?.[0]; + if (project) { + queryClient.invalidateQueries({ queryKey: ['project', project.id] }); + } else { + console.error('프로젝트 데이터가 없습니다.'); + } + }, + }); +}; diff --git a/frontend/src/hooks/useWorkspaceHooks.ts b/frontend/src/hooks/useWorkspaceHooks.ts new file mode 100644 index 0000000..e2f37da --- /dev/null +++ b/frontend/src/hooks/useWorkspaceHooks.ts @@ -0,0 +1,152 @@ +import { useQuery, UseQueryResult, useMutation, UseMutationResult, useQueryClient } from '@tanstack/react-query'; +import { AxiosError } from 'axios'; +import { + getWorkspaceApi, + updateWorkspaceApi, + deleteWorkspaceApi, + getAllWorkspacesApi, + createWorkspaceApi, + addWorkspaceMemberApi, + removeWorkspaceMemberApi, +} from '@/api/workspaceApi'; + +interface WorkspaceResponse { + status: number; + code: number; + message: string; + data: { + id: number; + memberId: string; + title: string; + content: string; + createdAt: string; + updatedAt: string; + }; + errors: Array<{ + field: string; + code: string; + message: string; + objectName: string; + }>; + isSuccess: boolean; +} +interface Workspace { + id: number; + memberId: string; + title: string; + content: string; + createdAt: string; + updatedAt: string; +} + +interface GetAllWorkspacesResponse { + status: number; + code: number; + message: string; + data: { + workspaceResponses: Workspace[]; + }; + errors: Array<{ + field: string; + code: string; + message: string; + objectName: string; + }>; + isSuccess: boolean; +} + +interface ErrorResponse { + message: string; +} + +export const useGetWorkspace = ( + workspaceId: number, + memberId: number +): UseQueryResult> => { + return useQuery>({ + queryKey: ['workspace', workspaceId], + queryFn: () => getWorkspaceApi(workspaceId, memberId), + }); +}; + +export const useUpdateWorkspace = (): UseMutationResult< + WorkspaceResponse, + AxiosError, + { workspaceId: number; memberId: number; data: { title: string; content: string } } +> => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: ({ workspaceId, memberId, data }) => updateWorkspaceApi(workspaceId, memberId, data), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['workspace'] }); + }, + }); +}; + +export const useDeleteWorkspace = (): UseMutationResult< + WorkspaceResponse, + AxiosError, + { workspaceId: number; memberId: number } +> => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: ({ workspaceId, memberId }) => deleteWorkspaceApi(workspaceId, memberId), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['workspace'] }); + }, + }); +}; + +export const useGetAllWorkspaces = ( + memberId: number, + lastWorkspaceId?: number, + limit?: number +): UseQueryResult> => { + return useQuery>({ + queryKey: ['workspaces'], + queryFn: () => getAllWorkspacesApi(memberId, lastWorkspaceId, limit), + }); +}; + +export const useCreateWorkspace = (): UseMutationResult< + WorkspaceResponse, + AxiosError, + { memberId: number; data: { title: string; content: string } } +> => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: ({ memberId, data }) => createWorkspaceApi(memberId, data), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['workspace'] }); + }, + }); +}; + +export const useAddWorkspaceMember = (): UseMutationResult< + WorkspaceResponse, + AxiosError, + { workspaceId: number; memberId: number; newMemberId: number } +> => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: ({ workspaceId, memberId, newMemberId }) => addWorkspaceMemberApi(workspaceId, memberId, newMemberId), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['workspace'] }); + }, + }); +}; + +export const useRemoveWorkspaceMember = (): UseMutationResult< + WorkspaceResponse, + AxiosError, + { workspaceId: number; memberId: number; targetMemberId: number } +> => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: ({ workspaceId, memberId, targetMemberId }) => + removeWorkspaceMemberApi(workspaceId, memberId, targetMemberId), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['workspace'] }); + }, + }); +};