From b4b6426ded907e44895a3fb0d1f61daddb6f1f64 Mon Sep 17 00:00:00 2001 From: SEAN Date: Mon, 2 Mar 2026 18:08:48 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=ED=83=9C=EA=B7=B8=20=EA=B4=80=EB=A6=AC?= =?UTF-8?q?=20=ED=8E=98=EC=9D=B4=EC=A7=80=20API=20=EC=97=B0=EB=8F=99=20(#4?= =?UTF-8?q?1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - tag.api.ts 신규 생성 (list/create/update/delete POST 엔드포인트) - TagListItem, TagResponse, TagPagination 등 API 타입 정의 - Mock 데이터(MOCK_TAGS, SERVICE_OPTIONS 등) 제거 - 서비스 목록 동적 로드 (fetchServices) + 탭 필터링 - X-Service-Code 헤더 처리 (전체 API) - tagCode 우측 배치 + 클릭 시 클립보드 복사 - swagger 제약조건 반영 (태그명 50자, 설명 200자) - ServiceSummary에 serviceId 필드 추가 Closes #41 --- react/src/api/tag.api.ts | 40 +++ react/src/features/service/types.ts | 1 + .../features/tag/components/TagAddModal.tsx | 17 +- react/src/features/tag/components/TagCard.tsx | 30 +- .../src/features/tag/pages/TagManagePage.tsx | 258 +++++++++++++----- react/src/features/tag/types.ts | 173 +++++------- 6 files changed, 340 insertions(+), 179 deletions(-) create mode 100644 react/src/api/tag.api.ts diff --git a/react/src/api/tag.api.ts b/react/src/api/tag.api.ts new file mode 100644 index 0000000..040f2c8 --- /dev/null +++ b/react/src/api/tag.api.ts @@ -0,0 +1,40 @@ +import { apiClient } from "./client"; +import type { ApiResponse } from "@/types/api"; +import type { + TagListRequest, + TagListResponse, + TagResponse, + CreateTagRequest, + UpdateTagRequest, + DeleteTagRequest, +} from "@/features/tag/types"; + +/** 태그 목록 조회 (serviceCode 생략 시 전체 서비스) */ +export function fetchTagList(data: TagListRequest, serviceCode?: string) { + return apiClient.post>( + "/v1/in/tag/list", + data, + serviceCode ? { headers: { "X-Service-Code": serviceCode } } : undefined, + ); +} + +/** 태그 생성 */ +export function createTag(data: CreateTagRequest, serviceCode: string) { + return apiClient.post>("/v1/in/tag/create", data, { + headers: { "X-Service-Code": serviceCode }, + }); +} + +/** 태그 수정 */ +export function updateTag(data: UpdateTagRequest, serviceCode: string) { + return apiClient.post>("/v1/in/tag/update", data, { + headers: { "X-Service-Code": serviceCode }, + }); +} + +/** 태그 삭제 */ +export function deleteTag(data: DeleteTagRequest, serviceCode: string) { + return apiClient.post>("/v1/in/tag/delete", data, { + headers: { "X-Service-Code": serviceCode }, + }); +} diff --git a/react/src/features/service/types.ts b/react/src/features/service/types.ts index 1af9996..7a06dce 100644 --- a/react/src/features/service/types.ts +++ b/react/src/features/service/types.ts @@ -26,6 +26,7 @@ export interface PlatformCredentialSummary { // 서비스 목록 항목 export interface ServiceSummary { + serviceId: number; serviceCode: string; serviceName: string; serviceIcon?: string; diff --git a/react/src/features/tag/components/TagAddModal.tsx b/react/src/features/tag/components/TagAddModal.tsx index 4bd9200..468ef0d 100644 --- a/react/src/features/tag/components/TagAddModal.tsx +++ b/react/src/features/tag/components/TagAddModal.tsx @@ -1,14 +1,14 @@ import { useState, useRef, useEffect } from "react"; import useShake from "@/hooks/useShake"; -import { SERVICE_OPTIONS } from "../types"; interface TagAddModalProps { open: boolean; onClose: () => void; onSave: (data: { name: string; service: string; description: string }) => void; + serviceOptions: string[]; } -export default function TagAddModal({ open, onClose, onSave }: TagAddModalProps) { +export default function TagAddModal({ open, onClose, onSave, serviceOptions }: TagAddModalProps) { const { triggerShake, cls } = useShake(); const [name, setName] = useState(""); @@ -107,9 +107,10 @@ export default function TagAddModal({ open, onClose, onSave }: TagAddModalProps) type="text" value={name} onChange={(e) => { - setName(e.target.value); + if (e.target.value.length <= 50) setName(e.target.value); if (e.target.value.trim()) setNameError(false); }} + maxLength={50} placeholder="태그 이름을 입력하세요" className={`w-full h-[38px] px-3 border rounded-lg text-sm focus:ring-2 focus:ring-primary/20 focus:border-primary outline-none transition-all ${ nameError @@ -154,7 +155,7 @@ export default function TagAddModal({ open, onClose, onSave }: TagAddModalProps) {serviceOpen && (
    - {SERVICE_OPTIONS.map((opt) => ( + {serviceOptions.map((opt) => (
  • { @@ -192,11 +193,17 @@ export default function TagAddModal({ open, onClose, onSave }: TagAddModalProps)