Files
sam-react-prod/claudedocs/_ITEM_MASTER_API_STRUCTURE.md
byeongcheolryu df3db155dd [feat]: Item Master 데이터 관리 기능 구현 및 타입 에러 수정
- ItemMasterDataManagement 컴포넌트 구조화 (tabs, dialogs, components 분리)
- HierarchyTab 타입 에러 수정 (BOMItem section_id, updated_at 추가)
- API 클라이언트 구현 (item-master.ts, 13개 엔드포인트)
- ItemMasterContext 구현 (상태 관리 및 데이터 흐름)
- 백엔드 요구사항 문서 작성 (CORS 설정, API 스펙 등)
- SSR 호환성 수정 (navigator API typeof window 체크)
- 미사용 변수 ESLint 에러 해결
- Context 리팩토링 (AuthContext, RootProvider 추가)
- API 유틸리티 추가 (error-handler, logger, transformers)

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-23 16:10:27 +09:00

1388 lines
31 KiB
Markdown

# 품목기준관리 API 구조
## 목차
1. [최초 화면 접근 시 필요한 API](#최초-화면-접근-시-필요한-api)
2. [각 항목별 저장 API](#각-항목별-저장-api)
3. [API 응답 구조](#api-응답-구조)
4. [에러 처리](#에러-처리)
---
## 최초 화면 접근 시 필요한 API
페이지 로드 시 `tenant.id` 기준으로 모든 기준정보를 조회합니다.
### 1. 전체 설정 조회 (통합 API)
**엔드포인트**: `GET /api/tenants/{tenantId}/item-master-config`
**목적**: 모든 기준정보를 한 번에 조회 (성능 최적화)
**Next.js 프록시**: `/api/tenants/[tenantId]/item-master-config/route.ts`
**요청 예시**:
```http
GET /api/tenants/282/item-master-config HTTP/1.1
Authorization: Bearer {access_token}
X-API-KEY: {api_key}
```
**PHP 백엔드 엔드포인트**:
```
GET /api/v1/tenants/282/item-master-config
```
**응답 구조**:
```json
{
"success": true,
"data": {
"tenantId": 282,
"itemMasters": [],
"specificationMasters": [...],
"materialItemNames": [...],
"itemCategories": [...],
"itemUnits": [...],
"itemMaterials": [...],
"surfaceTreatments": [...],
"partTypeOptions": [...],
"partUsageOptions": [...],
"guideRailOptions": [...],
"itemMasterFields": [...],
"itemPages": [...]
}
}
```
### 2. 개별 조회 API (선택적 사용)
필요 시 개별 항목만 조회할 수 있습니다.
#### 2.1 규격 마스터
```http
GET /api/tenants/{tenantId}/item-master-config/specification-masters
```
#### 2.2 자재 품목명
```http
GET /api/tenants/{tenantId}/item-master-config/material-item-names
```
#### 2.3 품목 카테고리
```http
GET /api/tenants/{tenantId}/item-master-config/item-categories
```
#### 2.4 품목 단위
```http
GET /api/tenants/{tenantId}/item-master-config/item-units
```
#### 2.5 품목 재질
```http
GET /api/tenants/{tenantId}/item-master-config/item-materials
```
#### 2.6 표면처리
```http
GET /api/tenants/{tenantId}/item-master-config/surface-treatments
```
#### 2.7 부품 타입 옵션
```http
GET /api/tenants/{tenantId}/item-master-config/part-type-options
```
#### 2.8 부품 용도 옵션
```http
GET /api/tenants/{tenantId}/item-master-config/part-usage-options
```
#### 2.9 가이드레일 옵션
```http
GET /api/tenants/{tenantId}/item-master-config/guide-rail-options
```
#### 2.10 품목 마스터 필드
```http
GET /api/tenants/{tenantId}/item-master-config/item-master-fields
```
#### 2.11 품목 페이지
```http
GET /api/tenants/{tenantId}/item-master-config/item-pages
```
---
## 각 항목별 저장 API
각 섹션의 저장 버튼 클릭 시 호출되는 API입니다.
### 1. 규격 마스터 (Specification Master)
#### 등록
```http
POST /api/tenants/{tenantId}/item-master-config/specification-masters
Content-Type: application/json
{
"specificationCode": "1219*1200",
"itemType": "RM",
"itemName": "SPHC-SD",
"fieldCount": "1",
"thickness": "1.6",
"widthA": "1219",
"length": "1200",
"isActive": true
}
```
#### 수정
```http
PUT /api/tenants/{tenantId}/item-master-config/specification-masters/{specId}
Content-Type: application/json
{
"specificationCode": "1219*1200",
"thickness": "1.8",
"isActive": true
}
```
#### 삭제
```http
DELETE /api/tenants/{tenantId}/item-master-config/specification-masters/{specId}
```
#### 일괄 저장 (추천)
```http
POST /api/tenants/{tenantId}/item-master-config/specification-masters/bulk
Content-Type: application/json
{
"items": [
{ "specificationCode": "1219*1200", ... },
{ "specificationCode": "1219*2438", ... }
]
}
```
### 2. 자재 품목명 (Material Item Name)
#### 등록
```http
POST /api/tenants/{tenantId}/item-master-config/material-item-names
Content-Type: application/json
{
"itemType": "RM",
"itemName": "SPHC-SD",
"category": "냉연강판",
"description": "일반냉연강판",
"isActive": true
}
```
#### 수정
```http
PUT /api/tenants/{tenantId}/item-master-config/material-item-names/{materialId}
```
#### 삭제
```http
DELETE /api/tenants/{tenantId}/item-master-config/material-item-names/{materialId}
```
#### 일괄 저장
```http
POST /api/tenants/{tenantId}/item-master-config/material-item-names/bulk
```
### 3. 품목 카테고리 (Item Category)
#### 등록
```http
POST /api/tenants/{tenantId}/item-master-config/item-categories
Content-Type: application/json
{
"categoryType": "PRODUCT",
"category1": "스크린",
"code": "SC",
"description": "스크린 셔터",
"isActive": true
}
```
#### 수정
```http
PUT /api/tenants/{tenantId}/item-master-config/item-categories/{categoryId}
```
#### 삭제
```http
DELETE /api/tenants/{tenantId}/item-master-config/item-categories/{categoryId}
```
### 4. 품목 단위 (Item Unit)
#### 등록
```http
POST /api/tenants/{tenantId}/item-master-config/item-units
Content-Type: application/json
{
"unitCode": "EA",
"unitName": "EA (개)",
"description": "낱개 단위",
"isActive": true
}
```
#### 수정
```http
PUT /api/tenants/{tenantId}/item-master-config/item-units/{unitId}
```
#### 삭제
```http
DELETE /api/tenants/{tenantId}/item-master-config/item-units/{unitId}
```
### 5. 품목 재질 (Item Material)
#### 등록
```http
POST /api/tenants/{tenantId}/item-master-config/item-materials
Content-Type: application/json
{
"materialCode": "EGI-1.2T",
"materialName": "EGI 1.2T",
"materialType": "STEEL",
"thickness": "1.2T",
"description": "전기아연도금강판",
"isActive": true
}
```
#### 수정
```http
PUT /api/tenants/{tenantId}/item-master-config/item-materials/{materialId}
```
#### 삭제
```http
DELETE /api/tenants/{tenantId}/item-master-config/item-materials/{materialId}
```
### 6. 표면처리 (Surface Treatment)
#### 등록
```http
POST /api/tenants/{tenantId}/item-master-config/surface-treatments
Content-Type: application/json
{
"treatmentCode": "POWDER",
"treatmentName": "파우더도장",
"treatmentType": "PAINTING",
"description": "분체도장",
"isActive": true
}
```
#### 수정
```http
PUT /api/tenants/{tenantId}/item-master-config/surface-treatments/{treatmentId}
```
#### 삭제
```http
DELETE /api/tenants/{tenantId}/item-master-config/surface-treatments/{treatmentId}
```
### 7. 부품 타입 옵션 (Part Type Option)
#### 등록
```http
POST /api/tenants/{tenantId}/item-master-config/part-type-options
Content-Type: application/json
{
"partType": "ASSEMBLY",
"optionCode": "ASSY",
"optionName": "조립품",
"description": "여러 부품을 조립하는 부품",
"isActive": true
}
```
#### 수정
```http
PUT /api/tenants/{tenantId}/item-master-config/part-type-options/{optionId}
```
#### 삭제
```http
DELETE /api/tenants/{tenantId}/item-master-config/part-type-options/{optionId}
```
### 8. 부품 용도 옵션 (Part Usage Option)
#### 등록
```http
POST /api/tenants/{tenantId}/item-master-config/part-usage-options
Content-Type: application/json
{
"usageCode": "GUIDE_RAIL",
"usageName": "가이드레일",
"description": "가이드레일용 부품",
"isActive": true
}
```
#### 수정
```http
PUT /api/tenants/{tenantId}/item-master-config/part-usage-options/{optionId}
```
#### 삭제
```http
DELETE /api/tenants/{tenantId}/item-master-config/part-usage-options/{optionId}
```
### 9. 가이드레일 옵션 (Guide Rail Option)
#### 등록
```http
POST /api/tenants/{tenantId}/item-master-config/guide-rail-options
Content-Type: application/json
{
"optionType": "MODEL_TYPE",
"optionCode": "SCREEN",
"optionName": "스크린용",
"description": "스크린 셔터용 가이드레일",
"parentOption": null,
"isActive": true
}
```
#### 수정
```http
PUT /api/tenants/{tenantId}/item-master-config/guide-rail-options/{optionId}
```
#### 삭제
```http
DELETE /api/tenants/{tenantId}/item-master-config/guide-rail-options/{optionId}
```
### 10. 품목 마스터 필드 (Item Master Field)
#### 등록
```http
POST /api/tenants/{tenantId}/item-master-config/item-master-fields
Content-Type: application/json
{
"name": "품목코드",
"fieldKey": "itemCode",
"property": {
"inputType": "textbox",
"required": true,
"row": 1,
"col": 1
},
"category": "공통",
"description": "품목의 고유 코드",
"isActive": true
}
```
#### 수정
```http
PUT /api/tenants/{tenantId}/item-master-config/item-master-fields/{fieldId}
```
#### 삭제
```http
DELETE /api/tenants/{tenantId}/item-master-config/item-master-fields/{fieldId}
```
### 11. 품목 페이지 (Item Page)
#### 페이지 조회
```http
GET /api/tenants/{tenantId}/item-master-config/pages/{pageId}
```
**Next.js 프록시**: `/api/tenants/[tenantId]/item-master-config/pages/[pageId]/route.ts`
#### 페이지 생성
```http
POST /api/tenants/{tenantId}/item-master-config/pages
Content-Type: application/json
{
"pageName": "품목 등록",
"itemType": "FG",
"isActive": true,
"sections": [
{
"title": "기본정보",
"order": 1,
"isCollapsible": true,
"isCollapsed": false,
"fields": [
{
"name": "품목코드",
"fieldKey": "itemCode",
"property": { "inputType": "textbox", "required": true, "row": 1, "col": 1 }
}
]
}
]
}
```
#### 페이지 수정
```http
PUT /api/tenants/{tenantId}/item-master-config/pages/{pageId}
Content-Type: application/json
{
"pageName": "품목 등록 (수정)",
"isActive": true,
"sections": [...]
}
```
**Next.js 프록시**: `/api/tenants/[tenantId]/item-master-config/pages/[pageId]/route.ts`
#### 페이지 삭제
```http
DELETE /api/tenants/{tenantId}/item-master-config/pages/{pageId}
```
**Next.js 프록시**: `/api/tenants/[tenantId]/item-master-config/pages/[pageId]/route.ts`
---
## API 응답 샘플 데이터
아래 샘플 데이터는 프론트엔드에서 주석 처리한 Mock 데이터이며, **백엔드 API 응답의 실제 구조 및 데이터 예시**입니다.
### 1. 규격 마스터 (Specification Masters)
**샘플 데이터** (27개 중 일부):
```json
[
{
"id": "spec-001",
"specificationCode": "1219*1200",
"itemType": "RM",
"itemName": "SPHC-SD",
"fieldCount": "1",
"thickness": "1.6",
"widthA": "1219",
"length": "1200",
"isActive": true,
"createdAt": "2025-01-19T10:00:00Z"
},
{
"id": "spec-002",
"specificationCode": "1219*2438",
"itemType": "RM",
"itemName": "SPHC-SD",
"fieldCount": "1",
"thickness": "1.6",
"widthA": "1219",
"length": "2438",
"isActive": true,
"createdAt": "2025-01-19T10:00:00Z"
},
{
"id": "spec-022",
"specificationCode": "1219*1200",
"itemType": "RM",
"itemName": "STS304",
"fieldCount": "1",
"thickness": "1.5",
"widthA": "1219",
"length": "1200",
"isActive": true,
"createdAt": "2025-01-19T10:00:00Z"
}
]
```
**데이터 설명**:
- 총 27개 규격: SPHC-SD, SPCC-SD, SECC, SGCC, SPHD, SS330, SS400, STS304, STS430
- 각 자재별로 3가지 길이 (1200, 2438, 3000mm) 규격 존재
### 2. 자재 품목명 (Material Item Names)
**샘플 데이터** (139개 중 일부):
```json
[
{
"id": "mat-001",
"itemType": "RM",
"itemName": "SPHC-SD",
"category": "냉연강판",
"description": "일반냉연강판",
"isActive": true,
"createdAt": "2025-01-19T10:00:00Z"
},
{
"id": "mat-008",
"itemType": "RM",
"itemName": "STS304",
"category": "스테인리스",
"description": "스테인리스강판 304",
"isActive": true,
"createdAt": "2025-01-19T10:00:00Z"
},
{
"id": "mat-101",
"itemType": "SM",
"itemName": "육각볼트",
"category": "체결부품",
"description": "육각머리볼트",
"isActive": true,
"createdAt": "2025-01-19T10:00:00Z"
},
{
"id": "mat-201",
"itemType": "SM",
"itemName": "용접봉",
"category": "용접재료",
"description": "용접봉",
"isActive": true,
"createdAt": "2025-01-19T10:00:00Z"
},
{
"id": "mat-301",
"itemType": "SM",
"itemName": "도료(백색)",
"category": "도장재료",
"description": "백색도료",
"isActive": true,
"createdAt": "2025-01-19T10:00:00Z"
}
]
```
**데이터 분류**:
- **원자재 (RM)**: 21개 (냉연강판, 스테인리스, 알루미늄, 파이프류, 형강류, 비철금속)
- **부자재 (SM)**: 118개
- 체결부품 (mat-101~111): 볼트, 너트, 와셔, 나사 등
- 용접/접착 (mat-201~207): 용접봉, 실리콘, 에폭시 등
- 도장/표면처리 (mat-301~306): 도료, 프라이머 등
- 연마/연삭 (mat-401~404): 연마지, 절단석 등
- 기타 부자재 (mat-501~539): 패킹, 베어링, 모터, 샤프트 등
### 3. 품목 카테고리 (Item Categories)
**샘플 데이터** (14개):
```json
[
{
"id": "CAT-001",
"categoryType": "PRODUCT",
"category1": "스크린",
"code": "SC",
"description": "스크린 셔터",
"isActive": true,
"createdAt": "2025-01-01"
},
{
"id": "CAT-101",
"categoryType": "PART",
"category1": "가이드레일",
"code": "GR",
"description": "가이드레일 부품",
"isActive": true,
"createdAt": "2025-01-01"
},
{
"id": "CAT-201",
"categoryType": "MATERIAL",
"category1": "강판",
"code": "SP",
"description": "강판 원자재",
"isActive": true,
"createdAt": "2025-01-01"
},
{
"id": "CAT-301",
"categoryType": "SUB_MATERIAL",
"category1": "볼트너트",
"code": "BN",
"description": "볼트/너트",
"isActive": true,
"createdAt": "2025-01-01"
}
]
```
**카테고리 타입 분류**:
- **PRODUCT**: 완제품 (스크린, 철재문, 블라인드)
- **PART**: 부품 (가이드레일, 하단마감재, 케이스, 도어, 브라켓)
- **MATERIAL**: 원자재 (강판, 알루미늄, 플라스틱)
- **SUB_MATERIAL**: 부자재 (볼트너트, 나사, 페인트)
### 4. 품목 단위 (Item Units)
**샘플 데이터** (7개):
```json
[
{
"id": "UNIT-001",
"unitCode": "EA",
"unitName": "EA (개)",
"description": "낱개 단위",
"isActive": true,
"createdAt": "2025-01-01"
},
{
"id": "UNIT-002",
"unitCode": "SET",
"unitName": "SET (세트)",
"description": "세트 단위",
"isActive": true,
"createdAt": "2025-01-01"
},
{
"id": "UNIT-003",
"unitCode": "M",
"unitName": "M (미터)",
"description": "길이 단위",
"isActive": true,
"createdAt": "2025-01-01"
},
{
"id": "UNIT-004",
"unitCode": "KG",
"unitName": "KG (킬로그램)",
"description": "무게 단위",
"isActive": true,
"createdAt": "2025-01-01"
},
{
"id": "UNIT-006",
"unitCode": "M2",
"unitName": "㎡ (제곱미터)",
"description": "면적 단위",
"isActive": true,
"createdAt": "2025-01-01"
},
{
"id": "UNIT-007",
"unitCode": "BOX",
"unitName": "BOX (박스)",
"description": "박스 단위",
"isActive": true,
"createdAt": "2025-01-01"
}
]
```
### 5. 품목 재질 (Item Materials)
**샘플 데이터** (7개):
```json
[
{
"id": "MAT-001",
"materialCode": "EGI-1.2T",
"materialName": "EGI 1.2T",
"materialType": "STEEL",
"thickness": "1.2T",
"description": "전기아연도금강판",
"isActive": true,
"createdAt": "2025-01-01"
},
{
"id": "MAT-002",
"materialCode": "EGI-1.55T",
"materialName": "EGI 1.55T",
"materialType": "STEEL",
"thickness": "1.55T",
"description": "전기아연도금강판",
"isActive": true,
"createdAt": "2025-01-01"
},
{
"id": "MAT-004",
"materialCode": "SUS-1.2T",
"materialName": "SUS 1.2T",
"materialType": "STEEL",
"thickness": "1.2T",
"description": "스테인리스 강판",
"isActive": true,
"createdAt": "2025-01-01"
},
{
"id": "MAT-006",
"materialCode": "AL-6063",
"materialName": "알루미늄 6063",
"materialType": "ALUMINUM",
"description": "알루미늄 압출재",
"isActive": true,
"createdAt": "2025-01-01"
}
]
```
**재질 타입**:
- **STEEL**: EGI (전기아연도금강판), SUS (스테인리스)
- **ALUMINUM**: AL-6063, AL-6061
### 6. 표면처리 (Surface Treatments)
**샘플 데이터** (5개):
```json
[
{
"id": "TREAT-001",
"treatmentCode": "NONE",
"treatmentName": "무도장",
"treatmentType": "NONE",
"description": "표면처리 없음",
"isActive": true,
"createdAt": "2025-01-01"
},
{
"id": "TREAT-002",
"treatmentCode": "POWDER",
"treatmentName": "파우더도장",
"treatmentType": "PAINTING",
"description": "분체도장",
"isActive": true,
"createdAt": "2025-01-01"
},
{
"id": "TREAT-003",
"treatmentCode": "ANODIZING",
"treatmentName": "아노다이징",
"treatmentType": "COATING",
"description": "알루미늄 양극산화처리",
"isActive": true,
"createdAt": "2025-01-01"
},
{
"id": "TREAT-004",
"treatmentCode": "ZINC",
"treatmentName": "아연도금",
"treatmentType": "PLATING",
"description": "아연 도금처리",
"isActive": true,
"createdAt": "2025-01-01"
}
]
```
**처리 타입**:
- **NONE**: 무도장
- **PAINTING**: 도장 (파우더도장)
- **COATING**: 코팅 (아노다이징)
- **PLATING**: 도금 (아연도금, 크롬도금)
### 7. 부품 타입 옵션 (Part Type Options)
**샘플 데이터** (3개):
```json
[
{
"id": "PTYPE-001",
"partType": "ASSEMBLY",
"optionCode": "ASSY",
"optionName": "조립품",
"description": "여러 부품을 조립하는 부품",
"isActive": true,
"createdAt": "2025-01-01"
},
{
"id": "PTYPE-002",
"partType": "BENDING",
"optionCode": "BEND",
"optionName": "절곡품",
"description": "절곡 가공 부품",
"isActive": true,
"createdAt": "2025-01-01"
},
{
"id": "PTYPE-003",
"partType": "PURCHASED",
"optionCode": "PURCH",
"optionName": "구매품",
"description": "외부에서 구매하는 부품",
"isActive": true,
"createdAt": "2025-01-01"
}
]
```
### 8. 부품 용도 옵션 (Part Usage Options)
**샘플 데이터** (6개):
```json
[
{
"id": "USAGE-001",
"usageCode": "GUIDE_RAIL",
"usageName": "가이드레일",
"description": "가이드레일용 부품",
"isActive": true,
"createdAt": "2025-01-01"
},
{
"id": "USAGE-002",
"usageCode": "BOTTOM_FINISH",
"usageName": "하단마감재",
"description": "하단마감재용 부품",
"isActive": true,
"createdAt": "2025-01-01"
},
{
"id": "USAGE-003",
"usageCode": "CASE",
"usageName": "케이스",
"description": "케이스용 부품",
"isActive": true,
"createdAt": "2025-01-01"
},
{
"id": "USAGE-004",
"usageCode": "DOOR",
"usageName": "도어",
"description": "도어용 부품",
"isActive": true,
"createdAt": "2025-01-01"
}
]
```
### 9. 가이드레일 옵션 (Guide Rail Options)
**샘플 데이터** (13개 중 일부):
```json
[
{
"id": "GR-001",
"optionType": "MODEL_TYPE",
"optionCode": "SCREEN",
"optionName": "스크린용",
"description": "스크린 셔터용 가이드레일",
"isActive": true,
"createdAt": "2025-01-01"
},
{
"id": "GR-011",
"optionType": "MODEL",
"optionCode": "T40",
"optionName": "T40",
"parentOption": "SCREEN",
"description": "T40 모델",
"isActive": true,
"createdAt": "2025-01-01"
},
{
"id": "GR-021",
"optionType": "CERTIFICATION",
"optionCode": "KFI",
"optionName": "KFI인증",
"description": "KFI 인증",
"isActive": true,
"createdAt": "2025-01-01"
},
{
"id": "GR-031",
"optionType": "SHAPE",
"optionCode": "ROUND",
"optionName": "R형",
"description": "라운드형",
"isActive": true,
"createdAt": "2025-01-01"
},
{
"id": "GR-041",
"optionType": "FINISH",
"optionCode": "POWDER",
"optionName": "파우더도장",
"description": "파우더 도장 마감",
"isActive": true,
"createdAt": "2025-01-01"
},
{
"id": "GR-051",
"optionType": "LENGTH",
"optionCode": "3000",
"optionName": "3000mm",
"description": "3미터",
"isActive": true,
"createdAt": "2025-01-01"
}
]
```
**옵션 타입 분류**:
- **MODEL_TYPE**: 제품 유형 (스크린용, 철재용)
- **MODEL**: 모델명 (T40, T60, 프리미엄) - `parentOption` 참조
- **CERTIFICATION**: 인증 (KFI인증, 미인증)
- **SHAPE**: 형상 (R형, ㄱ형)
- **FINISH**: 마감 (파우더도장, 아노다이징)
- **LENGTH**: 길이 (3000mm, 4000mm, 5000mm)
### 10. 품목 마스터 필드 (Item Master Fields)
**샘플 데이터** (5개):
```json
[
{
"id": "MASTER-001",
"name": "품목코드",
"fieldKey": "itemCode",
"property": {
"inputType": "textbox",
"required": true,
"row": 1,
"col": 1
},
"category": "공통",
"description": "품목의 고유 코드",
"isActive": true,
"createdAt": "2025-01-01"
},
{
"id": "MASTER-002",
"name": "품목명",
"fieldKey": "itemName",
"property": {
"inputType": "textbox",
"required": true,
"row": 1,
"col": 1
},
"category": "공통",
"description": "품목의 이름",
"isActive": true,
"createdAt": "2025-01-01"
},
{
"id": "MASTER-003",
"name": "단위",
"fieldKey": "unit",
"property": {
"inputType": "dropdown",
"required": true,
"row": 1,
"col": 1,
"options": ["EA", "SET", "KG", "M", "BOX"]
},
"category": "공통",
"description": "품목의 단위",
"isActive": true,
"createdAt": "2025-01-01"
},
{
"id": "MASTER-004",
"name": "재질",
"fieldKey": "material",
"property": {
"inputType": "dropdown",
"required": false,
"row": 1,
"col": 1,
"options": ["EGI 1.2T", "SUS 1.2T", "AL 1.5T"]
},
"category": "부품",
"description": "부품의 재질",
"isActive": true,
"createdAt": "2025-01-01"
}
]
```
**inputType 종류**:
- `textbox`: 텍스트 입력
- `dropdown`: 드롭다운 선택 (options 필수)
### 11. 품목 페이지 (Item Pages)
**샘플 구조** (실제 Mock 데이터는 빈 배열):
```json
[
{
"id": "PAGE-001",
"pageName": "제품 등록",
"itemType": "FG",
"isActive": true,
"createdAt": "2025-01-01",
"sections": [
{
"id": "SECTION-001",
"title": "기본정보",
"order": 1,
"isCollapsible": true,
"isCollapsed": false,
"fields": [
{
"name": "품목코드",
"fieldKey": "itemCode",
"property": {
"inputType": "textbox",
"required": true,
"row": 1,
"col": 1
}
},
{
"name": "품목명",
"fieldKey": "itemName",
"property": {
"inputType": "textbox",
"required": true,
"row": 1,
"col": 2
}
}
]
}
]
}
]
```
### 전체 설정 조회 API 응답 예시
`GET /api/tenants/282/item-master-config` 응답:
```json
{
"success": true,
"data": {
"tenantId": 282,
"itemMasters": [],
"specificationMasters": [
/* 위의 27 규격 마스터 데이터 */
],
"materialItemNames": [
/* 위의 139 자재 품목명 데이터 */
],
"itemCategories": [
/* 위의 14 품목 카테고리 데이터 */
],
"itemUnits": [
/* 위의 7 품목 단위 데이터 */
],
"itemMaterials": [
/* 위의 7 품목 재질 데이터 */
],
"surfaceTreatments": [
/* 위의 5 표면처리 데이터 */
],
"partTypeOptions": [
/* 위의 3 부품 타입 옵션 데이터 */
],
"partUsageOptions": [
/* 위의 6 부품 용도 옵션 데이터 */
],
"guideRailOptions": [
/* 위의 13 가이드레일 옵션 데이터 */
],
"itemMasterFields": [
/* 위의 5 품목 마스터 필드 데이터 */
],
"itemPages": []
}
}
```
**주의사항**:
- `id` 필드는 백엔드에서 자동 생성 (UUID 또는 Auto Increment)
- `createdAt`, `updatedAt` 필드는 백엔드에서 자동 관리
- Mock 데이터의 날짜는 예시용이며, 실제로는 백엔드에서 생성된 timestamp 사용
---
## API 응답 구조
### 성공 응답
```json
{
"success": true,
"data": {
"id": "spec-001",
"specificationCode": "1219*1200",
"itemType": "RM",
"itemName": "SPHC-SD",
"createdAt": "2025-01-19T10:00:00Z",
"updatedAt": "2025-01-19T10:00:00Z"
}
}
```
### 에러 응답
```json
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "필수 필드가 누락되었습니다.",
"details": {
"field": "itemName",
"reason": "required"
}
}
}
```
### 일괄 저장 응답
```json
{
"success": true,
"data": {
"total": 10,
"created": 8,
"updated": 2,
"failed": 0,
"items": [...]
}
}
```
---
## 에러 처리
### HTTP 상태 코드
| 코드 | 의미 | 설명 |
|------|------|------|
| 200 | OK | 성공 |
| 201 | Created | 생성 성공 |
| 400 | Bad Request | 잘못된 요청 |
| 401 | Unauthorized | 인증 실패 |
| 403 | Forbidden | 권한 없음 (테넌트 불일치) |
| 404 | Not Found | 리소스 없음 |
| 409 | Conflict | 중복 데이터 |
| 500 | Internal Server Error | 서버 오류 |
### 에러 코드
| 에러 코드 | 설명 |
|----------|------|
| `VALIDATION_ERROR` | 유효성 검사 실패 |
| `UNAUTHORIZED` | 인증 필요 |
| `FORBIDDEN` | 권한 없음 |
| `NOT_FOUND` | 리소스 없음 |
| `DUPLICATE` | 중복 데이터 |
| `SERVER_ERROR` | 서버 오류 |
---
## Next.js API Routes 구조
### 기존 구현 완료
1.`/api/tenants/[tenantId]/item-master-config/route.ts`
- GET: 전체 설정 조회
- POST: 전체 설정 생성
- PUT: 전체 설정 수정
2.`/api/tenants/[tenantId]/item-master-config/pages/[pageId]/route.ts`
- GET: 특정 페이지 조회
- PUT: 특정 페이지 수정
- DELETE: 특정 페이지 삭제
### 추가 필요 (선택사항)
개별 항목 CRUD가 필요한 경우 추가 라우트 생성:
```
/api/tenants/[tenantId]/item-master-config/
├── specification-masters/
├── material-item-names/
├── item-categories/
├── item-units/
├── item-materials/
├── surface-treatments/
├── part-type-options/
├── part-usage-options/
├── guide-rail-options/
├── item-master-fields/
└── pages/
└── [pageId]/
```
---
## 캐싱 전략
### TenantAwareCache 사용
```typescript
// ItemMasterContext.tsx에서 구현됨
cache.set('itemMasters', data, 3600000); // 1시간 TTL
cache.set('specificationMasters', data, 3600000);
// ... 기타 항목들
```
### 캐시 키 구조
```
mes-{tenantId}-itemMasters
mes-{tenantId}-specificationMasters
mes-{tenantId}-materialItemNames
mes-{tenantId}-itemCategories
mes-{tenantId}-itemUnits
mes-{tenantId}-itemMaterials
mes-{tenantId}-surfaceTreatments
mes-{tenantId}-partTypeOptions
mes-{tenantId}-partUsageOptions
mes-{tenantId}-guideRailOptions
mes-{tenantId}-itemMasterFields
mes-{tenantId}-itemPages
```
### 캐시 무효화
- 테넌트 전환 시: 이전 테넌트 캐시 자동 삭제
- 로그아웃 시: 현재 테넌트 캐시 삭제
- 데이터 저장 시: 해당 항목 캐시 무효화
---
## 프론트엔드 구현 체크리스트
### ItemMasterContext.tsx
- [ ] Mock 데이터 주석 처리
- [ ] API 호출 함수 구현
- [ ] TenantAwareCache 연동
- [ ] 에러 처리 추가
- [ ] 로딩 상태 관리
### ItemMasterDataManagement.tsx
- [ ] 각 섹션별 저장 버튼 이벤트
- [ ] API 호출 로직
- [ ] 성공/실패 토스트 메시지
- [ ] 낙관적 업데이트 (Optimistic UI)
- [ ] 에러 핸들링
### 백엔드 요구사항
#### 필수 구현
- [ ] PHP `/api/v1/tenants/{tenantId}/item-master-config` 엔드포인트 구현
- [ ] 테넌트 검증 로직 (인증된 사용자의 tenant.id와 URL의 tenantId 일치 확인)
- [ ] 데이터베이스 테이블 생성
- [ ] 트랜잭션 처리
- [ ] 에러 응답 표준화
#### DB 초기 데이터 등록 (Seeding)
**중요**: 모든 데이터는 API로 제공되므로, DB에 초기 데이터 등록이 필수입니다.
**시스템 기본값** (모든 테넌트 공통 또는 테넌트 생성 시 자동 복사):
1. **품목 단위** (7개)
- EA, SET, M, KG, L, M2, BOX
- 우선순위: 🔴 높음 (필수)
2. **품목 재질** (7개)
- EGI-1.2T, EGI-1.55T, EGI-2.0T, SUS-1.2T, SUS-1.5T, AL-6063, AL-6061
- 우선순위: 🔴 높음 (필수)
3. **표면처리** (5개)
- 무도장, 파우더도장, 아노다이징, 아연도금, 크롬도금
- 우선순위: 🔴 높음 (필수)
4. **품목 카테고리** (14개)
- PRODUCT, PART, MATERIAL, SUB_MATERIAL 타입별 카테고리
- 우선순위: 🟡 중간
5. **부품 타입 옵션** (3개)
- 조립품, 절곡품, 구매품
- 우선순위: 🟡 중간
6. **부품 용도 옵션** (6개)
- 가이드레일, 하단마감재, 케이스, 도어, 브라켓, 일반
- 우선순위: 🟡 중간
7. **품목 마스터 필드** (5개)
- 품목코드, 품목명, 단위, 재질, 표면처리
- 우선순위: 🟡 중간
**테넌트별 데이터** (선택적, 빈 배열로 시작 가능):
8. **규격 마스터** (27개 샘플)
- 우선순위: 🟢 낮음 (테스트/샘플 데이터)
9. **자재 품목명** (139개 샘플)
- 우선순위: 🟢 낮음 (테스트/샘플 데이터)
10. **가이드레일 옵션** (13개 샘플)
- 우선순위: 🟢 낮음 (테스트/샘플 데이트)
**초기 데이터 등록 방법**:
```sql
-- 예시: 품목 단위 초기 데이터
INSERT INTO item_units (tenant_id, unitCode, unitName, description, isActive) VALUES
(NULL, 'EA', 'EA (개)', '낱개 단위', true), -- tenant_id NULL = 전체 공통
(NULL, 'SET', 'SET (세트)', '세트 단위', true),
(NULL, 'M', 'M (미터)', '길이 단위', true),
(NULL, 'KG', 'KG (킬로그램)', '무게 단위', true),
(NULL, 'L', 'L (리터)', '부피 단위', true),
(NULL, 'M2', '㎡ (제곱미터)', '면적 단위', true),
(NULL, 'BOX', 'BOX (박스)', '박스 단위', true);
-- 또는 테넌트 생성 시 자동 복사
CREATE TRIGGER copy_default_data_on_tenant_create
AFTER INSERT ON tenants
FOR EACH ROW
BEGIN
-- 기본값 자동 복사 로직
END;
```
**API 응답 예시**:
```json
{
"success": true,
"data": {
"tenantId": 282,
"itemUnits": [
{
"id": "UNIT-001",
"unitCode": "EA",
"unitName": "EA (개)",
"description": "낱개 단위",
"isActive": true
}
// ... 6개 더
],
"itemMaterials": [/* 7 기본 재질 */],
"surfaceTreatments": [/* 5 기본 표면처리 */],
"itemCategories": [/* 14 */],
"partTypeOptions": [/* 3 */],
"partUsageOptions": [/* 6 */],
"itemMasterFields": [/* 5 */],
"specificationMasters": [], // 빈 배열 (테넌트별 데이터)
"materialItemNames": [],
"guideRailOptions": [],
"itemPages": []
}
}
```
**참고**: 위의 "API 응답 샘플 데이터" 섹션에 모든 초기 데이터의 상세 구조가 포함되어 있습니다.