Feat: get method API 쿼리 함수 추가

This commit is contained in:
jhynsoo 2024-09-15 00:39:34 +09:00
parent 07eadc03f6
commit 503976b3ee
15 changed files with 194 additions and 135 deletions

View File

@ -1,11 +1,14 @@
import api from '@/api/axiosConfig'; import api from '@/api/axiosConfig';
import { MemberResponse, RefreshTokenResponse } from '@/types';
export async function reissueToken() { export async function reissueToken() {
return api.post('/auth/reissue', null, { withCredentials: true }); return api.post<RefreshTokenResponse>('/auth/reissue', null, { withCredentials: true }).then(({ data }) => data);
} }
export async function fetchProfile() { export async function getProfile() {
return api.get('/auth/profile', { return api
.get<MemberResponse>('/auth/profile', {
withCredentials: true, withCredentials: true,
}); })
.then(({ data }) => data);
} }

View File

@ -1,37 +1,42 @@
import api from '@/api/axiosConfig'; import api from '@/api/axiosConfig';
import { FolderRequestDTO } from '@/types'; import { FolderRequest, FolderResponse } from '@/types';
export async function fetchFolder(projectId: number, folderId: number, memberId: number) { export async function fetchFolder(projectId: number, folderId: number, memberId: number) {
return api.get(`/projects/${projectId}/folders/${folderId}`, { return api
.get<FolderResponse>(`/projects/${projectId}/folders/${folderId}`, {
params: { memberId }, params: { memberId },
}); })
.then(({ data }) => data);
} }
export async function updateFolder( export async function updateFolder(projectId: number, folderId: number, memberId: number, folderData: FolderRequest) {
projectId: number, return api
folderId: number, .put(`/projects/${projectId}/folders/${folderId}`, folderData, {
memberId: number,
folderData: FolderRequestDTO
) {
return api.put(`/projects/${projectId}/folders/${folderId}`, folderData, {
params: { memberId }, params: { memberId },
}); })
.then(({ data }) => data);
} }
export async function deleteFolder(projectId: number, folderId: number, memberId: number) { export async function deleteFolder(projectId: number, folderId: number, memberId: number) {
return api.delete(`/projects/${projectId}/folders/${folderId}`, { return api
.delete(`/projects/${projectId}/folders/${folderId}`, {
params: { memberId }, params: { memberId },
}); })
.then(({ data }) => data);
} }
export async function createFolder(projectId: number, memberId: number, folderData: FolderRequestDTO) { export async function createFolder(projectId: number, memberId: number, folderData: FolderRequest) {
return api.post(`/projects/${projectId}/folders`, folderData, { return api
.post(`/projects/${projectId}/folders`, folderData, {
params: { memberId }, params: { memberId },
}); })
.then(({ data }) => data);
} }
export async function fetchFolderReviewList(projectId: number, folderId: number, memberId: number) { export async function getFolderReviewList(projectId: number, folderId: number, memberId: number) {
return api.get(`/projects/${projectId}/folders/${folderId}/review`, { return api
.get(`/projects/${projectId}/folders/${folderId}/review`, {
params: { memberId }, params: { memberId },
}); })
.then(({ data }) => data);
} }

View File

