- 5130 레거시 시스템 분석 (00_OVERVIEW ~ 08_SAM_COMPARISON) - MES 프로젝트 문서 - API/프론트엔드 스펙 문서 - 가이드 및 레퍼런스 문서
30 KiB
품목기준관리 API 명세서
작성일: 2025-11-20 버전: v1.0 작성자: 프론트엔드 개발팀 수신: 백엔드 개발팀
📋 목차
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 공통 응답 형식
성공 응답:
{
"success": true,
"message": "message.created",
"data": { ... }
}
에러 응답:
{
"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 (품목 페이지)
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 (섹션 인스턴스)
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 (필드)
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 항목)
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 (섹션 템플릿)
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 (마스터 필드)
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 (커스텀 탭)
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 (탭별 컬럼 설정)
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 (단위 옵션)
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:
{
"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:
{
"page_name": "완제품 A",
"item_type": "FG",
"absolute_path": "/FG/완제품 A"
}
Validation:
page_name: required, string, max:255item_type: required, in:FG,PT,SM,RM,CSabsolute_path: nullable, string, max:500
Response:
{
"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:
{
"page_name": "완제품 A (수정)",
"absolute_path": "/FG/완제품 A (수정)"
}
Validation:
page_name: string, max:255absolute_path: nullable, string, max:500
Response: 수정된 페이지 정보 반환
DELETE /v1/item-master/pages/{id}
목적: 페이지 삭제 (Soft Delete)
Request: 없음
Response:
{
"success": true,
"message": "message.deleted"
}
참고:
- Soft Delete 처리 (
deleted_at업데이트) - 하위 섹션/필드도 함께 Soft Delete 처리 (Cascade)
4.3 섹션 관리
POST /v1/item-master/pages/{pageId}/sections
목적: 섹션 생성
Request Body:
{
"title": "기본 정보",
"type": "fields"
}
Validation:
title: required, string, max:255type: required, in:fields,bom
Response:
{
"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:
{
"title": "기본 정보 (수정)"
}
Validation:
title: string, max:255
Response: 수정된 섹션 정보 반환
DELETE /v1/item-master/sections/{id}
목적: 섹션 삭제 (Soft Delete)
Request: 없음
Response:
{
"success": true,
"message": "message.deleted"
}
참고: 하위 필드도 함께 Soft Delete 처리
PUT /v1/item-master/pages/{pageId}/sections/reorder
목적: 섹션 순서 변경 (드래그 앤 드롭)
Request Body:
{
"section_orders": [
{"id": 2, "order_no": 0},
{"id": 1, "order_no": 1},
{"id": 3, "order_no": 2}
]
}
Validation:
section_orders: required, arraysection_orders.*.id: required, exists:item_sections,idsection_orders.*.order_no: required, integer, min:0
Response:
{
"success": true,
"message": "message.reordered"
}
4.4 필드 관리
POST /v1/item-master/sections/{sectionId}/fields
목적: 필드 생성
Request Body:
{
"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:255field_type: required, in:textbox,number,dropdown,checkbox,date,textareais_required: booleanplaceholder: nullable, string, max:255validation_rules: nullable, jsonproperties: nullable, jsonoptions: nullable, json (field_type=dropdown일 때 필수)
Response:
{
"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:
{
"success": true,
"message": "message.deleted"
}
PUT /v1/item-master/sections/{sectionId}/fields/reorder
목적: 필드 순서 변경
Request Body:
{
"field_orders": [
{"id": 3, "order_no": 0},
{"id": 1, "order_no": 1},
{"id": 2, "order_no": 2}
]
}
Validation: 섹션 순서 변경과 동일
Response:
{
"success": true,
"message": "message.reordered"
}
4.5 BOM 관리
POST /v1/item-master/sections/{sectionId}/bom-items
목적: BOM 항목 생성
Request Body:
{
"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:100item_name: required, string, max:255quantity: required, numeric, min:0unit: nullable, string, max:50unit_price: nullable, numeric, min:0total_price: nullable, numeric, min:0spec: nullable, stringnote: nullable, string
Response:
{
"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:
{
"success": true,
"message": "message.deleted"
}
4.6 섹션 템플릿
GET /v1/item-master/section-templates
목적: 섹션 템플릿 목록 조회
Request: 없음
Response:
{
"success": true,
"message": "message.fetched",
"data": [
{
"id": 1,
"title": "기본 정보 템플릿",
"type": "fields",
"description": "제품 기본 정보 입력용 템플릿",
"is_default": true
}
]
}
POST /v1/item-master/section-templates
목적: 섹션 템플릿 생성
Request Body:
{
"title": "기본 정보 템플릿",
"type": "fields",
"description": "제품 기본 정보 입력용 템플릿",
"is_default": false
}
Validation:
title: required, string, max:255type: required, in:fields,bomdescription: nullable, stringis_default: boolean
Response: 생성된 템플릿 정보 반환
PUT /v1/item-master/section-templates/{id}
목적: 섹션 템플릿 수정
Request Body: 생성과 동일 (모든 필드 선택적)
Response: 수정된 템플릿 정보 반환
DELETE /v1/item-master/section-templates/{id}
목적: 섹션 템플릿 삭제 (Soft Delete)
Request: 없음
Response:
{
"success": true,
"message": "message.deleted"
}
4.7 마스터 필드
GET /v1/item-master/master-fields
목적: 마스터 필드 목록 조회
Query Parameters:
| 파라미터 | 타입 | 필수 | 설명 |
|---|---|---|---|
category |
string | 선택 | 카테고리 필터 |
Response:
{
"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:
{
"field_name": "제품명",
"field_type": "textbox",
"category": "기본정보",
"description": "제품 이름",
"is_common": true,
"validation_rules": {"max": 100}
}
Validation:
field_name: required, string, max:255field_type: required, in:textbox,number,dropdown,checkbox,date,textareacategory: nullable, string, max:100description: nullable, stringis_common: booleanoptions: nullable, jsonvalidation_rules: nullable, jsonproperties: nullable, json
Response: 생성된 마스터 필드 정보 반환
PUT /v1/item-master/master-fields/{id}
목적: 마스터 필드 수정
Request Body: 생성과 동일 (모든 필드 선택적)
Response: 수정된 마스터 필드 정보 반환
DELETE /v1/item-master/master-fields/{id}
목적: 마스터 필드 삭제 (Soft Delete)
Request: 없음
Response:
{
"success": true,
"message": "message.deleted"
}
4.8 커스텀 탭
GET /v1/item-master/custom-tabs
목적: 커스텀 탭 목록 조회
Request: 없음
Response:
{
"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:
{
"label": "품질",
"icon": "quality-icon",
"is_default": false
}
Validation:
label: required, string, max:255icon: nullable, string, max:100is_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:
{
"success": true,
"message": "message.deleted"
}
PUT /v1/item-master/custom-tabs/reorder
목적: 탭 순서 변경
Request Body:
{
"tab_orders": [
{"id": 2, "order_no": 0},
{"id": 1, "order_no": 1}
]
}
Validation: 섹션 순서 변경과 동일
Response:
{
"success": true,
"message": "message.reordered"
}
PUT /v1/item-master/custom-tabs/{id}/columns
목적: 탭별 컬럼 설정
Request Body:
{
"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, arraycolumns.*.key: required, stringcolumns.*.label: required, stringcolumns.*.visible: required, booleancolumns.*.order: required, integer
Response:
{
"success": true,
"message": "message.updated",
"data": {
"tab_id": 1,
"columns": [...]
}
}
4.9 단위 관리
GET /v1/item-master/units
목적: 단위 목록 조회
Request: 없음
Response:
{
"success": true,
"message": "message.fetched",
"data": [
{"id": 1, "label": "킬로그램", "value": "kg"},
{"id": 2, "label": "미터", "value": "m"}
]
}
POST /v1/item-master/units
목적: 단위 생성
Request Body:
{
"label": "킬로그램",
"value": "kg"
}
Validation:
label: required, string, max:100value: required, string, max:50
Response: 생성된 단위 정보 반환
DELETE /v1/item-master/units/{id}
목적: 단위 삭제 (Soft Delete)
Request: 없음
Response:
{
"success": true,
"message": "message.deleted"
}
5. 요청/응답 예시
5.1 페이지 생성 → 섹션 추가 → 필드 추가 흐름
1단계: 페이지 생성
POST /v1/item-master/pages
{
"page_name": "완제품 A",
"item_type": "FG",
"absolute_path": "/FG/완제품 A"
}
→ Response: {"data": {"id": 1, ...}}
2단계: 섹션 추가
POST /v1/item-master/pages/1/sections
{
"title": "기본 정보",
"type": "fields"
}
→ Response: {"data": {"id": 1, "page_id": 1, ...}}
3단계: 필드 추가
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 섹션 생성
POST /v1/item-master/pages/1/sections
{
"title": "자재 목록",
"type": "bom"
}
→ Response: {"data": {"id": 2, "type": "bom", ...}}
2단계: BOM 항목 추가
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 에러 응답 형식
{
"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 격리 에러
{
"success": false,
"message": "error.forbidden",
"errors": {
"tenant_id": ["다른 테넌트의 리소스에 접근할 수 없습니다."]
}
}
참고: BelongsToTenant 스코프가 자동으로 처리하므로 404 반환
7. 구현 우선순위
🔴 우선순위 1 (필수 - 화면 기본 동작)
- 초기화 API:
GET /v1/item-master/init - 페이지 CRUD:
GET /v1/item-master/pagesPOST /v1/item-master/pagesPUT /v1/item-master/pages/{id}DELETE /v1/item-master/pages/{id}
- 섹션 CRUD:
POST /v1/item-master/pages/{pageId}/sectionsPUT /v1/item-master/sections/{id}DELETE /v1/item-master/sections/{id}
- 필드 CRUD:
POST /v1/item-master/sections/{sectionId}/fieldsPUT /v1/item-master/fields/{id}DELETE /v1/item-master/fields/{id}
🟡 우선순위 2 (중요 - 핵심 기능)
- BOM 관리:
POST /v1/item-master/sections/{sectionId}/bom-itemsPUT /v1/item-master/bom-items/{id}DELETE /v1/item-master/bom-items/{id}
- 순서 변경:
PUT /v1/item-master/pages/{pageId}/sections/reorderPUT /v1/item-master/sections/{sectionId}/fields/reorder
- 단위 관리:
GET /v1/item-master/unitsPOST /v1/item-master/unitsDELETE /v1/item-master/units/{id}
🟢 우선순위 3 (부가 기능)
- 섹션 템플릿: 전체 CRUD
- 마스터 필드: 전체 CRUD
- 커스텀 탭: 전체 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, deletedbefore,after필드에 변경 전후 데이터 JSON 저장
5. i18n 메시지 키
// 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 다음 리뷰 예정일: 백엔드 구현 완료 후