From b9650783cb5c16636249dcf9dcec80b8c061e9a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=ED=98=84=EC=A1=B0?= Date: Mon, 23 Sep 2024 15:52:48 +0900 Subject: [PATCH 1/6] =?UTF-8?q?Feat:=20=EB=AA=A8=EB=8D=B8=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/types/index.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts index 2a556b9..4be47b3 100644 --- a/frontend/src/types/index.ts +++ b/frontend/src/types/index.ts @@ -280,3 +280,23 @@ export interface ImageFolderRequest { parentId: number; files: File[]; } + +// 모델 요청 DTO (API로 전달할 데이터 타입) +export interface ModelRequest { + name: string; +} + +// 모델 응답 DTO (API로부터 받는 데이터 타입) +export interface ModelResponse { + id: number; + name: string; +} + +// 모델 카테고리 응답 DTO +export interface ModelCategoryResponse { + id: number; + name: string; +} + +// 프로젝트 모델 리스트 응답 DTO +export interface ProjectModelsResponse extends Array {} From e7cf03593c562c3c0597d27b6f0674f6c2c98501 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=ED=98=84=EC=A1=B0?= Date: Mon, 23 Sep 2024 15:56:13 +0900 Subject: [PATCH 2/6] =?UTF-8?q?Feat:=20models=20=EC=BF=BC=EB=A6=AC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/queries/models/useCreateModelQuery.ts | 14 ++++++++++++++ .../src/queries/models/useModelCategoriesQuery.ts | 9 +++++++++ .../src/queries/models/useProjectModelsQuery.ts | 9 +++++++++ frontend/src/queries/models/useTrainModelQuery.ts | 8 ++++++++ .../src/queries/models/useUpdateModelNameQuery.ts | 14 ++++++++++++++ 5 files changed, 54 insertions(+) create mode 100644 frontend/src/queries/models/useCreateModelQuery.ts create mode 100644 frontend/src/queries/models/useModelCategoriesQuery.ts create mode 100644 frontend/src/queries/models/useProjectModelsQuery.ts create mode 100644 frontend/src/queries/models/useTrainModelQuery.ts create mode 100644 frontend/src/queries/models/useUpdateModelNameQuery.ts diff --git a/frontend/src/queries/models/useCreateModelQuery.ts b/frontend/src/queries/models/useCreateModelQuery.ts new file mode 100644 index 0000000..595d304 --- /dev/null +++ b/frontend/src/queries/models/useCreateModelQuery.ts @@ -0,0 +1,14 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { addProjectModel } from '@/api/modelApi'; +import { ModelRequest } from '@/types'; + +export default function useCreateModelQuery(projectId: number) { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (modelData: ModelRequest) => addProjectModel(projectId, modelData), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['projectModels', projectId] }); + }, + }); +} diff --git a/frontend/src/queries/models/useModelCategoriesQuery.ts b/frontend/src/queries/models/useModelCategoriesQuery.ts new file mode 100644 index 0000000..cf9fc47 --- /dev/null +++ b/frontend/src/queries/models/useModelCategoriesQuery.ts @@ -0,0 +1,9 @@ +import { getModelCategories } from '@/api/modelApi'; +import { useSuspenseQuery } from '@tanstack/react-query'; + +export default function useModelCategoriesQuery(modelId: number) { + return useSuspenseQuery({ + queryKey: ['modelCategories', modelId], + queryFn: () => getModelCategories(modelId), + }); +} diff --git a/frontend/src/queries/models/useProjectModelsQuery.ts b/frontend/src/queries/models/useProjectModelsQuery.ts new file mode 100644 index 0000000..ba13a2c --- /dev/null +++ b/frontend/src/queries/models/useProjectModelsQuery.ts @@ -0,0 +1,9 @@ +import { getProjectModels } from '@/api/modelApi'; +import { useSuspenseQuery } from '@tanstack/react-query'; + +export default function useProjectModelsQuery(projectId: number) { + return useSuspenseQuery({ + queryKey: ['projectModels', projectId], + queryFn: () => getProjectModels(projectId), + }); +} diff --git a/frontend/src/queries/models/useTrainModelQuery.ts b/frontend/src/queries/models/useTrainModelQuery.ts new file mode 100644 index 0000000..822c7e3 --- /dev/null +++ b/frontend/src/queries/models/useTrainModelQuery.ts @@ -0,0 +1,8 @@ +import { useMutation } from '@tanstack/react-query'; +import { trainModel } from '@/api/modelApi'; + +export default function useTrainModelQuery(projectId: number) { + return useMutation({ + mutationFn: () => trainModel(projectId), + }); +} diff --git a/frontend/src/queries/models/useUpdateModelNameQuery.ts b/frontend/src/queries/models/useUpdateModelNameQuery.ts new file mode 100644 index 0000000..5ba6486 --- /dev/null +++ b/frontend/src/queries/models/useUpdateModelNameQuery.ts @@ -0,0 +1,14 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { updateModelName } from '@/api/modelApi'; +import { ModelRequest } from '@/types'; + +export default function useUpdateModelNameQuery(projectId: number, modelId: number) { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (modelData: ModelRequest) => updateModelName(projectId, modelId, modelData), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['projectModels', projectId] }); + }, + }); +} From 722a15be7b1a65fb1fb6a6f49f42fbf9c02a9108 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=ED=98=84=EC=A1=B0?= Date: Mon, 23 Sep 2024 15:56:43 +0900 Subject: [PATCH 3/6] =?UTF-8?q?Feat:=20=EB=AA=A8=EB=8D=B8=20api=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/api/modelApi.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 frontend/src/api/modelApi.ts diff --git a/frontend/src/api/modelApi.ts b/frontend/src/api/modelApi.ts new file mode 100644 index 0000000..644465a --- /dev/null +++ b/frontend/src/api/modelApi.ts @@ -0,0 +1,22 @@ +import api from '@/api/axiosConfig'; +import { ModelRequest, ModelResponse, ProjectModelsResponse, ModelCategoryResponse } from '@/types'; + +export async function updateModelName(projectId: number, modelId: number, modelData: ModelRequest) { + return api.put(`/api/projects/${projectId}/models/${modelId}`, modelData).then(({ data }) => data); +} + +export async function trainModel(projectId: number) { + return api.post(`/api/projects/${projectId}/train`).then(({ data }) => data); +} + +export async function getProjectModels(projectId: number) { + return api.get(`/api/projects/${projectId}/models`).then(({ data }) => data); +} + +export async function addProjectModel(projectId: number, modelData: ModelRequest) { + return api.post(`/api/projects/${projectId}/models`, modelData).then(({ data }) => data); +} + +export async function getModelCategories(modelId: number) { + return api.get(`/api/models/${modelId}/categories`).then(({ data }) => data); +} From 89c873a12c41cc72a3ba1a20f3400f3ac8ad2927 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=ED=98=84=EC=A1=B0?= Date: Mon, 23 Sep 2024 16:05:23 +0900 Subject: [PATCH 4/6] =?UTF-8?q?Refactor:=20=EB=AA=A8=EB=8D=B8=20=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/pages/ModelManage.tsx | 128 ++++++++++++++++++----------- 1 file changed, 79 insertions(+), 49 deletions(-) diff --git a/frontend/src/pages/ModelManage.tsx b/frontend/src/pages/ModelManage.tsx index eb3e41c..1383a45 100644 --- a/frontend/src/pages/ModelManage.tsx +++ b/frontend/src/pages/ModelManage.tsx @@ -1,12 +1,11 @@ -import { Rabbit, Bird, Turtle, Settings, Share } from 'lucide-react'; +import { Rabbit, Bird, Turtle, Share } from 'lucide-react'; import { Button } from '@/components/ui/button'; -import { Drawer, DrawerContent, DrawerHeader, DrawerTitle, DrawerTrigger } from '@/components/ui/drawer'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import ModelLineChart from '@/components/ModelLineChart'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; -import { useState, useEffect } from 'react'; +import { useState } from 'react'; import ModelBarChart from '@/components/ModelBarChart'; export default function ModelManage() { @@ -27,17 +26,7 @@ export default function ModelManage() { const [lossData] = useState(dummyLossData); const [training, setTraining] = useState(false); - - useEffect(() => { - if (training) { - const socket = new WebSocket('ws://localhost:8080/ws'); - socket.onmessage = (event) => { - const data = JSON.parse(event.data); - console.log('Received data:', data); - }; - return () => socket.close(); - } - }, [training]); + const [selectedModel, setSelectedModel] = useState(null); const handleTrainingToggle = () => { setTraining((prev) => !prev); @@ -48,24 +37,6 @@ export default function ModelManage() {

