From 4beef110df1fe0e6abe20b0ada530d1bf40c6394 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9C=A0=EB=B3=91=EC=B2=A0?= Date: Mon, 23 Mar 2026 12:29:47 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20[=EC=9E=91=EC=97=85=EC=9E=90?= =?UTF-8?q?=ED=99=94=EB=A9=B4]=20WorkerScreen/actions.ts=20buildApiUrl=20?= =?UTF-8?q?=EB=A7=88=EC=9D=B4=EA=B7=B8=EB=A0=88=EC=9D=B4=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ${API_URL} 직접 조립 22개 함수 → buildApiUrl() 전환 - const API_URL 선언 제거 - sam-docs 코딩 컨벤션 금지 패턴 예시 보강 Co-Authored-By: Claude Opus 4.6 (1M context) --- sam-docs/frontend/v1/09-coding-conventions.md | 12 +++-- .../production/WorkerScreen/actions.ts | 46 +++++++++---------- 2 files changed, 31 insertions(+), 27 deletions(-) diff --git a/sam-docs/frontend/v1/09-coding-conventions.md b/sam-docs/frontend/v1/09-coding-conventions.md index c41171e1..4d624615 100644 --- a/sam-docs/frontend/v1/09-coding-conventions.md +++ b/sam-docs/frontend/v1/09-coding-conventions.md @@ -48,12 +48,18 @@ export default function Page() { ```tsx // ✅ 필수 import { buildApiUrl } from '@/lib/api/query-params'; -const url = buildApiUrl('/api/v1/items', { search, page }); +buildApiUrl('/api/v1/items', { search, page }); // 쿼리 파라미터 +buildApiUrl(`/api/v1/items/${id}`); // 동적 경로 +buildApiUrl(`/api/v1/items/${id}`, { with_details: true }); // 동적 경로 + 파라미터 -// ❌ 금지 +// ❌ 금지 패턴 1: ${API_URL} 직접 조립 +const API_URL = process.env.NEXT_PUBLIC_API_URL; +url: `${API_URL}/api/v1/items/${id}`, + +// ❌ 금지 패턴 2: URLSearchParams 직접 사용 const params = new URLSearchParams(); params.set('search', value); -const url = `${API_URL}/api/v1/items?${params.toString()}`; +url: `${API_URL}/api/v1/items?${params.toString()}`, ``` --- diff --git a/src/components/production/WorkerScreen/actions.ts b/src/components/production/WorkerScreen/actions.ts index ac554c95..66bc8895 100644 --- a/src/components/production/WorkerScreen/actions.ts +++ b/src/components/production/WorkerScreen/actions.ts @@ -214,8 +214,6 @@ function transformToWorkerScreenFormat(api: WorkOrderApiItem): WorkOrder { }; } -const API_URL = process.env.NEXT_PUBLIC_API_URL; - // ===== 내 작업 목록 조회 ===== export async function getMyWorkOrders(): Promise<{ success: boolean; @@ -224,7 +222,7 @@ export async function getMyWorkOrders(): Promise<{ }> { interface PaginatedWO { data: WorkOrderApiItem[] } const result = await executeServerAction({ - url: `${API_URL}/api/v1/work-orders?per_page=100&worker_screen=1`, + url: buildApiUrl('/api/v1/work-orders', { per_page: 100, worker_screen: 1 }), errorMessage: '작업 목록 조회에 실패했습니다.', }); if (!result.success || !result.data) return { success: false, data: [], error: result.error }; @@ -241,7 +239,7 @@ export async function completeWorkOrder( materials?: { materialId: number; quantity: number; lotNo?: string }[] ): Promise<{ success: boolean; lotNo?: string; error?: string }> { const result = await executeServerAction({ - url: `${API_URL}/api/v1/work-orders/${id}/status`, + url: buildApiUrl(`/api/v1/work-orders/${id}/status`), method: 'PATCH', body: { status: 'completed', materials }, errorMessage: '작업 완료 처리에 실패했습니다.', @@ -258,7 +256,7 @@ export async function updateWorkOrderInfo( data: { scheduled_date?: string; team_id?: number | null; assignee_id?: number | null } ): Promise<{ success: boolean; error?: string }> { const result = await executeServerAction({ - url: `${API_URL}/api/v1/work-orders/${id}`, + url: buildApiUrl(`/api/v1/work-orders/${id}`), method: 'PUT', body: data, errorMessage: '작업정보 저장에 실패했습니다.', @@ -300,7 +298,7 @@ export async function getMaterialsForWorkOrder( work_order_item_id?: number; lot_prefix?: string; part_type?: string; category?: string; } const result = await executeServerAction({ - url: `${API_URL}/api/v1/work-orders/${workOrderId}/materials`, + url: buildApiUrl(`/api/v1/work-orders/${workOrderId}/materials`), errorMessage: '자재 목록 조회에 실패했습니다.', }); if (!result.success || !result.data) return { success: false, data: [], error: result.error }; @@ -323,7 +321,7 @@ export async function registerMaterialInput( inputs: { stock_lot_id: number; qty: number; work_order_item_id?: number }[] ): Promise<{ success: boolean; error?: string }> { const result = await executeServerAction({ - url: `${API_URL}/api/v1/work-orders/${workOrderId}/material-inputs`, + url: buildApiUrl(`/api/v1/work-orders/${workOrderId}/material-inputs`), method: 'POST', body: { inputs }, errorMessage: '자재 투입 등록에 실패했습니다.', @@ -360,7 +358,7 @@ export async function getMaterialsForItem( bom_group_key?: string; } const result = await executeServerAction({ - url: `${API_URL}/api/v1/work-orders/${workOrderId}/items/${itemId}/materials`, + url: buildApiUrl(`/api/v1/work-orders/${workOrderId}/items/${itemId}/materials`), errorMessage: '개소별 자재 목록 조회에 실패했습니다.', }); if (!result.success || !result.data) return { success: false, data: [], error: result.error }; @@ -390,7 +388,7 @@ export async function registerMaterialInputForItem( replace = false ): Promise<{ success: boolean; error?: string }> { const result = await executeServerAction({ - url: `${API_URL}/api/v1/work-orders/${workOrderId}/items/${itemId}/material-inputs`, + url: buildApiUrl(`/api/v1/work-orders/${workOrderId}/items/${itemId}/material-inputs`), method: 'POST', body: { inputs, replace }, errorMessage: '개소별 자재 투입 등록에 실패했습니다.', @@ -428,7 +426,7 @@ export async function getMaterialInputsForItem( input_by: number | null; input_by_name: string | null; input_at: string | null; } const result = await executeServerAction({ - url: `${API_URL}/api/v1/work-orders/${workOrderId}/items/${itemId}/material-inputs`, + url: buildApiUrl(`/api/v1/work-orders/${workOrderId}/items/${itemId}/material-inputs`), errorMessage: '개소별 투입 이력 조회에 실패했습니다.', }); if (!result.success || !result.data) return { success: false, data: [], error: result.error }; @@ -449,7 +447,7 @@ export async function deleteMaterialInput( inputId: number ): Promise<{ success: boolean; error?: string }> { const result = await executeServerAction({ - url: `${API_URL}/api/v1/work-orders/${workOrderId}/material-inputs/${inputId}`, + url: buildApiUrl(`/api/v1/work-orders/${workOrderId}/material-inputs/${inputId}`), method: 'DELETE', errorMessage: '자재 투입 삭제에 실패했습니다.', }); @@ -463,7 +461,7 @@ export async function updateMaterialInput( qty: number ): Promise<{ success: boolean; data?: { id: number; qty: number; changed: boolean }; error?: string }> { const result = await executeServerAction<{ id: number; qty: number; changed: boolean }>({ - url: `${API_URL}/api/v1/work-orders/${workOrderId}/material-inputs/${inputId}`, + url: buildApiUrl(`/api/v1/work-orders/${workOrderId}/material-inputs/${inputId}`), method: 'PATCH', body: { qty }, errorMessage: '자재 투입 수정에 실패했습니다.', @@ -481,7 +479,7 @@ export async function reportIssue( } ): Promise<{ success: boolean; error?: string }> { const result = await executeServerAction({ - url: `${API_URL}/api/v1/work-orders/${workOrderId}/issues`, + url: buildApiUrl(`/api/v1/work-orders/${workOrderId}/issues`), method: 'POST', body: data, errorMessage: '이슈 보고에 실패했습니다.', @@ -523,7 +521,7 @@ export async function getProcessSteps( items?: { id: number; item_no: string; location: string; is_priority: boolean; spec: string; material: string; lot: string }[]; } const result = await executeServerAction({ - url: `${API_URL}/api/v1/work-orders/${workOrderId}/process-steps`, + url: buildApiUrl(`/api/v1/work-orders/${workOrderId}/process-steps`), errorMessage: '공정 단계 조회에 실패했습니다.', }); if (!result.success || !result.data) return { success: false, data: [], error: result.error }; @@ -546,7 +544,7 @@ export async function requestInspection( stepId: string ): Promise<{ success: boolean; error?: string }> { const result = await executeServerAction({ - url: `${API_URL}/api/v1/work-orders/${workOrderId}/process-steps/${stepId}/inspection-request`, + url: buildApiUrl(`/api/v1/work-orders/${workOrderId}/process-steps/${stepId}/inspection-request`), method: 'POST', body: {}, errorMessage: '검사 요청에 실패했습니다.', @@ -580,7 +578,7 @@ export async function getStepProgress( error?: string; }> { const result = await executeServerAction({ - url: `${API_URL}/api/v1/work-orders/${workOrderId}/step-progress`, + url: buildApiUrl(`/api/v1/work-orders/${workOrderId}/step-progress`), errorMessage: '단계 진행 조회에 실패했습니다.', }); if (!result.success || !result.data) return { success: false, data: [], error: result.error }; @@ -593,7 +591,7 @@ export async function toggleStepProgress( progressId: number ): Promise<{ success: boolean; data?: StepProgressItem; error?: string }> { const result = await executeServerAction({ - url: `${API_URL}/api/v1/work-orders/${workOrderId}/step-progress/${progressId}/toggle`, + url: buildApiUrl(`/api/v1/work-orders/${workOrderId}/step-progress/${progressId}/toggle`), method: 'PATCH', errorMessage: '단계 토글에 실패했습니다.', }); @@ -610,7 +608,7 @@ export async function getWorkOrderDetail( }> { // eslint-disable-next-line @typescript-eslint/no-explicit-any const result = await executeServerAction({ - url: `${API_URL}/api/v1/work-orders/${workOrderId}`, + url: buildApiUrl(`/api/v1/work-orders/${workOrderId}`), errorMessage: '작업지시 상세 조회에 실패했습니다.', }); if (!result.success || !result.data) return { success: false, data: [], error: result.error }; @@ -723,7 +721,7 @@ export async function saveItemInspection( inspectionData: Record ): Promise<{ success: boolean; data?: Record; error?: string }> { const result = await executeServerAction>({ - url: `${API_URL}/api/v1/work-orders/${workOrderId}/items/${itemId}/inspection`, + url: buildApiUrl(`/api/v1/work-orders/${workOrderId}/items/${itemId}/inspection`), method: 'POST', body: { process_type: processType, inspection_data: inspectionData }, errorMessage: '검사 데이터 저장에 실패했습니다.', @@ -750,7 +748,7 @@ export async function getWorkOrderInspectionData( error?: string; }> { const result = await executeServerAction<{ work_order_id: number; items: InspectionDataItem[]; total: number }>({ - url: `${API_URL}/api/v1/work-orders/${workOrderId}/inspection-data`, + url: buildApiUrl(`/api/v1/work-orders/${workOrderId}/inspection-data`), errorMessage: '검사 데이터 조회에 실패했습니다.', }); return { success: result.success, data: result.data, error: result.error }; @@ -772,7 +770,7 @@ export async function saveWorkLog( error?: string; }> { const result = await executeServerAction<{ document_id: number; document_no: string; status: string }>({ - url: `${API_URL}/api/v1/work-orders/${workOrderId}/work-log`, + url: buildApiUrl(`/api/v1/work-orders/${workOrderId}/work-log`), method: 'POST', body: data, errorMessage: '작업일지 저장에 실패했습니다.', @@ -801,7 +799,7 @@ export async function getWorkLog( work_stats: Record; bending_images: Record; }>({ - url: `${API_URL}/api/v1/work-orders/${workOrderId}/work-log`, + url: buildApiUrl(`/api/v1/work-orders/${workOrderId}/work-log`), errorMessage: '작업일지 조회에 실패했습니다.', }); return { success: result.success, data: result.data, error: result.error }; @@ -818,7 +816,7 @@ export async function getInspectionTemplate( error?: string; }> { const result = await executeServerAction({ - url: `${API_URL}/api/v1/work-orders/${workOrderId}/inspection-template`, + url: buildApiUrl(`/api/v1/work-orders/${workOrderId}/inspection-template`), errorMessage: '검사 템플릿 조회에 실패했습니다.', }); return { success: result.success, data: result.data, error: result.error }; @@ -837,7 +835,7 @@ export async function saveInspectionDocument( error?: string; }> { const result = await executeServerAction<{ document_id: number; document_no: string; status: string }>({ - url: `${API_URL}/api/v1/work-orders/${workOrderId}/inspection-document`, + url: buildApiUrl(`/api/v1/work-orders/${workOrderId}/inspection-document`), method: 'POST', body: data, errorMessage: '검사 문서 동기화에 실패했습니다.',