@ -1,7 +1,7 @@
import api from '@/api/axiosConfig'; import api from '@/api/axiosConfig';
import { ImageMoveRequestDTO, ImageStatusChangeRequestDTO } from '@/types'; import { ImageMoveRequest, ImageStatusChangeRequest } from '@/types';
export async function fetchImage(projectId: number, folderId: number, imageId: number, memberId: number) { export async function getImage(projectId: number, folderId: number, imageId: number, memberId: number) {
return api.get(`/projects/${projectId}/folders/${folderId}/images/${imageId}`, { return api.get(`/projects/${projectId}/folders/${folderId}/images/${imageId}`, {
params: { memberId }, params: { memberId },
}); });
@ -12,7 +12,7 @@ export async function moveImage(
folderId: number, folderId: number,
imageId: number, imageId: number,
memberId: number, memberId: number,
moveRequest: ImageMoveRequestDTO moveRequest: ImageMoveRequest
) { ) {
return api.put(`/projects/${projectId}/folders/${folderId}/images/${imageId}`, moveRequest, { return api.put(`/projects/${projectId}/folders/${folderId}/images/${imageId}`, moveRequest, {
params: { memberId }, params: { memberId },
@ -30,7 +30,7 @@ export async function changeImageStatus(
folderId: number, folderId: number,
imageId: number, imageId: number,
memberId: number, memberId: number,
statusChangeRequest: ImageStatusChangeRequestDTO statusChangeRequest: ImageStatusChangeRequest
) { ) {
return api.put(`/projects/${projectId}/folders/${folderId}/images/${imageId}/status`, statusChangeRequest, { return api.put(`/projects/${projectId}/folders/${folderId}/images/${imageId}/status`, statusChangeRequest, {
params: { memberId }, params: { memberId },

View File

@ -1,7 +1,7 @@
import api from '@/api/axiosConfig'; import api from '@/api/axiosConfig';
import { LabelingRequestDTO } from '@/types'; import { LabelingRequest } from '@/types';
export async function saveImageLabels(projectId: number, imageId: number, memberId: number, data: LabelingRequestDTO) { export async function saveImageLabels(projectId: number, imageId: number, memberId: number, data: LabelingRequest) {
return api.post(`/projects/${projectId}/label/image/${imageId}`, data, { return api.post(`/projects/${projectId}/label/image/${imageId}`, data, {
params: { memberId }, params: { memberId },
}); });

View File

@ -1,70 +1,84 @@
import api from '@/api/axiosConfig'; import api from '@/api/axiosConfig';
export async function getProjectApi(projectId: number, memberId: number) { export async function getProject(projectId: number, memberId: number) {
return api.get(`/projects/${projectId}`, { return api
.get(`/projects/${projectId}`, {
params: { memberId }, params: { memberId },
}); })
.then(({ data }) => data);
} }
export async function updateProjectApi( export async function updateProject(
projectId: number, projectId: number,
memberId: number, memberId: number,
data: { title: string; projectType: 'classification' | 'detection' | 'segmentation' } data: { title: string; projectType: 'classification' | 'detection' | 'segmentation' }
) { ) {
return api.put(`/projects/${projectId}`, data, { return api
.put(`/projects/${projectId}`, data, {
params: { memberId }, params: { memberId },
}); })
.then(({ data }) => data);
} }
export async function deleteProjectApi(projectId: number, memberId: number) { export async function deleteProject(projectId: number, memberId: number) {
return api.delete(`/projects/${projectId}`, { return api
.delete(`/projects/${projectId}`, {
params: { memberId }, params: { memberId },
}); })
.then(({ data }) => data);
} }
export async function addProjectMemberApi( export async function addProjectMember(
projectId: number, projectId: number,
memberId: number, memberId: number,
newMemberId: number, newMemberId: number,
privilegeType: string privilegeType: string
) { ) {
return api.post( return api
.post(
`/projects/${projectId}/members`, `/projects/${projectId}/members`,
{ memberId: newMemberId, privilegeType }, { memberId: newMemberId, privilegeType },
{ {
params: { memberId }, params: { memberId },
} }
); )
.then(({ data }) => data);
} }
export async function removeProjectMemberApi(projectId: number, memberId: number, targetMemberId: number) { export async function removeProjectMember(projectId: number, memberId: number, targetMemberId: number) {
return api.delete(`/projects/${projectId}/members`, { return api
.delete(`/projects/${projectId}/members`, {
params: { memberId }, params: { memberId },
data: { memberId: targetMemberId }, data: { memberId: targetMemberId },
}); })
.then(({ data }) => data);
} }
export async function getAllProjectsApi( export async function getAllProjects(
workspaceId: number, workspaceId: number,
memberId: number, memberId: number,
lastProjectId?: number, lastProjectId?: number,
limit: number = 10 limit: number = 10
) { ) {
return api.get(`/workspaces/${workspaceId}/projects`, { return api
.get(`/workspaces/${workspaceId}/projects`, {
params: { params: {
memberId, memberId,
lastProjectId, lastProjectId,
limit, limit,
}, },
}); })
.then(({ data }) => data);
} }
export async function createProjectApi( export async function createProject(
workspaceId: number, workspaceId: number,
memberId: number, memberId: number,
data: { title: string; projectType: 'classification' | 'detection' | 'segmentation' } data: { title: string; projectType: 'classification' | 'detection' | 'segmentation' }
) { ) {
return api.post(`/workspaces/${workspaceId}/projects`, data, { return api
.post(`/workspaces/${workspaceId}/projects`, data, {
params: { memberId }, params: { memberId },
}); })
.then(({ data }) => data);
} }

View File

@ -14,6 +14,7 @@ export async function uploadFilesToProject(
formData.append('files', file); formData.append('files', file);
}); });
// TODO: api.post()를 return하고 사이드 이펙트는 외부에서 처리하도록 수정
return api return api
.post('/projects/{project_id}', formData, { .post('/projects/{project_id}', formData, {
onUploadProgress: (progressEvent: AxiosProgressEvent) => { onUploadProgress: (progressEvent: AxiosProgressEvent) => {

View File

@ -1,5 +1,5 @@
import api from '@/api/axiosConfig'; import api from '@/api/axiosConfig';
import { WorkspaceRequestDTO } from '@/types'; import { WorkspaceRequest } from '@/types';
export async function fetchWorkspaceList(memberId: number, lastWorkspaceId?: number, limit?: number) { export async function fetchWorkspaceList(memberId: number, lastWorkspaceId?: number, limit?: number) {
return api.get('/workspaces', { return api.get('/workspaces', {
@ -13,7 +13,7 @@ export async function fetchWorkspace(workspaceId: number, memberId: number) {
}); });
} }
export async function updateWorkspace(workspaceId: number, memberId: number, data: WorkspaceRequestDTO) { export async function updateWorkspace(workspaceId: number, memberId: number, data: WorkspaceRequest) {
return api.put(`/workspaces/${workspaceId}`, data, { return api.put(`/workspaces/${workspaceId}`, data, {
params: { memberId }, params: { memberId },
}); });
@ -25,7 +25,7 @@ export async function deleteWorkspace(workspaceId: number, memberId: number) {
}); });
} }
export async function createWorkspace(memberId: number, data: WorkspaceRequestDTO) { export async function createWorkspace(memberId: number, data: WorkspaceRequest) {
return api.post('/workspaces', data, { return api.post('/workspaces', data, {
params: { memberId }, params: { memberId },
}); });

View File

@ -1,47 +1,31 @@
import { useQuery, UseQueryResult, useMutation, useQueryClient, UseMutationResult } from '@tanstack/react-query'; import { useMutation, useQueryClient } from '@tanstack/react-query';
import { AxiosError } from 'axios'; import { AxiosError } from 'axios';
import useAuthStore from '@/stores/useAuthStore'; import useAuthStore from '@/stores/useAuthStore';
import { reissueTokenApi, fetchProfileApi } from '@/api/authApi'; import { reissueToken, getProfile } from '@/api/authApi';
import { SuccessResponse, RefreshTokenResponseDTO, MemberResponseDTO, CustomError } from '@/types'; import { CustomError } from '@/types';
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import useProfileQuery from '@/queries/useProfileQuery';
export const useReissueToken = (): UseMutationResult< export const useReissueToken = () => {
SuccessResponse<RefreshTokenResponseDTO>,
AxiosError<CustomError>
> => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const { setLoggedIn } = useAuthStore(); const { setLoggedIn } = useAuthStore();
return useMutation({ return useMutation({
mutationFn: reissueTokenApi, mutationFn: reissueToken,
onSuccess: (data) => { onSuccess: (data) => {
if (data.isSuccess && data.data) { setLoggedIn(true, data.accessToken);
setLoggedIn(true, data.data.accessToken);
queryClient.invalidateQueries({ queryKey: ['profile'] }); queryClient.invalidateQueries({ queryKey: ['profile'] });
} else {
console.error('토큰 재발급 응답 오류:', data.message);
}
},
onError: (error) => {
console.error('토큰 재발급 실패:', error?.response?.data?.message || '알 수 없는 오류');
}, },
}); });
}; };
export const useProfile = (): UseQueryResult<SuccessResponse<MemberResponseDTO>, AxiosError<CustomError>> => { export const useProfile = () => {
const { accessToken, setProfile } = useAuthStore(); const { setProfile } = useAuthStore();
const query = useProfileQuery();
const query = useQuery<SuccessResponse<MemberResponseDTO>, AxiosError<CustomError>>({
queryKey: ['profile'],
queryFn: fetchProfileApi,
enabled: !!accessToken,
select: (data) => data,
});
// TODO: query.data가 변경될 때마다 setProfile을 호출하여 profile 업데이트, useEffect 제거
useEffect(() => { useEffect(() => {
if (query.data?.isSuccess) { setProfile(query.data);
setProfile(query.data.data);
}
}, [query.data, setProfile]); }, [query.data, setProfile]);
return query; return query;
@ -53,14 +37,10 @@ export const useFetchProfile = () => {
useEffect(() => { useEffect(() => {
if (!profile && !isFetched) { if (!profile && !isFetched) {
fetchProfileApi() getProfile()
.then((data) => { .then((data) => {
if (data.isSuccess && data.data) { setProfile(data);
setProfile(data.data);
setIsFetched(true); setIsFetched(true);
} else {
console.error('프로필 응답 오류:', data.message);
}
}) })
.catch((error: AxiosError<CustomError>) => { .catch((error: AxiosError<CustomError>) => {
alert('프로필을 가져오는 중 오류가 발생했습니다. 다시 시도해주세요.'); alert('프로필을 가져오는 중 오류가 발생했습니다. 다시 시도해주세요.');

View File

@ -0,0 +1,9 @@
import { fetchFolder } from '@/api/folderApi';
import { useSuspenseQuery } from '@tanstack/react-query';
export default function useFolderQuery(projectId: number, folderId: number, memberId: number) {
return useSuspenseQuery({
queryKey: ['folder', projectId, folderId, memberId],
queryFn: () => fetchFolder(projectId, folderId, memberId),
});
}

View File

@ -0,0 +1,9 @@
import { getFolderReviewList } from '@/api/folderApi';
import { useSuspenseQuery } from '@tanstack/react-query';
export function useFolderReviewListQuery(projectId: number, folderId: number, memberId: number) {
return useSuspenseQuery({
queryKey: ['folderReviewList', projectId, folderId, memberId],
queryFn: () => getFolderReviewList(projectId, folderId, memberId),
});
}

View File

@ -0,0 +1,9 @@
import { getProfile } from '@/api/authApi';
import { useSuspenseQuery } from '@tanstack/react-query';
export default function useProfileQuery() {
return useSuspenseQuery({
queryKey: ['profile'],
queryFn: getProfile,
});
}

View File

@ -0,0 +1,9 @@
import { getProject } from '@/api/projectApi';
import { useSuspenseQuery } from '@tanstack/react-query';
export default function useProjectQuery(projectId: number, memberId: number) {
return useSuspenseQuery({
queryKey: ['project', projectId, memberId],
queryFn: () => getProject(projectId, memberId),
});
}

View File

@ -0,0 +1,9 @@
import { fetchWorkspaceList } from '@/api/workspaceApi';
import { useSuspenseQuery } from '@tanstack/react-query';
export default function useWorkspaceListQuery(memberId: number, lastWorkspaceId?: number, limit?: number) {
return useSuspenseQuery({
queryKey: ['workspaceList'],
queryFn: () => fetchWorkspaceList(memberId, lastWorkspaceId, limit),
});
}

View File

@ -0,0 +1,9 @@
import { fetchWorkspace } from '@/api/workspaceApi';
import { useSuspenseQuery } from '@tanstack/react-query';
export default function useWorkspaceQuery(workspaceId: number, memberId: number) {
return useSuspenseQuery({
queryKey: ['workspace', workspaceId, memberId],
queryFn: () => fetchWorkspace(workspaceId, memberId),
});
}

View File

@ -34,50 +34,50 @@ export type Label = {
coordinates: Array<[number, number]>; coordinates: Array<[number, number]>;
}; };
export interface FolderRequestDTO { export interface FolderRequest {
title: string; title: string;
parentId: number; parentId: number;
} }
export interface ChildFolderDTO { export interface ChildFolder {
id: number; id: number;
title: string; title: string;
} }
export interface FolderResponseDTO { export interface FolderResponse {
id: number; id: number;
title: string; title: string;
images: ImageResponseDTO[]; images: ImageResponse[];
children: ChildFolderDTO[]; children: ChildFolder[];
} }
export interface ImageResponseDTO { export interface ImageResponse {
id: number; id: number;
imageTitle: string; imageTitle: string;
imageUrl: string; imageUrl: string;
status: 'PENDING' | 'IN_PROGRESS' | 'NEED_REVIEW' | 'COMPLETED'; status: 'PENDING' | 'IN_PROGRESS' | 'NEED_REVIEW' | 'COMPLETED';
} }
export interface ImageMoveRequestDTO { export interface ImageMoveRequest {
moveFolderId: number; moveFolderId: number;
} }
export interface ImageStatusChangeRequestDTO { export interface ImageStatusChangeRequest {
labelStatus: 'PENDING' | 'IN_PROGRESS' | 'NEED_REVIEW' | 'COMPLETED'; labelStatus: 'PENDING' | 'IN_PROGRESS' | 'NEED_REVIEW' | 'COMPLETED';
} }
export interface MemberResponseDTO { export interface MemberResponse {
id: number; id: number;
nickname: string; nickname: string;
profileImage: string; profileImage: string;
} }
export interface WorkspaceRequestDTO { export interface WorkspaceRequest {
title: string; title: string;
content: string; content: string;
} }
export interface WorkspaceResponseDTO { export interface WorkspaceResponse {
id: number; id: number;
memberId: string; memberId: string;
title: string; title: string;
@ -86,8 +86,8 @@ export interface WorkspaceResponseDTO {
updatedAt: string; updatedAt: string;
} }
export interface WorkspaceListResponseDTO { export interface WorkspaceListResponse {
workspaceResponses: WorkspaceResponseDTO[]; workspaceResponses: WorkspaceResponse[];
} }
export interface SuccessResponse<T> { export interface SuccessResponse<T> {
@ -98,11 +98,13 @@ export interface SuccessResponse<T> {
errors: CustomError[]; errors: CustomError[];
isSuccess: boolean; isSuccess: boolean;
} }
export interface ProjectRequestDTO {
export interface ProjectRequest {
title: string; title: string;
projectType: 'classification' | 'detection' | 'segmentation'; projectType: 'classification' | 'detection' | 'segmentation';
} }
export interface ProjectResponseDTO {
export interface ProjectResponse {
id: number; id: number;
title: string; title: string;
workspaceId: number; workspaceId: number;
@ -111,8 +113,8 @@ export interface ProjectResponseDTO {
updatedAt: string; updatedAt: string;
} }
export interface ProjectListResponseDTO { export interface ProjectListResponse {
workspaceResponses: ProjectResponseDTO[]; workspaceResponses: ProjectResponse[];
} }
export interface CustomError { export interface CustomError {
@ -140,17 +142,17 @@ export interface BaseResponse<T> {
isSuccess: boolean; isSuccess: boolean;
} }
export interface RefreshTokenResponseDTO { export interface RefreshTokenResponse {
accessToken: string; accessToken: string;
} }
export interface CommentRequestDTO { export interface CommentRequest {
content: string; content: string;
positionX: number; positionX: number;
positionY: number; positionY: number;
} }
export interface CommentResponseDTO { export interface CommentResponse {
id: number; id: number;
memberId: number; memberId: number;
memberNickname: string; memberNickname: string;
@ -161,18 +163,18 @@ export interface CommentResponseDTO {
createTime: string; // 작성 일자 (ISO 8601 형식) createTime: string; // 작성 일자 (ISO 8601 형식)
} }
export interface ProjectMemberRequestDTO { export interface ProjectMemberRequest {
memberId: number; memberId: number;
privilegeType: 'ADMIN' | 'MANAGER' | 'EDITOR' | 'VIEWER'; privilegeType: 'ADMIN' | 'MANAGER' | 'EDITOR' | 'VIEWER';
} }
export interface LabelingRequestDTO { export interface LabelingRequest {
memberId: number; memberId: number;
projectId: number; projectId: number;
imageId: number; imageId: number;
} }
export interface AutoLabelingResponseDTO { export interface AutoLabelingResponse {
imageId: number; imageId: number;
imageUrl: string; imageUrl: string;
data: string; data: string;