refactor: [작업자화면] WorkerScreen/actions.ts buildApiUrl 마이그레이션

- ${API_URL} 직접 조립 22개 함수 → buildApiUrl() 전환
- const API_URL 선언 제거
- sam-docs 코딩 컨벤션 금지 패턴 예시 보강

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
유병철
2026-03-23 12:29:47 +09:00
parent ed77ac2759
commit 4beef110df
2 changed files with 31 additions and 27 deletions

View File

@@ -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()}`,
```
---

View File

@@ -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<PaginatedWO>({
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<MaterialApiItem[]>({
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<MaterialItemApiItem[]>({
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<HistoryApiItem[]>({
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<StepApiItem[]>({
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<StepProgressItem[]>({
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<StepProgressItem>({
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<any>({
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<string, unknown>
): Promise<{ success: boolean; data?: Record<string, unknown>; error?: string }> {
const result = await executeServerAction<Record<string, unknown>>({
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<string, unknown>;
bending_images: Record<string, string>;
}>({
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<InspectionTemplateData>({
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: '검사 문서 동기화에 실패했습니다.',