모델 관리

- - - - - - - 설정 - - - -
-
+ {/* 모델 평가 탭 */} -
-
- -
-
- -
+ {/* 모델 선택 */} +
+ +
+ + {/* 선택된 모델에 따른 BarChart 및 Labeling Preview */} + {selectedModel && ( +
+
+ +
+
+ +
+
+ )} @@ -190,11 +180,22 @@ function SettingsForm() { placeholder="-1" id="batch" /> + +
@@ -244,3 +245,32 @@ function InputWithLabel({ label, id, placeholder }: InputWithLabelProps) { ); } +interface SelectWithLabelProps { + label: string; + id: string; + options: string[]; + placeholder: string; +} + +function SelectWithLabel({ label, id, options, placeholder }: SelectWithLabelProps) { + return ( +
+ + +
+ ); +} From 11ca592a78c511a9e96526a76752c67d1165e159 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=ED=98=84=EC=A1=B0?= Date: Mon, 23 Sep 2024 16:19:30 +0900 Subject: [PATCH 5/6] =?UTF-8?q?Feat:=20=EB=A0=88=EC=9D=B4=EB=B8=94=20?= =?UTF-8?q?=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=20api=20=EB=B0=8F=20?= =?UTF-8?q?=EC=BF=BC=EB=A6=AC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/api/categoryApi.ts | 31 +++++++++++++++++++ frontend/src/api/modelApi.ts | 10 +++--- .../queries/category/useAddCategoryQuery.ts | 14 +++++++++ .../queries/category/useCategoryByIdQuery.ts | 9 ++++++ .../category/useCheckCategoryExistsQuery.ts | 9 ++++++ .../category/useDeleteCategoryQuery.ts | 13 ++++++++ .../category/useProjectCategoriesQuery.ts | 9 ++++++ frontend/src/types/index.ts | 11 +++++++ 8 files changed, 101 insertions(+), 5 deletions(-) create mode 100644 frontend/src/api/categoryApi.ts create mode 100644 frontend/src/queries/category/useAddCategoryQuery.ts create mode 100644 frontend/src/queries/category/useCategoryByIdQuery.ts create mode 100644 frontend/src/queries/category/useCheckCategoryExistsQuery.ts create mode 100644 frontend/src/queries/category/useDeleteCategoryQuery.ts create mode 100644 frontend/src/queries/category/useProjectCategoriesQuery.ts diff --git a/frontend/src/api/categoryApi.ts b/frontend/src/api/categoryApi.ts new file mode 100644 index 0000000..02a1f42 --- /dev/null +++ b/frontend/src/api/categoryApi.ts @@ -0,0 +1,31 @@ +import api from '@/api/axiosConfig'; +import { LabelCategoryRequest, LabelCategoryResponse } from '@/types'; + +// 레이블 카테고리 리스트 조회 +export async function getProjectCategories(projectId: number) { + return api.get(`/projects/${projectId}/categories`).then(({ data }) => data); +} + +// 레이블 카테고리 추가 +export async function addProjectCategories(projectId: number, categoryData: LabelCategoryRequest) { + return api.post(`/projects/${projectId}/categories`, categoryData).then(({ data }) => data); +} + +// 레이블 카테고리 단일 조회 +export async function getCategoryById(projectId: number, categoryId: number) { + return api.get(`/projects/${projectId}/categories/${categoryId}`).then(({ data }) => data); +} + +// 레이블 카테고리 삭제 +export async function deleteCategory(projectId: number, categoryId: number) { + return api.delete(`/projects/${projectId}/categories/${categoryId}`).then(({ data }) => data); +} + +// 레이블 카테고리 존재 여부 조회 +export async function checkCategoryExists(projectId: number, categoryName: string) { + return api + .get(`/projects/${projectId}/categories/exist`, { + params: { categoryName }, + }) + .then(({ data }) => data); +} diff --git a/frontend/src/api/modelApi.ts b/frontend/src/api/modelApi.ts index 644465a..106b735 100644 --- a/frontend/src/api/modelApi.ts +++ b/frontend/src/api/modelApi.ts @@ -2,21 +2,21 @@ import api from '@/api/axiosConfig'; import { ModelRequest, ModelResponse, ProjectModelsResponse, ModelCategoryResponse } from '@/types'; export async function updateModelName(projectId: number, modelId: number, modelData: ModelRequest) { - return api.put(`/api/projects/${projectId}/models/${modelId}`, modelData).then(({ data }) => data); + return api.put(`/projects/${projectId}/models/${modelId}`, modelData).then(({ data }) => data); } export async function trainModel(projectId: number) { - return api.post(`/api/projects/${projectId}/train`).then(({ data }) => data); + return api.post(`/projects/${projectId}/train`).then(({ data }) => data); } export async function getProjectModels(projectId: number) { - return api.get(`/api/projects/${projectId}/models`).then(({ data }) => data); + return api.get(`/projects/${projectId}/models`).then(({ data }) => data); } export async function addProjectModel(projectId: number, modelData: ModelRequest) { - return api.post(`/api/projects/${projectId}/models`, modelData).then(({ data }) => data); + return api.post(`/projects/${projectId}/models`, modelData).then(({ data }) => data); } export async function getModelCategories(modelId: number) { - return api.get(`/api/models/${modelId}/categories`).then(({ data }) => data); + return api.get(`/models/${modelId}/categories`).then(({ data }) => data); } diff --git a/frontend/src/queries/category/useAddCategoryQuery.ts b/frontend/src/queries/category/useAddCategoryQuery.ts new file mode 100644 index 0000000..631b5a9 --- /dev/null +++ b/frontend/src/queries/category/useAddCategoryQuery.ts @@ -0,0 +1,14 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { addProjectCategories } from '@/api/categoryApi'; +import { LabelCategoryRequest } from '@/types'; + +export default function useAddCategoryQuery(projectId: number) { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: (categoryData: LabelCategoryRequest) => addProjectCategories(projectId, categoryData), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['projectCategories', projectId] }); + }, + }); +} diff --git a/frontend/src/queries/category/useCategoryByIdQuery.ts b/frontend/src/queries/category/useCategoryByIdQuery.ts new file mode 100644 index 0000000..ef9c501 --- /dev/null +++ b/frontend/src/queries/category/useCategoryByIdQuery.ts @@ -0,0 +1,9 @@ +import { getCategoryById } from '@/api/categoryApi'; +import { useSuspenseQuery } from '@tanstack/react-query'; + +export default function useCategoryByIdQuery(projectId: number, categoryId: number) { + return useSuspenseQuery({ + queryKey: ['category', projectId, categoryId], + queryFn: () => getCategoryById(projectId, categoryId), + }); +} diff --git a/frontend/src/queries/category/useCheckCategoryExistsQuery.ts b/frontend/src/queries/category/useCheckCategoryExistsQuery.ts new file mode 100644 index 0000000..0174c17 --- /dev/null +++ b/frontend/src/queries/category/useCheckCategoryExistsQuery.ts @@ -0,0 +1,9 @@ +import { checkCategoryExists } from '@/api/categoryApi'; +import { useSuspenseQuery } from '@tanstack/react-query'; + +export default function useCheckCategoryExistsQuery(projectId: number, categoryName: string) { + return useSuspenseQuery({ + queryKey: ['categoryExists', projectId, categoryName], + queryFn: () => checkCategoryExists(projectId, categoryName), + }); +} diff --git a/frontend/src/queries/category/useDeleteCategoryQuery.ts b/frontend/src/queries/category/useDeleteCategoryQuery.ts new file mode 100644 index 0000000..94eca1d --- /dev/null +++ b/frontend/src/queries/category/useDeleteCategoryQuery.ts @@ -0,0 +1,13 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { deleteCategory } from '@/api/categoryApi'; + +export default function useDeleteCategoryQuery(projectId: number, categoryId: number) { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: () => deleteCategory(projectId, categoryId), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['projectCategories', projectId] }); + }, + }); +} diff --git a/frontend/src/queries/category/useProjectCategoriesQuery.ts b/frontend/src/queries/category/useProjectCategoriesQuery.ts new file mode 100644 index 0000000..f069959 --- /dev/null +++ b/frontend/src/queries/category/useProjectCategoriesQuery.ts @@ -0,0 +1,9 @@ +import { getProjectCategories } from '@/api/categoryApi'; +import { useSuspenseQuery } from '@tanstack/react-query'; + +export default function useProjectCategoriesQuery(projectId: number) { + return useSuspenseQuery({ + queryKey: ['projectCategories', projectId], + queryFn: () => getProjectCategories(projectId), + }); +} diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts index 4be47b3..c0bc387 100644 --- a/frontend/src/types/index.ts +++ b/frontend/src/types/index.ts @@ -300,3 +300,14 @@ export interface ModelCategoryResponse { // 프로젝트 모델 리스트 응답 DTO export interface ProjectModelsResponse extends Array {} + +// 카테고리 요청 DTO +export interface LabelCategoryRequest { + labelCategoryList: number[]; +} + +// 카테고리 응답 DTO +export interface LabelCategoryResponse { + id: number; + name: string; +} From 732143f91c9887082744af8df80ea6309c29c328 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=ED=98=84=EC=A1=B0?= Date: Mon, 23 Sep 2024 16:22:49 +0900 Subject: [PATCH 6/6] =?UTF-8?q?Refactor:=20=ED=95=84=EC=9A=94=EC=97=86?= =?UTF-8?q?=EB=8A=94=20=EA=B3=B5=EC=9C=A0=20=EB=B2=84=ED=8A=BC=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/pages/ModelManage.tsx | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/frontend/src/pages/ModelManage.tsx b/frontend/src/pages/ModelManage.tsx index 1383a45..eb814d4 100644 --- a/frontend/src/pages/ModelManage.tsx +++ b/frontend/src/pages/ModelManage.tsx @@ -1,4 +1,4 @@ -import { Rabbit, Bird, Turtle, Share } from 'lucide-react'; +import { Rabbit, Bird, Turtle } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; @@ -37,14 +37,6 @@ export default function ModelManage() {

모델 관리

-