Files
sam-docs/history/2025-11/item-master-spec.md
hskwon 08a8259313 docs: 5130 레거시 분석 문서 및 기존 문서 초기 커밋
- 5130 레거시 시스템 분석 (00_OVERVIEW ~ 08_SAM_COMPARISON)
- MES 프로젝트 문서
- API/프론트엔드 스펙 문서
- 가이드 및 레퍼런스 문서
2025-12-04 18:47:19 +09:00

1298 lines
30 KiB
Markdown

# 품목기준관리 API 명세서
**작성일**: 2025-11-20
**버전**: v1.0
**작성자**: 프론트엔드 개발팀
**수신**: 백엔드 개발팀
---
## 📋 목차
1. [개요](#1-개요)
2. [인증 및 공통 사항](#2-인증-및-공통-사항)
3. [데이터베이스 테이블 정의](#3-데이터베이스-테이블-정의)
4. [API 엔드포인트](#4-api-엔드포인트)
5. [요청/응답 예시](#5-요청응답-예시)
6. [에러 처리](#6-에러-처리)
7. [구현 우선순위](#7-구현-우선순위)
---
## 1. 개요
### 1.1 목적
품목기준관리 화면에서 사용할 API 개발 요청
### 1.2 주요 기능
- 품목 유형별 페이지 관리 (FG, PT, SM, RM, CS)
- 계층구조 기반 섹션 및 필드 관리
- BOM(Bill of Materials) 항목 관리
- 섹션 템플릿 및 마스터 필드 관리
- 커스텀 탭 및 단위 관리
### 1.3 기술 요구사항
-**Service-First 패턴** 적용
-**Multi-tenant**: `tenant_id` 기반 격리, `BelongsToTenant` 스코프
-**Soft Delete**: 모든 테이블 적용
-**감사 로그**: 생성/수정/삭제 시 `audit_logs` 기록
-**i18n**: 메시지는 `__('message.xxx')` 키만 사용
-**실시간 저장**: 모든 CUD 작업 즉시 처리 (일괄 저장 없음)
### 1.4 저장 전략
**중요**: 프론트엔드에서 **실시간 저장** 방식 사용
- 페이지/섹션/필드 생성 즉시 API 호출
- 수정/삭제/순서변경 즉시 API 호출
- 일괄 저장(Batch Save) API 불필요
---
## 2. 인증 및 공통 사항
### 2.1 인증
```
Headers:
X-API-KEY: {api_key}
Authorization: Bearer {sanctum_token}
```
### 2.2 Base URL
```
http://api.sam.kr/api/v1/item-master
```
### 2.3 공통 응답 형식
**성공 응답**:
```json
{
"success": true,
"message": "message.created",
"data": { ... }
}
```
**에러 응답**:
```json
{
"success": false,
"message": "error.validation_failed",
"errors": {
"page_name": ["페이지명은 필수입니다."]
}
}
```
### 2.4 공통 컬럼
모든 테이블에 다음 컬럼 포함:
- `tenant_id` (BIGINT, NOT NULL, INDEX)
- `created_by` (BIGINT, NULL)
- `updated_by` (BIGINT, NULL)
- `deleted_by` (BIGINT, NULL)
- `created_at` (TIMESTAMP)
- `updated_at` (TIMESTAMP)
- `deleted_at` (TIMESTAMP, NULL) - Soft Delete
---
## 3. 데이터베이스 테이블 정의
### 3.1 item_pages (품목 페이지)
```sql
CREATE TABLE item_pages (
id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '테넌트 ID',
page_name VARCHAR(255) NOT NULL COMMENT '페이지명',
item_type ENUM('FG', 'PT', 'SM', 'RM', 'CS') NOT NULL COMMENT '품목 유형 (완제품/반제품/부자재/원자재/소모품)',
absolute_path VARCHAR(500) NULL COMMENT '절대 경로',
is_active TINYINT(1) DEFAULT 1 COMMENT '활성 여부',
created_by BIGINT UNSIGNED NULL COMMENT '생성자 ID',
updated_by BIGINT UNSIGNED NULL COMMENT '수정자 ID',
deleted_by BIGINT UNSIGNED NULL COMMENT '삭제자 ID',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
deleted_at TIMESTAMP NULL,
INDEX idx_tenant_id (tenant_id),
INDEX idx_item_type (item_type),
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='품목 페이지';
```
### 3.2 item_sections (섹션 인스턴스)
```sql
CREATE TABLE item_sections (
id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '테넌트 ID',
page_id BIGINT UNSIGNED NOT NULL COMMENT '페이지 ID',
title VARCHAR(255) NOT NULL COMMENT '섹션 제목',
type ENUM('fields', 'bom') NOT NULL DEFAULT 'fields' COMMENT '섹션 타입 (필드형/BOM형)',
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_page (tenant_id, page_id),
INDEX idx_order (page_id, order_no),
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE,
FOREIGN KEY (page_id) REFERENCES item_pages(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='품목 섹션 인스턴스';
```
### 3.3 item_fields (필드)
```sql
CREATE TABLE item_fields (
id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '테넌트 ID',
section_id BIGINT UNSIGNED NOT NULL COMMENT '섹션 ID',
field_name VARCHAR(255) 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 '필수 여부',
default_value TEXT NULL COMMENT '기본값',
placeholder VARCHAR(255) NULL COMMENT '플레이스홀더',
display_condition JSON NULL COMMENT '표시 조건 {"field_id": "1", "operator": "equals", "value": "true"}',
validation_rules JSON NULL COMMENT '검증 규칙 {"min": 0, "max": 100, "pattern": "regex"}',
options JSON NULL COMMENT '드롭다운 옵션 [{"label": "옵션1", "value": "val1"}]',
properties JSON NULL COMMENT '필드 속성 {"unit": "mm", "precision": 2, "format": "YYYY-MM-DD"}',
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_section (tenant_id, section_id),
INDEX idx_order (section_id, order_no),
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE,
FOREIGN KEY (section_id) REFERENCES item_sections(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='품목 필드';
```
### 3.4 item_bom_items (BOM 항목)
```sql
CREATE TABLE item_bom_items (
id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '테넌트 ID',
section_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 '비고',
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_section (tenant_id, section_id),
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE,
FOREIGN KEY (section_id) REFERENCES item_sections(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='BOM 항목';
```
### 3.5 section_templates (섹션 템플릿)
```sql
CREATE TABLE section_templates (
id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '테넌트 ID',
title VARCHAR(255) NOT NULL COMMENT '템플릿명',
type ENUM('fields', 'bom') NOT NULL DEFAULT 'fields' COMMENT '섹션 타입',
description TEXT NULL COMMENT '설명',
is_default TINYINT(1) 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 (tenant_id),
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='섹션 템플릿';
```
### 3.6 item_master_fields (마스터 필드)
```sql
CREATE TABLE item_master_fields (
id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '테넌트 ID',
field_name VARCHAR(255) NOT NULL COMMENT '필드명',
field_type ENUM('textbox', 'number', 'dropdown', 'checkbox', 'date', 'textarea') NOT NULL COMMENT '필드 타입',
category VARCHAR(100) NULL COMMENT '카테고리',
description TEXT NULL COMMENT '설명',
is_common TINYINT(1) DEFAULT 0 COMMENT '공통 필드 여부',
default_value TEXT NULL COMMENT '기본값',
options JSON NULL COMMENT '옵션',
validation_rules JSON NULL COMMENT '검증 규칙',
properties JSON 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 (tenant_id),
INDEX idx_category (category),
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='마스터 필드';
```
### 3.7 custom_tabs (커스텀 탭)
```sql
CREATE TABLE custom_tabs (
id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '테넌트 ID',
label VARCHAR(255) NOT NULL COMMENT '탭 라벨',
icon VARCHAR(100) NULL COMMENT '아이콘',
is_default TINYINT(1) DEFAULT 0 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 (tenant_id),
INDEX idx_order (tenant_id, order_no),
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='커스텀 탭';
```
### 3.8 tab_columns (탭별 컬럼 설정)
```sql
CREATE TABLE tab_columns (
id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '테넌트 ID',
tab_id BIGINT UNSIGNED NOT NULL COMMENT '탭 ID',
columns JSON NOT NULL COMMENT '컬럼 설정 [{"key": "name", "label": "품목명", "visible": true, "order": 0}]',
created_by BIGINT UNSIGNED NULL,
updated_by BIGINT UNSIGNED NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_tenant_tab (tenant_id, tab_id),
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE,
FOREIGN KEY (tab_id) REFERENCES custom_tabs(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='탭별 컬럼 설정';
```
### 3.9 unit_options (단위 옵션)
```sql
CREATE TABLE unit_options (
id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '테넌트 ID',
label VARCHAR(100) NOT NULL COMMENT '단위 라벨',
value VARCHAR(50) NOT 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 (tenant_id),
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='단위 옵션';
```
---
## 4. API 엔드포인트
### 4.1 초기화 API
#### `GET /v1/item-master/init`
**목적**: 화면 진입 시 전체 초기 데이터 로드
**Request**: 없음
**Response**:
```json
{
"success": true,
"message": "message.fetched",
"data": {
"pages": [
{
"id": 1,
"page_name": "완제품 A",
"item_type": "FG",
"absolute_path": "/FG/완제품 A",
"is_active": true,
"sections": [
{
"id": 1,
"title": "기본 정보",
"type": "fields",
"order_no": 0,
"fields": [...]
}
]
}
],
"sectionTemplates": [...],
"masterFields": [...],
"customTabs": [...],
"tabColumns": {...},
"unitOptions": [...]
}
}
```
**참고**:
- `pages``sections`, `fields`, `bomItems`를 중첩(nested) 포함
- 한 번의 API 호출로 모든 데이터 로드
---
### 4.2 페이지 관리
#### `GET /v1/item-master/pages`
**목적**: 페이지 목록 조회 (섹션/필드 포함)
**Query Parameters**:
| 파라미터 | 타입 | 필수 | 설명 |
|---------|------|------|------|
| `item_type` | string | 선택 | 품목 유형 필터 (FG, PT, SM, RM, CS) |
**Response**: 초기화 API의 `data.pages`와 동일
---
#### `POST /v1/item-master/pages`
**목적**: 페이지 생성
**Request Body**:
```json
{
"page_name": "완제품 A",
"item_type": "FG",
"absolute_path": "/FG/완제품 A"
}
```
**Validation**:
- `page_name`: required, string, max:255
- `item_type`: required, in:FG,PT,SM,RM,CS
- `absolute_path`: nullable, string, max:500
**Response**:
```json
{
"success": true,
"message": "message.created",
"data": {
"id": 1,
"page_name": "완제품 A",
"item_type": "FG",
"absolute_path": "/FG/완제품 A",
"is_active": true,
"sections": [],
"created_at": "2025-11-20T10:00:00.000000Z",
"updated_at": "2025-11-20T10:00:00.000000Z"
}
}
```
---
#### `PUT /v1/item-master/pages/{id}`
**목적**: 페이지 수정
**Request Body**:
```json
{
"page_name": "완제품 A (수정)",
"absolute_path": "/FG/완제품 A (수정)"
}
```
**Validation**:
- `page_name`: string, max:255
- `absolute_path`: nullable, string, max:500
**Response**: 수정된 페이지 정보 반환
---
#### `DELETE /v1/item-master/pages/{id}`
**목적**: 페이지 삭제 (Soft Delete)
**Request**: 없음
**Response**:
```json
{
"success": true,
"message": "message.deleted"
}
```
**참고**:
- Soft Delete 처리 (`deleted_at` 업데이트)
- 하위 섹션/필드도 함께 Soft Delete 처리 (Cascade)
---
### 4.3 섹션 관리
#### `POST /v1/item-master/pages/{pageId}/sections`
**목적**: 섹션 생성
**Request Body**:
```json
{
"title": "기본 정보",
"type": "fields"
}
```
**Validation**:
- `title`: required, string, max:255
- `type`: required, in:fields,bom
**Response**:
```json
{
"success": true,
"message": "message.created",
"data": {
"id": 1,
"page_id": 1,
"title": "기본 정보",
"type": "fields",
"order_no": 0,
"fields": []
}
}
```
**참고**:
- `order_no`는 자동 계산 (해당 페이지의 마지막 섹션 order + 1)
---
#### `PUT /v1/item-master/sections/{id}`
**목적**: 섹션 수정
**Request Body**:
```json
{
"title": "기본 정보 (수정)"
}
```
**Validation**:
- `title`: string, max:255
**Response**: 수정된 섹션 정보 반환
---
#### `DELETE /v1/item-master/sections/{id}`
**목적**: 섹션 삭제 (Soft Delete)
**Request**: 없음
**Response**:
```json
{
"success": true,
"message": "message.deleted"
}
```
**참고**: 하위 필드도 함께 Soft Delete 처리
---
#### `PUT /v1/item-master/pages/{pageId}/sections/reorder`
**목적**: 섹션 순서 변경 (드래그 앤 드롭)
**Request Body**:
```json
{
"section_orders": [
{"id": 2, "order_no": 0},
{"id": 1, "order_no": 1},
{"id": 3, "order_no": 2}
]
}
```
**Validation**:
- `section_orders`: required, array
- `section_orders.*.id`: required, exists:item_sections,id
- `section_orders.*.order_no`: required, integer, min:0
**Response**:
```json
{
"success": true,
"message": "message.reordered"
}
```
---
### 4.4 필드 관리
#### `POST /v1/item-master/sections/{sectionId}/fields`
**목적**: 필드 생성
**Request Body**:
```json
{
"field_name": "제품명",
"field_type": "textbox",
"is_required": true,
"placeholder": "제품명을 입력하세요",
"validation_rules": {
"min": 1,
"max": 100
},
"properties": {
"unit": "mm",
"precision": 2
}
}
```
**Validation**:
- `field_name`: required, string, max:255
- `field_type`: required, in:textbox,number,dropdown,checkbox,date,textarea
- `is_required`: boolean
- `placeholder`: nullable, string, max:255
- `validation_rules`: nullable, json
- `properties`: nullable, json
- `options`: nullable, json (field_type=dropdown일 때 필수)
**Response**:
```json
{
"success": true,
"message": "message.created",
"data": {
"id": 1,
"section_id": 1,
"field_name": "제품명",
"field_type": "textbox",
"order_no": 0,
"is_required": true,
"placeholder": "제품명을 입력하세요",
"validation_rules": {...},
"properties": {...}
}
}
```
---
#### `PUT /v1/item-master/fields/{id}`
**목적**: 필드 수정
**Request Body**: 생성과 동일 (모든 필드 선택적)
**Response**: 수정된 필드 정보 반환
---
#### `DELETE /v1/item-master/fields/{id}`
**목적**: 필드 삭제 (Soft Delete)
**Request**: 없음
**Response**:
```json
{
"success": true,
"message": "message.deleted"
}
```
---
#### `PUT /v1/item-master/sections/{sectionId}/fields/reorder`
**목적**: 필드 순서 변경
**Request Body**:
```json
{
"field_orders": [
{"id": 3, "order_no": 0},
{"id": 1, "order_no": 1},
{"id": 2, "order_no": 2}
]
}
```
**Validation**: 섹션 순서 변경과 동일
**Response**:
```json
{
"success": true,
"message": "message.reordered"
}
```
---
### 4.5 BOM 관리
#### `POST /v1/item-master/sections/{sectionId}/bom-items`
**목적**: BOM 항목 생성
**Request Body**:
```json
{
"item_code": "MAT-001",
"item_name": "철판",
"quantity": 10.5,
"unit": "kg",
"unit_price": 5000.00,
"total_price": 52500.00,
"spec": "두께 2mm, 스테인리스",
"note": "비고 사항"
}
```
**Validation**:
- `item_code`: nullable, string, max:100
- `item_name`: required, string, max:255
- `quantity`: required, numeric, min:0
- `unit`: nullable, string, max:50
- `unit_price`: nullable, numeric, min:0
- `total_price`: nullable, numeric, min:0
- `spec`: nullable, string
- `note`: nullable, string
**Response**:
```json
{
"success": true,
"message": "message.created",
"data": {
"id": 1,
"section_id": 1,
"item_code": "MAT-001",
"item_name": "철판",
"quantity": 10.5,
"unit": "kg",
"unit_price": 5000.00,
"total_price": 52500.00,
"spec": "두께 2mm, 스테인리스",
"note": "비고 사항"
}
}
```
---
#### `PUT /v1/item-master/bom-items/{id}`
**목적**: BOM 항목 수정
**Request Body**: 생성과 동일 (모든 필드 선택적)
**Response**: 수정된 BOM 항목 반환
---
#### `DELETE /v1/item-master/bom-items/{id}`
**목적**: BOM 항목 삭제 (Soft Delete)
**Request**: 없음
**Response**:
```json
{
"success": true,
"message": "message.deleted"
}
```
---
### 4.6 섹션 템플릿
#### `GET /v1/item-master/section-templates`
**목적**: 섹션 템플릿 목록 조회
**Request**: 없음
**Response**:
```json
{
"success": true,
"message": "message.fetched",
"data": [
{
"id": 1,
"title": "기본 정보 템플릿",
"type": "fields",
"description": "제품 기본 정보 입력용 템플릿",
"is_default": true
}
]
}
```
---
#### `POST /v1/item-master/section-templates`
**목적**: 섹션 템플릿 생성
**Request Body**:
```json
{
"title": "기본 정보 템플릿",
"type": "fields",
"description": "제품 기본 정보 입력용 템플릿",
"is_default": false
}
```
**Validation**:
- `title`: required, string, max:255
- `type`: required, in:fields,bom
- `description`: nullable, string
- `is_default`: boolean
**Response**: 생성된 템플릿 정보 반환
---
#### `PUT /v1/item-master/section-templates/{id}`
**목적**: 섹션 템플릿 수정
**Request Body**: 생성과 동일 (모든 필드 선택적)
**Response**: 수정된 템플릿 정보 반환
---
#### `DELETE /v1/item-master/section-templates/{id}`
**목적**: 섹션 템플릿 삭제 (Soft Delete)
**Request**: 없음
**Response**:
```json
{
"success": true,
"message": "message.deleted"
}
```
---
### 4.7 마스터 필드
#### `GET /v1/item-master/master-fields`
**목적**: 마스터 필드 목록 조회
**Query Parameters**:
| 파라미터 | 타입 | 필수 | 설명 |
|---------|------|------|------|
| `category` | string | 선택 | 카테고리 필터 |
**Response**:
```json
{
"success": true,
"message": "message.fetched",
"data": [
{
"id": 1,
"field_name": "제품명",
"field_type": "textbox",
"category": "기본정보",
"description": "제품 이름",
"is_common": true,
"options": null,
"validation_rules": {"max": 100},
"properties": null
}
]
}
```
---
#### `POST /v1/item-master/master-fields`
**목적**: 마스터 필드 생성
**Request Body**:
```json
{
"field_name": "제품명",
"field_type": "textbox",
"category": "기본정보",
"description": "제품 이름",
"is_common": true,
"validation_rules": {"max": 100}
}
```
**Validation**:
- `field_name`: required, string, max:255
- `field_type`: required, in:textbox,number,dropdown,checkbox,date,textarea
- `category`: nullable, string, max:100
- `description`: nullable, string
- `is_common`: boolean
- `options`: nullable, json
- `validation_rules`: nullable, json
- `properties`: nullable, json
**Response**: 생성된 마스터 필드 정보 반환
---
#### `PUT /v1/item-master/master-fields/{id}`
**목적**: 마스터 필드 수정
**Request Body**: 생성과 동일 (모든 필드 선택적)
**Response**: 수정된 마스터 필드 정보 반환
---
#### `DELETE /v1/item-master/master-fields/{id}`
**목적**: 마스터 필드 삭제 (Soft Delete)
**Request**: 없음
**Response**:
```json
{
"success": true,
"message": "message.deleted"
}
```
---
### 4.8 커스텀 탭
#### `GET /v1/item-master/custom-tabs`
**목적**: 커스텀 탭 목록 조회
**Request**: 없음
**Response**:
```json
{
"success": true,
"message": "message.fetched",
"data": [
{
"id": 1,
"label": "품질",
"icon": "quality-icon",
"is_default": false,
"order_no": 0
}
]
}
```
---
#### `POST /v1/item-master/custom-tabs`
**목적**: 커스텀 탭 생성
**Request Body**:
```json
{
"label": "품질",
"icon": "quality-icon",
"is_default": false
}
```
**Validation**:
- `label`: required, string, max:255
- `icon`: nullable, string, max:100
- `is_default`: boolean
**Response**: 생성된 탭 정보 반환 (order_no 자동 계산)
---
#### `PUT /v1/item-master/custom-tabs/{id}`
**목적**: 커스텀 탭 수정
**Request Body**: 생성과 동일 (모든 필드 선택적)
**Response**: 수정된 탭 정보 반환
---
#### `DELETE /v1/item-master/custom-tabs/{id}`
**목적**: 커스텀 탭 삭제 (Soft Delete)
**Request**: 없음
**Response**:
```json
{
"success": true,
"message": "message.deleted"
}
```
---
#### `PUT /v1/item-master/custom-tabs/reorder`
**목적**: 탭 순서 변경
**Request Body**:
```json
{
"tab_orders": [
{"id": 2, "order_no": 0},
{"id": 1, "order_no": 1}
]
}
```
**Validation**: 섹션 순서 변경과 동일
**Response**:
```json
{
"success": true,
"message": "message.reordered"
}
```
---
#### `PUT /v1/item-master/custom-tabs/{id}/columns`
**목적**: 탭별 컬럼 설정
**Request Body**:
```json
{
"columns": [
{"key": "name", "label": "품목명", "visible": true, "order": 0},
{"key": "code", "label": "품목코드", "visible": true, "order": 1},
{"key": "price", "label": "가격", "visible": false, "order": 2}
]
}
```
**Validation**:
- `columns`: required, array
- `columns.*.key`: required, string
- `columns.*.label`: required, string
- `columns.*.visible`: required, boolean
- `columns.*.order`: required, integer
**Response**:
```json
{
"success": true,
"message": "message.updated",
"data": {
"tab_id": 1,
"columns": [...]
}
}
```
---
### 4.9 단위 관리
#### `GET /v1/item-master/units`
**목적**: 단위 목록 조회
**Request**: 없음
**Response**:
```json
{
"success": true,
"message": "message.fetched",
"data": [
{"id": 1, "label": "킬로그램", "value": "kg"},
{"id": 2, "label": "미터", "value": "m"}
]
}
```
---
#### `POST /v1/item-master/units`
**목적**: 단위 생성
**Request Body**:
```json
{
"label": "킬로그램",
"value": "kg"
}
```
**Validation**:
- `label`: required, string, max:100
- `value`: required, string, max:50
**Response**: 생성된 단위 정보 반환
---
#### `DELETE /v1/item-master/units/{id}`
**목적**: 단위 삭제 (Soft Delete)
**Request**: 없음
**Response**:
```json
{
"success": true,
"message": "message.deleted"
}
```
---
## 5. 요청/응답 예시
### 5.1 페이지 생성 → 섹션 추가 → 필드 추가 흐름
**1단계: 페이지 생성**
```bash
POST /v1/item-master/pages
{
"page_name": "완제품 A",
"item_type": "FG",
"absolute_path": "/FG/완제품 A"
}
→ Response: {"data": {"id": 1, ...}}
```
**2단계: 섹션 추가**
```bash
POST /v1/item-master/pages/1/sections
{
"title": "기본 정보",
"type": "fields"
}
→ Response: {"data": {"id": 1, "page_id": 1, ...}}
```
**3단계: 필드 추가**
```bash
POST /v1/item-master/sections/1/fields
{
"field_name": "제품명",
"field_type": "textbox",
"is_required": true
}
→ Response: {"data": {"id": 1, "section_id": 1, ...}}
```
---
### 5.2 BOM 섹션 생성 → BOM 항목 추가
**1단계: BOM 섹션 생성**
```bash
POST /v1/item-master/pages/1/sections
{
"title": "자재 목록",
"type": "bom"
}
→ Response: {"data": {"id": 2, "type": "bom", ...}}
```
**2단계: BOM 항목 추가**
```bash
POST /v1/item-master/sections/2/bom-items
{
"item_name": "철판",
"quantity": 10.5,
"unit": "kg",
"unit_price": 5000
}
→ Response: {"data": {"id": 1, "section_id": 2, ...}}
```
---
## 6. 에러 처리
### 6.1 에러 응답 형식
```json
{
"success": false,
"message": "error.validation_failed",
"errors": {
"page_name": ["페이지명은 필수입니다."],
"item_type": ["올바른 품목 유형을 선택하세요."]
}
}
```
### 6.2 주요 에러 코드
| HTTP 상태 | message 키 | 설명 |
|----------|-----------|------|
| 400 | error.validation_failed | 유효성 검증 실패 |
| 401 | error.unauthorized | 인증 실패 |
| 403 | error.forbidden | 권한 없음 |
| 404 | error.not_found | 리소스를 찾을 수 없음 |
| 422 | error.unprocessable | 처리할 수 없는 요청 |
| 500 | error.internal_server | 서버 내부 오류 |
### 6.3 Tenant 격리 에러
```json
{
"success": false,
"message": "error.forbidden",
"errors": {
"tenant_id": ["다른 테넌트의 리소스에 접근할 수 없습니다."]
}
}
```
**참고**: `BelongsToTenant` 스코프가 자동으로 처리하므로 404 반환
---
## 7. 구현 우선순위
### 🔴 우선순위 1 (필수 - 화면 기본 동작)
1. **초기화 API**: `GET /v1/item-master/init`
2. **페이지 CRUD**:
- `GET /v1/item-master/pages`
- `POST /v1/item-master/pages`
- `PUT /v1/item-master/pages/{id}`
- `DELETE /v1/item-master/pages/{id}`
3. **섹션 CRUD**:
- `POST /v1/item-master/pages/{pageId}/sections`
- `PUT /v1/item-master/sections/{id}`
- `DELETE /v1/item-master/sections/{id}`
4. **필드 CRUD**:
- `POST /v1/item-master/sections/{sectionId}/fields`
- `PUT /v1/item-master/fields/{id}`
- `DELETE /v1/item-master/fields/{id}`
### 🟡 우선순위 2 (중요 - 핵심 기능)
5. **BOM 관리**:
- `POST /v1/item-master/sections/{sectionId}/bom-items`
- `PUT /v1/item-master/bom-items/{id}`
- `DELETE /v1/item-master/bom-items/{id}`
6. **순서 변경**:
- `PUT /v1/item-master/pages/{pageId}/sections/reorder`
- `PUT /v1/item-master/sections/{sectionId}/fields/reorder`
7. **단위 관리**:
- `GET /v1/item-master/units`
- `POST /v1/item-master/units`
- `DELETE /v1/item-master/units/{id}`
### 🟢 우선순위 3 (부가 기능)
8. **섹션 템플릿**: 전체 CRUD
9. **마스터 필드**: 전체 CRUD
10. **커스텀 탭**: 전체 CRUD + 컬럼 설정
---
## 📌 참고 사항
### 1. Cascade 삭제 정책
- 페이지 삭제 시 → 하위 섹션/필드 모두 Soft Delete
- 섹션 삭제 시 → 하위 필드 모두 Soft Delete
- 모두 `deleted_at` 업데이트로 처리
### 2. order_no 자동 계산
- 새로운 섹션/필드 생성 시 자동으로 마지막 순서 + 1
- 프론트엔드에서 order_no 전달 불필요
### 3. Nested 조회 최적화
- `GET /v1/item-master/pages`: with(['sections.fields', 'sections.bomItems'])
- Eager Loading으로 N+1 문제 방지
### 4. 감사 로그
- 모든 생성/수정/삭제 시 `audit_logs` 기록
- `action`: created, updated, deleted
- `before`, `after` 필드에 변경 전후 데이터 JSON 저장
### 5. i18n 메시지 키
```php
// lang/ko/message.php
return [
'fetched' => '조회되었습니다.',
'created' => '생성되었습니다.',
'updated' => '수정되었습니다.',
'deleted' => '삭제되었습니다.',
'reordered' => '순서가 변경되었습니다.',
];
```
---
## ✅ 체크리스트
백엔드 개발 완료 전 확인사항:
```
□ Service-First 패턴 적용 (Controller는 DI + Service 호출만)
□ BelongsToTenant scope 모든 모델에 적용
□ SoftDeletes 모든 모델에 적용
□ 공통 컬럼 (tenant_id, created_by, updated_by, deleted_by) 포함
□ 감사 로그 생성/수정/삭제 시 기록
□ i18n 메시지 키 사용 (__('message.xxx'))
□ FormRequest 검증
□ Swagger 문서화 (app/Swagger/v1/ItemMasterApi.php)
□ Cascade 삭제 정책 적용
□ Nested 조회 최적화 (Eager Loading)
□ order_no 자동 계산 로직
□ 실시간 저장 지원 (일괄 저장 없음)
```
---
## 📞 문의
**프론트엔드 개발팀**: [연락처]
**백엔드 개발팀**: [연락처]
---
**문서 버전**: v1.0
**작성일**: 2025-11-20
**다음 리뷰 예정일**: 백엔드 구현 완료 후