- BOM 항목 추가/수정/삭제 시 섹션탭 즉시 반영 - 섹션 복제 시 UI 즉시 업데이트 (null vs undefined 이슈 해결) - 항목 수정 기능 추가 (useTemplateManagement) - 실시간 동기화 문서 추가 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
17 KiB
품목기준관리 API 추가 요청 - 섹션 템플릿 하위 데이터
요청일: 2025-11-25 버전: v1.1 작성자: 프론트엔드 개발팀 수신: 백엔드 개발팀 긴급도: 🔴 높음
📋 목차
1. 요청 배경
1.1 문제 상황
- 섹션탭 > 일반 섹션에 항목(필드) 추가 후 새로고침 시 데이터 사라짐
- 섹션탭 > 모듈 섹션(BOM)에 BOM 품목 추가 후 새로고침 시 데이터 사라짐
- 원인: 섹션 템플릿 하위 데이터를 저장/조회하는 API 없음
1.2 현재 상태 비교
| 구분 | 계층구조 (정상) | 섹션 템플릿 (문제) |
|---|---|---|
| 섹션/템플릿 CRUD | ✅ 있음 | ✅ 있음 |
| 필드 CRUD | ✅ /sections/{id}/fields |
❌ 없음 |
| BOM 품목 CRUD | ✅ /sections/{id}/bom-items |
❌ 없음 |
| init 응답에 중첩 포함 | ✅ fields, bomItems 포함 |
❌ 미포함 |
1.3 요청 내용
- 섹션 템플릿 필드 테이블 및 CRUD API 추가
- 섹션 템플릿 BOM 품목 테이블 및 CRUD API 추가
- init API 응답에 섹션 템플릿 하위 데이터 중첩 포함
- 🔴 [추가] 계층구조 섹션 ↔ 섹션 템플릿 데이터 동기화
2. 데이터베이스 테이블 추가
2.0 section_templates 테이블 수정 (데이터 동기화용)
요구사항: 계층구조에서 생성한 섹션과 섹션탭의 템플릿이 동일한 데이터로 연동되어야 함
현재 문제:
계층구조 섹션 생성 시:
├── item_sections 테이블에 저장 (id: 1)
└── section_templates 테이블에 저장 (id: 1)
→ 두 개의 별도 데이터! 연결 없음!
해결 방안: section_templates에 section_id 컬럼 추가
ALTER TABLE section_templates
ADD COLUMN section_id BIGINT UNSIGNED NULL COMMENT '연결된 계층구조 섹션 ID (동기화용)' AFTER tenant_id,
ADD INDEX idx_section_id (section_id),
ADD FOREIGN KEY (section_id) REFERENCES item_sections(id) ON DELETE SET NULL;
동기화 동작:
| 액션 | 동작 |
|---|---|
| 계층구조에서 섹션 생성 | item_sections + section_templates 생성, section_id로 연결 |
| 계층구조에서 섹션 수정 | item_sections 수정 → 연결된 section_templates도 수정 |
| 계층구조에서 섹션 삭제 | item_sections 삭제 → 연결된 section_templates의 section_id = NULL |
| 섹션탭에서 템플릿 수정 | section_templates 수정 → 연결된 item_sections도 수정 |
| 섹션탭에서 템플릿 삭제 | section_templates 삭제 → 연결된 item_sections는 유지 |
init API 응답 수정 (section_id 포함):
{
"sectionTemplates": [
{
"id": 1,
"section_id": 5, // 연결된 계층구조 섹션 ID (없으면 null)
"title": "일반 섹션",
"type": "fields",
...
}
]
}
2.1 section_template_fields (섹션 템플릿 필드)
참고: 기존 item_fields 테이블 구조와 유사하게 설계
CREATE TABLE section_template_fields (
id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '테넌트 ID',
template_id BIGINT UNSIGNED NOT NULL COMMENT '섹션 템플릿 ID',
field_name VARCHAR(255) NOT NULL COMMENT '필드명',
field_key VARCHAR(100) NOT NULL COMMENT '필드 키 (영문)',
field_type ENUM('textbox', 'number', 'dropdown', 'checkbox', 'date', 'textarea') NOT NULL COMMENT '필드 타입',
order_no INT NOT NULL DEFAULT 0 COMMENT '정렬 순서',
is_required TINYINT(1) DEFAULT 0 COMMENT '필수 여부',
options JSON NULL COMMENT '드롭다운 옵션 ["옵션1", "옵션2"]',
multi_column TINYINT(1) DEFAULT 0 COMMENT '다중 컬럼 여부',
column_count INT NULL COMMENT '컬럼 수',
column_names JSON NULL COMMENT '컬럼명 목록 ["컬럼1", "컬럼2"]',
description TEXT NULL COMMENT '설명',
created_by BIGINT UNSIGNED NULL,
updated_by BIGINT UNSIGNED NULL,
deleted_by BIGINT UNSIGNED NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
deleted_at TIMESTAMP NULL,
INDEX idx_tenant_template (tenant_id, template_id),
INDEX idx_order (template_id, order_no),
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE,
FOREIGN KEY (template_id) REFERENCES section_templates(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='섹션 템플릿 필드';
2.2 section_template_bom_items (섹션 템플릿 BOM 품목)
참고: 기존 item_bom_items 테이블 구조와 유사하게 설계
CREATE TABLE section_template_bom_items (
id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '테넌트 ID',
template_id BIGINT UNSIGNED NOT NULL COMMENT '섹션 템플릿 ID',
item_code VARCHAR(100) NULL COMMENT '품목 코드',
item_name VARCHAR(255) NOT NULL COMMENT '품목명',
quantity DECIMAL(15, 4) NOT NULL DEFAULT 0 COMMENT '수량',
unit VARCHAR(50) NULL COMMENT '단위',
unit_price DECIMAL(15, 2) NULL COMMENT '단가',
total_price DECIMAL(15, 2) NULL COMMENT '총액',
spec TEXT NULL COMMENT '규격/사양',
note TEXT NULL COMMENT '비고',
order_no INT NOT NULL DEFAULT 0 COMMENT '정렬 순서',
created_by BIGINT UNSIGNED NULL,
updated_by BIGINT UNSIGNED NULL,
deleted_by BIGINT UNSIGNED NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
deleted_at TIMESTAMP NULL,
INDEX idx_tenant_template (tenant_id, template_id),
INDEX idx_order (template_id, order_no),
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE,
FOREIGN KEY (template_id) REFERENCES section_templates(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='섹션 템플릿 BOM 품목';
3. API 엔드포인트 추가
3.1 섹션 템플릿 필드 관리 (우선순위 1)
POST /v1/item-master/section-templates/{templateId}/fields
목적: 템플릿 필드 생성
Request Body:
{
"field_name": "품목코드",
"field_key": "item_code",
"field_type": "textbox",
"is_required": true,
"options": null,
"multi_column": false,
"column_count": null,
"column_names": null,
"description": "품목 고유 코드"
}
Validation:
field_name: required, string, max:255field_key: required, string, max:100, alpha_dashfield_type: required, in:textbox,number,dropdown,checkbox,date,textareais_required: booleanoptions: nullable, array (dropdown 타입일 경우)multi_column: booleancolumn_count: nullable, integer, min:2, max:10column_names: nullable, arraydescription: nullable, string
Response:
{
"success": true,
"message": "message.created",
"data": {
"id": 1,
"template_id": 1,
"field_name": "품목코드",
"field_key": "item_code",
"field_type": "textbox",
"order_no": 0,
"is_required": true,
"options": null,
"multi_column": false,
"column_count": null,
"column_names": null,
"description": "품목 고유 코드",
"created_at": "2025-11-25T10:00:00.000000Z",
"updated_at": "2025-11-25T10:00:00.000000Z"
}
}
참고:
order_no는 자동 계산 (해당 템플릿의 마지막 필드 order + 1)
PUT /v1/item-master/section-templates/{templateId}/fields/{fieldId}
목적: 템플릿 필드 수정
Request Body:
{
"field_name": "품목코드 (수정)",
"field_type": "dropdown",
"options": ["옵션1", "옵션2"],
"is_required": false
}
Validation: POST와 동일 (모든 필드 optional)
Response: 수정된 필드 정보 반환
DELETE /v1/item-master/section-templates/{templateId}/fields/{fieldId}
목적: 템플릿 필드 삭제 (Soft Delete)
Request: 없음
Response:
{
"success": true,
"message": "message.deleted"
}
PUT /v1/item-master/section-templates/{templateId}/fields/reorder
목적: 템플릿 필드 순서 변경
Request Body:
{
"field_orders": [
{ "id": 3, "order_no": 0 },
{ "id": 1, "order_no": 1 },
{ "id": 2, "order_no": 2 }
]
}
Validation:
field_orders: required, arrayfield_orders.*.id: required, exists:section_template_fields,idfield_orders.*.order_no: required, integer, min:0
Response:
{
"success": true,
"message": "message.updated",
"data": [
{ "id": 3, "order_no": 0 },
{ "id": 1, "order_no": 1 },
{ "id": 2, "order_no": 2 }
]
}
3.2 섹션 템플릿 BOM 품목 관리 (우선순위 2)
POST /v1/item-master/section-templates/{templateId}/bom-items
목적: 템플릿 BOM 품목 생성
Request Body:
{
"item_code": "PART-001",
"item_name": "부품 A",
"quantity": 2,
"unit": "EA",
"unit_price": 15000,
"spec": "100x50x20",
"note": "필수 부품"
}
Validation:
item_code: nullable, string, max:100item_name: required, string, max:255quantity: required, numeric, min:0unit: nullable, string, max:50unit_price: nullable, numeric, min:0spec: nullable, stringnote: nullable, string
Response:
{
"success": true,
"message": "message.created",
"data": {
"id": 1,
"template_id": 2,
"item_code": "PART-001",
"item_name": "부품 A",
"quantity": 2,
"unit": "EA",
"unit_price": 15000,
"total_price": 30000,
"spec": "100x50x20",
"note": "필수 부품",
"order_no": 0,
"created_at": "2025-11-25T10:00:00.000000Z",
"updated_at": "2025-11-25T10:00:00.000000Z"
}
}
참고:
total_price는 서버에서 자동 계산 (quantity * unit_price)order_no는 자동 계산 (해당 템플릿의 마지막 BOM 품목 order + 1)
PUT /v1/item-master/section-templates/{templateId}/bom-items/{itemId}
목적: 템플릿 BOM 품목 수정
Request Body:
{
"item_name": "부품 A (수정)",
"quantity": 3,
"unit_price": 12000
}
Validation: POST와 동일 (모든 필드 optional)
Response: 수정된 BOM 품목 정보 반환
DELETE /v1/item-master/section-templates/{templateId}/bom-items/{itemId}
목적: 템플릿 BOM 품목 삭제 (Soft Delete)
Request: 없음
Response:
{
"success": true,
"message": "message.deleted"
}
PUT /v1/item-master/section-templates/{templateId}/bom-items/reorder
목적: 템플릿 BOM 품목 순서 변경
Request Body:
{
"item_orders": [
{ "id": 3, "order_no": 0 },
{ "id": 1, "order_no": 1 },
{ "id": 2, "order_no": 2 }
]
}
Validation:
item_orders: required, arrayitem_orders.*.id: required, exists:section_template_bom_items,iditem_orders.*.order_no: required, integer, min:0
Response:
{
"success": true,
"message": "message.updated",
"data": [...]
}
4. init API 응답 수정
4.1 현재 응답 (문제)
{
"success": true,
"data": {
"sectionTemplates": [
{
"id": 1,
"title": "일반 섹션",
"type": "fields",
"description": null,
"is_default": false
},
{
"id": 2,
"title": "BOM 섹션",
"type": "bom",
"description": null,
"is_default": false
}
]
}
}
4.2 수정 요청
sectionTemplates에 하위 데이터 중첩 포함:
{
"success": true,
"data": {
"sectionTemplates": [
{
"id": 1,
"title": "일반 섹션",
"type": "fields",
"description": null,
"is_default": false,
"fields": [
{
"id": 1,
"field_name": "품목코드",
"field_key": "item_code",
"field_type": "textbox",
"order_no": 0,
"is_required": true,
"options": null,
"multi_column": false,
"column_count": null,
"column_names": null,
"description": "품목 고유 코드"
}
]
},
{
"id": 2,
"title": "BOM 섹션",
"type": "bom",
"description": null,
"is_default": false,
"bomItems": [
{
"id": 1,
"item_code": "PART-001",
"item_name": "부품 A",
"quantity": 2,
"unit": "EA",
"unit_price": 15000,
"total_price": 30000,
"spec": "100x50x20",
"note": "필수 부품",
"order_no": 0
}
]
}
]
}
}
참고:
type: "fields"템플릿:fields배열 포함type: "bom"템플릿:bomItems배열 포함- 기존
pages응답의 중첩 구조와 동일한 패턴
5. 구현 우선순위
| 우선순위 | 작업 내용 | 예상 공수 |
|---|---|---|
| 🔴 0 | section_templates에 section_id 컬럼 추가 (동기화용) |
0.5일 |
| 🔴 0 | 계층구조 섹션 생성 시 section_templates 자동 생성 로직 |
0.5일 |
| 🔴 1 | section_template_fields 테이블 생성 |
0.5일 |
| 🔴 1 | 섹션 템플릿 필드 CRUD API (5개) | 1일 |
| 🔴 1 | init API 응답에 fields 중첩 포함 |
0.5일 |
| 🟡 2 | section_template_bom_items 테이블 생성 |
0.5일 |
| 🟡 2 | 섹션 템플릿 BOM 품목 CRUD API (5개) | 1일 |
| 🟡 2 | init API 응답에 bomItems 중첩 포함 |
0.5일 |
| 🟢 3 | 양방향 동기화 로직 (섹션↔템플릿 수정 시 상호 반영) | 1일 |
| 🟢 3 | Swagger 문서 업데이트 | 0.5일 |
총 예상 공수: 백엔드 6.5일
6. 프론트엔드 연동 계획
6.1 API 완료 후 프론트엔드 작업
| 작업 | 설명 | 의존성 |
|---|---|---|
| 타입 정의 수정 | SectionTemplateResponse에 fields, bomItems, section_id 추가 |
init API 수정 후 |
| Context 수정 | 섹션 템플릿 필드/BOM API 호출 로직 추가 | CRUD API 완료 후 |
| 로컬 상태 제거 | default_fields 로컬 관리 로직 → API 연동으로 교체 |
CRUD API 완료 후 |
| 동기화 UI | 계층구조↔섹션탭 간 데이터 자동 반영 | section_id 추가 후 |
6.2 타입 수정 예시
현재 (src/types/item-master-api.ts):
export interface SectionTemplateResponse {
id: number;
title: string;
type: 'fields' | 'bom';
description?: string;
is_default: boolean;
}
수정 후:
export interface SectionTemplateResponse {
id: number;
section_id?: number | null; // 연결된 계층구조 섹션 ID
title: string;
type: 'fields' | 'bom';
description?: string;
is_default: boolean;
fields?: SectionTemplateFieldResponse[]; // type='fields'일 때
bomItems?: SectionTemplateBomItemResponse[]; // type='bom'일 때
}
6.3 동기화 시나리오 정리
[시나리오 1] 계층구조에서 섹션 생성
└─ 백엔드: item_sections + section_templates 동시 생성 (section_id로 연결)
└─ 프론트: init 재조회 → 양쪽 탭에 데이터 표시
[시나리오 2] 계층구조에서 필드 추가/수정
└─ 백엔드: item_fields 저장 → 연결된 section_template_fields도 동기화
└─ 프론트: init 재조회 → 섹션탭에 필드 반영
[시나리오 3] 섹션탭에서 필드 추가/수정
└─ 백엔드: section_template_fields 저장 → 연결된 item_fields도 동기화
└─ 프론트: init 재조회 → 계층구조탭에 필드 반영
[시나리오 4] 섹션탭에서 독립 템플릿 생성 (section_id = null)
└─ 백엔드: section_templates만 생성 (계층구조와 무관)
└─ 프론트: 섹션탭에서만 사용 가능한 템플릿
📞 문의
질문 있으시면 프론트엔드 팀으로 연락 주세요.
관련 파일
프론트엔드
src/components/items/ItemMasterDataManagement.tsx- 메인 컴포넌트src/components/items/ItemMasterDataManagement/tabs/SectionsTab.tsx- 섹션 탭src/components/items/ItemMasterDataManagement/dialogs/TemplateFieldDialog.tsx- 템플릿 필드 다이얼로그src/lib/api/item-master.ts- API 클라이언트src/types/item-master-api.ts- API 타입 정의src/contexts/ItemMasterContext.tsx- Context Provider
참조 문서
claudedocs/item-master/[API-2025-11-20] item-master-specification.md- 기준 문서claudedocs/item-master/[API-2025-11-25] item-master-data-management-api-request.md- API 요청서
작성일: 2025-11-25
기준 문서: [API-2025-11-20] item-master-specification.md