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

30 KiB

품목기준관리 API 명세서

작성일: 2025-11-20 버전: v1.0 작성자: 프론트엔드 개발팀 수신: 백엔드 개발팀


📋 목차

  1. 개요
  2. 인증 및 공통 사항
  3. 데이터베이스 테이블 정의
  4. API 엔드포인트
  5. 요청/응답 예시
  6. 에러 처리
  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 공통 응답 형식

성공 응답:

{
  "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": [...]
  }
}

참고:

  • pagessections, 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:255
  • item_type: required, in:FG,PT,SM,RM,CS
  • absolute_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:255
  • absolute_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:255
  • type: 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, array
  • section_orders.*.id: required, exists:item_sections,id
  • section_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: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:

{
  "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: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:

{
  "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: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:

{
  "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: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:

{
  "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: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:

{
  "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, array
  • columns.*.key: required, string
  • columns.*.label: required, string
  • columns.*.visible: required, boolean
  • columns.*.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:100
  • value: 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 (필수 - 화면 기본 동작)

  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 (중요 - 핵심 기능)

  1. BOM 관리:
    • POST /v1/item-master/sections/{sectionId}/bom-items
    • PUT /v1/item-master/bom-items/{id}
    • DELETE /v1/item-master/bom-items/{id}
  2. 순서 변경:
    • PUT /v1/item-master/pages/{pageId}/sections/reorder
    • PUT /v1/item-master/sections/{sectionId}/fields/reorder
  3. 단위 관리:
    • GET /v1/item-master/units
    • POST /v1/item-master/units
    • DELETE /v1/item-master/units/{id}

🟢 우선순위 3 (부가 기능)

  1. 섹션 템플릿: 전체 CRUD
  2. 마스터 필드: 전체 CRUD
  3. 커스텀 탭: 전체 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 메시지 키

// 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 다음 리뷰 예정일: 백엔드 구현 완료 후