# 품목기준관리(ItemMaster) API 가이드 > 품목 입력 화면을 구성하는 **페이지-섹션-필드** 구조 관리 시스템 --- ## 1. 개요 ### 1.1 핵심 개념 | 엔티티 | 테이블 | 설명 | |--------|--------|------| | **Page** | `item_pages` | 품목 유형별 화면 (FG, PT, SM, RM, CS) | | **Section** | `item_sections` | 페이지 내 논리적 영역 | | **Field** | `item_fields` | 섹션 내 입력 항목 | | **BomItem** | `item_bom_items` | BOM 섹션 내 부품 항목 | | **CustomTab** | `custom_tabs` | 커스텀 탭 설정 | | **UnitOption** | `unit_options` | 단위 옵션 | ### 1.2 아키텍처 - **독립 엔티티 구조**: 섹션, 필드, BOM은 독립적으로 존재하며 재사용 가능 - **링크 테이블**: `entity_relationships`로 관계 관리 - **연결 잠금**: 중요한 구조는 잠금으로 보호 가능 ``` ItemPage (item_type: FG, PT, SM, RM, CS) │ │ entity_relationships (is_locked) ▼ ItemSection (type: default, bom, custom) │ ├─ entity_relationships → ItemField └─ entity_relationships → ItemBomItem ``` --- ## 2. API 엔드포인트 ### 2.1 초기화 | Method | Endpoint | 설명 | |--------|----------|------| | GET | `/api/v1/item-master/init` | 전체 데이터 로드 | **응답 구조:** ```json { "pages": [], // 페이지 + 연결된 섹션/필드 "sections": [], // 모든 독립 섹션 "fields": [], // 모든 독립 필드 "customTabs": [], // 커스텀 탭 "unitOptions": [] // 단위 옵션 } ``` ### 2.2 페이지 | Method | Endpoint | 설명 | |--------|----------|------| | GET | `/api/v1/item-master/pages` | 페이지 목록 | | POST | `/api/v1/item-master/pages` | 페이지 생성 | | PUT | `/api/v1/item-master/pages/{id}` | 페이지 수정 | | DELETE | `/api/v1/item-master/pages/{id}` | 페이지 삭제 | ### 2.3 섹션 | Method | Endpoint | 설명 | |--------|----------|------| | GET | `/api/v1/item-master/sections` | 독립 섹션 목록 | | POST | `/api/v1/item-master/sections` | 독립 섹션 생성 | | POST | `/api/v1/item-master/pages/{pageId}/sections` | 섹션 생성 + 페이지 연결 | | PUT | `/api/v1/item-master/sections/{id}` | 섹션 수정 | | DELETE | `/api/v1/item-master/sections/{id}` | 섹션 삭제 | | POST | `/api/v1/item-master/sections/{id}/clone` | 섹션 복제 | | GET | `/api/v1/item-master/sections/{id}/usage` | 사용처 조회 | | PUT | `/api/v1/item-master/pages/{pageId}/sections/reorder` | 섹션 순서 변경 | ### 2.4 필드 | Method | Endpoint | 설명 | |--------|----------|------| | GET | `/api/v1/item-master/fields` | 독립 필드 목록 | | POST | `/api/v1/item-master/fields` | 독립 필드 생성 | | POST | `/api/v1/item-master/sections/{sectionId}/fields` | 필드 생성 + 섹션 연결 | | PUT | `/api/v1/item-master/fields/{id}` | 필드 수정 | | DELETE | `/api/v1/item-master/fields/{id}` | 필드 삭제 | | POST | `/api/v1/item-master/fields/{id}/clone` | 필드 복제 | | GET | `/api/v1/item-master/fields/{id}/usage` | 사용처 조회 | | PUT | `/api/v1/item-master/sections/{sectionId}/fields/reorder` | 필드 순서 변경 | ### 2.5 BOM | Method | Endpoint | 설명 | |--------|----------|------| | GET | `/api/v1/item-master/bom-items` | 독립 BOM 목록 | | POST | `/api/v1/item-master/bom-items` | 독립 BOM 생성 | | POST | `/api/v1/item-master/sections/{sectionId}/bom-items` | BOM 생성 + 섹션 연결 | | PUT | `/api/v1/item-master/bom-items/{id}` | BOM 수정 | | DELETE | `/api/v1/item-master/bom-items/{id}` | BOM 삭제 | ### 2.6 섹션 템플릿 | Method | Endpoint | 설명 | |--------|----------|------| | GET | `/api/v1/item-master/section-templates` | 템플릿 목록 | | POST | `/api/v1/item-master/section-templates` | 템플릿 생성 | | PUT | `/api/v1/item-master/section-templates/{id}` | 템플릿 수정 | | DELETE | `/api/v1/item-master/section-templates/{id}` | 템플릿 삭제 | ### 2.7 커스텀 탭 | Method | Endpoint | 설명 | |--------|----------|------| | GET | `/api/v1/item-master/custom-tabs` | 탭 목록 | | POST | `/api/v1/item-master/custom-tabs` | 탭 생성 | | PUT | `/api/v1/item-master/custom-tabs/{id}` | 탭 수정 | | DELETE | `/api/v1/item-master/custom-tabs/{id}` | 탭 삭제 | | PUT | `/api/v1/item-master/custom-tabs/reorder` | 탭 순서 변경 | ### 2.8 단위 옵션 | Method | Endpoint | 설명 | |--------|----------|------| | GET | `/api/v1/item-master/unit-options` | 단위 목록 | | POST | `/api/v1/item-master/unit-options` | 단위 생성 | | DELETE | `/api/v1/item-master/unit-options/{id}` | 단위 삭제 | ### 2.9 엔티티 관계 (Link/Unlink) **페이지-섹션 연결:** | Method | Endpoint | 설명 | |--------|----------|------| | POST | `/api/v1/item-master/pages/{pageId}/link-section` | 섹션 연결 | | DELETE | `/api/v1/item-master/pages/{pageId}/unlink-section/{sectionId}` | 섹션 연결 해제 | **페이지-필드 연결:** | Method | Endpoint | 설명 | |--------|----------|------| | POST | `/api/v1/item-master/pages/{pageId}/link-field` | 필드 연결 | | DELETE | `/api/v1/item-master/pages/{pageId}/unlink-field/{fieldId}` | 필드 연결 해제 | **섹션-필드 연결:** | Method | Endpoint | 설명 | |--------|----------|------| | POST | `/api/v1/item-master/sections/{sectionId}/link-field` | 필드 연결 | | DELETE | `/api/v1/item-master/sections/{sectionId}/unlink-field/{fieldId}` | 필드 연결 해제 | **섹션-BOM 연결:** | Method | Endpoint | 설명 | |--------|----------|------| | POST | `/api/v1/item-master/sections/{sectionId}/link-bom` | BOM 연결 | | DELETE | `/api/v1/item-master/sections/{sectionId}/unlink-bom/{bomId}` | BOM 연결 해제 | **관계 조회/정렬:** | Method | Endpoint | 설명 | |--------|----------|------| | GET | `/api/v1/item-master/pages/{pageId}/relationships` | 페이지 관계 조회 | | GET | `/api/v1/item-master/pages/{pageId}/structure` | 페이지 구조 조회 | | GET | `/api/v1/item-master/sections/{sectionId}/relationships` | 섹션 관계 조회 | | POST | `/api/v1/item-master/relationships/reorder` | 관계 순서 변경 | --- ## 3. 데이터 구조 ### 3.1 ItemPage ```typescript interface ItemPage { id: number; tenant_id: number; group_id: number; page_name: string; item_type: 'FG' | 'PT' | 'SM' | 'RM' | 'CS'; source_table: 'products' | 'materials'; absolute_path?: string; is_active: boolean; sections: ItemSection[]; // init 응답에 포함 } ``` ### 3.2 ItemSection ```typescript interface ItemSection { id: number; tenant_id: number; group_id: number; title: string; type: string; order_no: number; is_template: boolean; is_default: boolean; is_locked?: boolean; // 연결 잠금 상태 description?: string; fields?: ItemField[]; bom_items?: ItemBomItem[]; } ``` ### 3.3 ItemField ```typescript interface ItemField { id: number; tenant_id: number; group_id: number; field_name: string; field_key: string; // 저장 시 사용할 키 field_type: FieldType; order_no: number; is_required: boolean; is_common: boolean; is_active: boolean; is_locked: boolean; default_value?: string; placeholder?: string; display_condition?: object; // 조건부 표시 validation_rules?: object; // 유효성 검사 규칙 options?: object; // dropdown 옵션 등 properties?: object; // 추가 설정 category?: string; description?: string; } type FieldType = 'textbox' | 'number' | 'dropdown' | 'checkbox' | 'date' | 'textarea'; ``` ### 3.4 EntityRelationship ```typescript interface EntityRelationship { id: number; tenant_id: number; group_id: number; parent_type: 'page' | 'section'; parent_id: number; child_type: 'section' | 'field' | 'bom'; child_id: number; order_no: number; is_locked: boolean; locked_by?: number; locked_at?: string; metadata?: object; } ``` --- ## 4. 잠금(Lock) 기능 ### 4.1 잠금의 의미 **연결이 잠기면:** - 해당 연결(관계)를 해제할 수 없음 - 연결된 자식 엔티티를 삭제할 수 없음 - 이름 변경, 속성 추가 등 **비구조적 수정은 허용** ``` 예시: page→section 연결이 잠김 ├─ ❌ 섹션을 페이지에서 분리할 수 없음 ├─ ❌ 해당 섹션을 삭제할 수 없음 ├─ ✅ 섹션 제목 변경 가능 └─ ✅ 섹션에 새 필드 추가 가능 ``` ### 4.2 잠금 상태 확인 init API 응답에 `is_locked` 필드가 포함됩니다. ```json { "pages": [{ "id": 1, "sections": [{ "id": 10, "is_locked": true, "fields": [{ "id": 100, "is_locked": false }] }] }] } ``` ### 4.3 잠금 관련 에러 ```json { "success": false, "message": "잠금된 연결은 해제할 수 없습니다.", "error": "entity_protected_by_locked_relationship" } ``` --- ## 5. 필드 타입 | field_type | 설명 | 렌더링 컴포넌트 | |------------|------|----------------| | `textbox` | 텍스트 입력 | `` | | `number` | 숫자 입력 | `` | | `dropdown` | 드롭다운 선택 | `