feat: 품목기준관리 Zustand 리팩토링 및 422 에러 팝업

- Zustand store 도입 (useItemMasterStore)
- 훅 분리 및 구조 개선 (hooks/, contexts/)
- 422 ValidationException 에러 AlertDialog 팝업 추가
- API 함수 분리 (src/lib/api/item-master.ts)
- 타입 정의 정리 (item-master.types.ts, item-master-api.ts)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
byeongcheolryu
2026-01-06 20:49:37 +09:00
32 changed files with 6747 additions and 1410 deletions

View File

@@ -38,6 +38,11 @@ import type {
LinkEntityRequest,
LinkBomRequest,
ReorderRelationshipsRequest,
// 2025-12-21 추가: 재질/표면처리 타입
MaterialOptionRequest,
MaterialOptionResponse,
TreatmentOptionRequest,
TreatmentOptionResponse,
} from '@/types/item-master-api';
import { getAuthHeaders } from './auth-headers';
import { handleApiError } from './error-handler';
@@ -1893,6 +1898,36 @@ export const itemMasterApi = {
}
},
update: async (id: number, data: Partial<UnitOptionRequest>): Promise<ApiResponse<UnitOptionResponse>> => {
const startTime = apiLogger.logRequest('PUT', `${BASE_URL}/item-master/unit-options/${id}`, data);
try {
const headers = getAuthHeaders();
const response = await fetch(`${BASE_URL}/item-master/unit-options/${id}`, {
method: 'PUT',
headers,
body: JSON.stringify(data),
});
if (!response.ok) {
await handleApiError(response);
}
const result: ApiResponse<UnitOptionResponse> = await response.json();
apiLogger.logResponse('PUT', `${BASE_URL}/item-master/unit-options/${id}`, response.status, result, startTime);
return result;
} catch (error) {
if (error instanceof TypeError) {
apiLogger.logError('PUT', `${BASE_URL}/item-master/unit-options/${id}`, error, undefined, startTime);
throw new Error('네트워크 연결을 확인해주세요. 서버에 연결할 수 없습니다.');
}
apiLogger.logError('PUT', `${BASE_URL}/item-master/unit-options/${id}`, error as Error, undefined, startTime);
throw error;
}
},
delete: async (id: number): Promise<ApiResponse<void>> => {
const startTime = apiLogger.logRequest('DELETE', `${BASE_URL}/item-master/unit-options/${id}`);
@@ -1922,4 +1957,276 @@ export const itemMasterApi = {
}
},
},
// ============================================
// 재질 관리
// ============================================
materials: {
list: async (): Promise<ApiResponse<any[]>> => {
const startTime = apiLogger.logRequest('GET', `${BASE_URL}/item-master/materials`);
try {
const headers = getAuthHeaders();
const response = await fetch(`${BASE_URL}/item-master/materials`, {
method: 'GET',
headers,
});
if (!response.ok) {
await handleApiError(response);
}
const result: ApiResponse<any[]> = await response.json();
apiLogger.logResponse('GET', `${BASE_URL}/item-master/materials`, response.status, result, startTime);
return result;
} catch (error) {
if (error instanceof TypeError) {
apiLogger.logError('GET', `${BASE_URL}/item-master/materials`, error, undefined, startTime);
throw new Error('네트워크 연결을 확인해주세요. 서버에 연결할 수 없습니다.');
}
apiLogger.logError('GET', `${BASE_URL}/item-master/materials`, error as Error, undefined, startTime);
throw error;
}
},
create: async (data: {
material_code: string;
material_name: string;
material_type: string;
thickness?: string;
description?: string;
is_active?: boolean;
}): Promise<ApiResponse<any>> => {
const startTime = apiLogger.logRequest('POST', `${BASE_URL}/item-master/materials`, data);
try {
const headers = getAuthHeaders();
const response = await fetch(`${BASE_URL}/item-master/materials`, {
method: 'POST',
headers,
body: JSON.stringify(data),
});
if (!response.ok) {
await handleApiError(response);
}
const result: ApiResponse<any> = await response.json();
apiLogger.logResponse('POST', `${BASE_URL}/item-master/materials`, response.status, result, startTime);
return result;
} catch (error) {
if (error instanceof TypeError) {
apiLogger.logError('POST', `${BASE_URL}/item-master/materials`, error, undefined, startTime);
throw new Error('네트워크 연결을 확인해주세요. 서버에 연결할 수 없습니다.');
}
apiLogger.logError('POST', `${BASE_URL}/item-master/materials`, error as Error, undefined, startTime);
throw error;
}
},
update: async (id: number | string, data: {
material_code?: string;
material_name?: string;
material_type?: string;
thickness?: string;
description?: string;
is_active?: boolean;
}): Promise<ApiResponse<any>> => {
const startTime = apiLogger.logRequest('PUT', `${BASE_URL}/item-master/materials/${id}`, data);
try {
const headers = getAuthHeaders();
const response = await fetch(`${BASE_URL}/item-master/materials/${id}`, {
method: 'PUT',
headers,
body: JSON.stringify(data),
});
if (!response.ok) {
await handleApiError(response);
}
const result: ApiResponse<any> = await response.json();
apiLogger.logResponse('PUT', `${BASE_URL}/item-master/materials/${id}`, response.status, result, startTime);
return result;
} catch (error) {
if (error instanceof TypeError) {
apiLogger.logError('PUT', `${BASE_URL}/item-master/materials/${id}`, error, undefined, startTime);
throw new Error('네트워크 연결을 확인해주세요. 서버에 연결할 수 없습니다.');
}
apiLogger.logError('PUT', `${BASE_URL}/item-master/materials/${id}`, error as Error, undefined, startTime);
throw error;
}
},
delete: async (id: number | string): Promise<ApiResponse<void>> => {
const startTime = apiLogger.logRequest('DELETE', `${BASE_URL}/item-master/materials/${id}`);
try {
const headers = getAuthHeaders();
const response = await fetch(`${BASE_URL}/item-master/materials/${id}`, {
method: 'DELETE',
headers,
});
if (!response.ok) {
await handleApiError(response);
}
const result: ApiResponse<void> = await response.json();
apiLogger.logResponse('DELETE', `${BASE_URL}/item-master/materials/${id}`, response.status, result, startTime);
return result;
} catch (error) {
if (error instanceof TypeError) {
apiLogger.logError('DELETE', `${BASE_URL}/item-master/materials/${id}`, error, undefined, startTime);
throw new Error('네트워크 연결을 확인해주세요. 서버에 연결할 수 없습니다.');
}
apiLogger.logError('DELETE', `${BASE_URL}/item-master/materials/${id}`, error as Error, undefined, startTime);
throw error;
}
},
},
// ============================================
// 표면처리 관리
// ============================================
treatments: {
list: async (): Promise<ApiResponse<any[]>> => {
const startTime = apiLogger.logRequest('GET', `${BASE_URL}/item-master/surface-treatments`);
try {
const headers = getAuthHeaders();
const response = await fetch(`${BASE_URL}/item-master/surface-treatments`, {
method: 'GET',
headers,
});
if (!response.ok) {
await handleApiError(response);
}
const result: ApiResponse<any[]> = await response.json();
apiLogger.logResponse('GET', `${BASE_URL}/item-master/surface-treatments`, response.status, result, startTime);
return result;
} catch (error) {
if (error instanceof TypeError) {
apiLogger.logError('GET', `${BASE_URL}/item-master/surface-treatments`, error, undefined, startTime);
throw new Error('네트워크 연결을 확인해주세요. 서버에 연결할 수 없습니다.');
}
apiLogger.logError('GET', `${BASE_URL}/item-master/surface-treatments`, error as Error, undefined, startTime);
throw error;
}
},
create: async (data: {
treatment_code: string;
treatment_name: string;
treatment_type: string;
description?: string;
is_active?: boolean;
}): Promise<ApiResponse<any>> => {
const startTime = apiLogger.logRequest('POST', `${BASE_URL}/item-master/surface-treatments`, data);
try {
const headers = getAuthHeaders();
const response = await fetch(`${BASE_URL}/item-master/surface-treatments`, {
method: 'POST',
headers,
body: JSON.stringify(data),
});
if (!response.ok) {
await handleApiError(response);
}
const result: ApiResponse<any> = await response.json();
apiLogger.logResponse('POST', `${BASE_URL}/item-master/surface-treatments`, response.status, result, startTime);
return result;
} catch (error) {
if (error instanceof TypeError) {
apiLogger.logError('POST', `${BASE_URL}/item-master/surface-treatments`, error, undefined, startTime);
throw new Error('네트워크 연결을 확인해주세요. 서버에 연결할 수 없습니다.');
}
apiLogger.logError('POST', `${BASE_URL}/item-master/surface-treatments`, error as Error, undefined, startTime);
throw error;
}
},
update: async (id: number | string, data: {
treatment_code?: string;
treatment_name?: string;
treatment_type?: string;
description?: string;
is_active?: boolean;
}): Promise<ApiResponse<any>> => {
const startTime = apiLogger.logRequest('PUT', `${BASE_URL}/item-master/surface-treatments/${id}`, data);
try {
const headers = getAuthHeaders();
const response = await fetch(`${BASE_URL}/item-master/surface-treatments/${id}`, {
method: 'PUT',
headers,
body: JSON.stringify(data),
});
if (!response.ok) {
await handleApiError(response);
}
const result: ApiResponse<any> = await response.json();
apiLogger.logResponse('PUT', `${BASE_URL}/item-master/surface-treatments/${id}`, response.status, result, startTime);
return result;
} catch (error) {
if (error instanceof TypeError) {
apiLogger.logError('PUT', `${BASE_URL}/item-master/surface-treatments/${id}`, error, undefined, startTime);
throw new Error('네트워크 연결을 확인해주세요. 서버에 연결할 수 없습니다.');
}
apiLogger.logError('PUT', `${BASE_URL}/item-master/surface-treatments/${id}`, error as Error, undefined, startTime);
throw error;
}
},
delete: async (id: number | string): Promise<ApiResponse<void>> => {
const startTime = apiLogger.logRequest('DELETE', `${BASE_URL}/item-master/surface-treatments/${id}`);
try {
const headers = getAuthHeaders();
const response = await fetch(`${BASE_URL}/item-master/surface-treatments/${id}`, {
method: 'DELETE',
headers,
});
if (!response.ok) {
await handleApiError(response);
}
const result: ApiResponse<void> = await response.json();
apiLogger.logResponse('DELETE', `${BASE_URL}/item-master/surface-treatments/${id}`, response.status, result, startTime);
return result;
} catch (error) {
if (error instanceof TypeError) {
apiLogger.logError('DELETE', `${BASE_URL}/item-master/surface-treatments/${id}`, error, undefined, startTime);
throw new Error('네트워크 연결을 확인해주세요. 서버에 연결할 수 없습니다.');
}
apiLogger.logError('DELETE', `${BASE_URL}/item-master/surface-treatments/${id}`, error as Error, undefined, startTime);
throw error;
}
},
},
};

View File

@@ -376,9 +376,9 @@ export const transformUnitOptionResponse = (
): { id: string; value: string; label: string; isActive: boolean } => {
return {
id: response.id.toString(), // number → string 변환
value: response.value,
label: response.label,
isActive: true, // API에 없으므로 기본값
value: response.unit_code,
label: response.unit_name,
isActive: response.is_active,
};
};