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

12 KiB

품목기준관리(ItemMaster) 프론트엔드 가이드

📌 품목설정 시스템의 구조, API, 잠금 기능에 대한 프론트엔드 개발 가이드


1. 개요

품목기준관리(ItemMaster)는 제품의 입력 화면을 구성하는 페이지-섹션-필드 구조를 관리하는 시스템입니다.

1.1 핵심 개념

엔티티 설명 예시
Page 품목 유형별 화면 (FG, PT, SM, RM, CS) "완제품 기본정보", "부품 상세"
Section 페이지 내 논리적 영역 "제품 상세", "BOM 정보"
Field 섹션 내 입력 항목 "제품명", "규격", "단가"
BomItem BOM 섹션 내 부품 항목 "부품A x 2개"

1.2 아키텍처 특징

  • 독립 엔티티 구조: 섹션, 필드, BOM은 독립적으로 존재하며 재사용 가능
  • 링크 테이블: entity_relationships로 관계 관리
  • 연결 잠금: 중요한 구조는 잠금으로 보호 가능

2. 데이터 구조

2.1 엔티티 관계도

┌─────────────────────────────────────────────────────────────────┐
│                         ItemPage                                │
│  (id, page_name, item_type, is_active)                         │
└─────────────┬──────────────────────────────────────────────────┘
              │ entity_relationships (is_locked)
              ▼
┌─────────────────────────────────────────────────────────────────┐
│                        ItemSection                              │
│  (id, title, type, is_template, is_default)                    │
└─────────────┬───────────────────────────────────┬──────────────┘
              │ entity_relationships              │ entity_relationships
              │ (is_locked)                       │ (is_locked)
              ▼                                   ▼
┌─────────────────────────────┐     ┌─────────────────────────────┐
│         ItemField           │     │        ItemBomItem          │
│  (id, field_name,           │     │  (id, item_code,            │
│   field_type, is_required)  │     │   item_name, quantity)      │
└─────────────────────────────┘     └─────────────────────────────┘

2.2 entity_relationships 테이블

모든 엔티티 간 관계는 entity_relationships 테이블로 관리됩니다.

interface EntityRelationship {
  id: number;
  tenant_id: number;
  group_id: number;           // 1: ItemMaster
  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;
}

3. 잠금(Lock) 기능

3.1 잠금의 의미

연결이 잠기면:

  • 해당 연결(관계)를 해제할 수 없음
  • 연결된 자식 엔티티를 삭제할 수 없음
  • 이름 변경, 속성 추가 등 비구조적 수정은 허용
예시: page→section 연결이 잠김
├─ ❌ 섹션을 페이지에서 분리할 수 없음
├─ ❌ 해당 섹션을 삭제할 수 없음
├─ ✅ 섹션 제목 변경 가능
└─ ✅ 섹션에 새 필드 추가 가능

3.2 잠금 상태 확인

init API 응답에 is_locked 필드가 포함됩니다.

// GET /api/v1/item-master/init 응답
{
  "pages": [
    {
      "id": 1,
      "page_name": "완제품 기본정보",
      "sections": [
        {
          "id": 10,
          "title": "제품 상세",
          "is_locked": true,    // ⭐ 이 연결이 잠김
          "fields": [
            {
              "id": 100,
              "field_name": "제품명",
              "is_locked": false  // ⭐ 이 연결은 잠기지 않음
            }
          ]
        }
      ]
    }
  ]
}

3.3 프론트엔드 처리 가이드

// 잠금 상태에 따른 UI 처리 예시
interface SectionProps {
  section: ItemSection;
  isLocked: boolean;  // 부모로부터 전달받은 잠금 상태
}

function SectionItem({ section, isLocked }: SectionProps) {
  return (
    <div className={isLocked ? 'locked-section' : ''}>
      <h3>
        {section.title}
        {isLocked && <LockIcon />}  {/* 잠금 아이콘 표시 */}
      </h3>

      {/* 잠금 시 삭제/분리 버튼 비활성화 */}
      <button
        disabled={isLocked}
        onClick={handleUnlink}
      >
        분리
      </button>

      <button
        disabled={isLocked}
        onClick={handleDelete}
      >
        삭제
      </button>

      {/* 수정은 항상 가능 */}
      <button onClick={handleEdit}>
        수정
      </button>
    </div>
  );
}

3.4 잠금 관련 에러 처리

잠금된 항목에 대해 삭제/해제 시도 시 에러가 반환됩니다.

// 에러 응답 예시
{
  "success": false,
  "message": "잠금된 연결은 해제할 수 없습니다.",
  "error": "relationship_locked"
}

// 프론트엔드 에러 처리
try {
  await deleteSection(sectionId);
} catch (error) {
  if (error.response?.data?.error === 'entity_protected_by_locked_relationship') {
    toast.error('잠금된 연결로 보호된 항목은 삭제할 수 없습니다.');
  }
}

4. API 엔드포인트

4.1 초기화 API

Method Endpoint 설명
GET /api/v1/item-master/init 전체 데이터 로드 (페이지, 섹션, 커스텀탭, 단위옵션)

응답 구조:

interface InitResponse {
  pages: ItemPage[];          // 페이지 + 연결된 섹션/필드
  sections: ItemSection[];    // 모든 독립 섹션 (재사용 풀)
  customTabs: CustomTab[];
  unitOptions: UnitOption[];
}

4.2 페이지 API

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} 페이지 삭제

4.3 섹션 API

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 사용처 조회

4.4 필드 API

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 사용처 조회

4.5 BOM API

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 삭제

4.6 순서 변경 API

Method Endpoint 설명
PUT /api/v1/item-master/pages/{pageId}/sections/reorder 섹션 순서 변경
PUT /api/v1/item-master/sections/{sectionId}/fields/reorder 필드 순서 변경

5. 필드 타입

5.1 지원 필드 타입

field_type 설명 렌더링 컴포넌트
textbox 텍스트 입력 <Input type="text" />
number 숫자 입력 <Input type="number" />
dropdown 드롭다운 선택 <Select />
checkbox 체크박스 <Checkbox />
date 날짜 선택 <DatePicker />
textarea 장문 텍스트 <Textarea />

5.2 필드 속성

interface ItemField {
  id: number;
  field_name: string;
  field_type: FieldType;
  order_no: number;
  is_required: boolean;
  is_locked?: boolean;        // init 응답에 포함
  default_value?: string;
  placeholder?: string;
  display_condition?: object; // 조건부 표시
  validation_rules?: object;  // 유효성 검사 규칙
  options?: object;           // dropdown 옵션 등
  properties?: object;        // 추가 설정
  category?: string;
  description?: string;
  is_common?: boolean;
}

6. 상태 관리 권장 패턴

6.1 Zustand Store 예시

interface ItemMasterStore {
  // 데이터
  pages: ItemPage[];
  sections: ItemSection[];
  customTabs: CustomTab[];
  unitOptions: UnitOption[];

  // 상태
  isLoading: boolean;
  selectedPageId: number | null;
  selectedSectionId: number | null;

  // 액션
  fetchInit: () => Promise<void>;
  addSection: (pageId: number, data: CreateSectionDTO) => Promise<void>;
  updateSection: (id: number, data: UpdateSectionDTO) => Promise<void>;
  deleteSection: (id: number) => Promise<void>;
  reorderSections: (pageId: number, items: ReorderItem[]) => Promise<void>;

  // 유틸리티
  getSectionsByPage: (pageId: number) => ItemSection[];
  isEntityLocked: (entityType: string, entityId: number) => boolean;
}

6.2 잠금 상태 캐싱

// 잠금 상태를 Map으로 캐싱
const lockStatusMap = new Map<string, boolean>();

function buildLockStatusMap(pages: ItemPage[]) {
  pages.forEach(page => {
    page.sections?.forEach(section => {
      lockStatusMap.set(`section:${section.id}`, section.is_locked);

      section.fields?.forEach(field => {
        lockStatusMap.set(`field:${field.id}`, field.is_locked);
      });

      section.bom_items?.forEach(bom => {
        lockStatusMap.set(`bom:${bom.id}`, bom.is_locked);
      });
    });
  });
}

function isLocked(entityType: string, entityId: number): boolean {
  return lockStatusMap.get(`${entityType}:${entityId}`) || false;
}

7. 주의사항

7.1 삭제 시 동작

  • 페이지 삭제: 연결된 섹션/필드는 삭제되지 않고 관계만 해제
  • 섹션 삭제: 연결된 필드/BOM은 삭제되지 않고 관계만 해제
  • 잠금된 연결이 있으면: 삭제/해제 불가

7.2 복제(Clone) 시 동작

  • 섹션 복제: 섹션 + 필드 + BOM 모두 복제
  • 필드 복제: 필드만 복제
  • 복제된 항목은 독립 엔티티로 생성됨

7.3 순서 변경

  • Drag & Drop 후 reorder API 호출 필요
  • items 배열에 {id, order_no} 형태로 전달

8. 변경 이력

날짜 버전 변경 내용
2025-11-27 1.0.0 잠금(Lock) 기능 추가
2025-11-26 0.9.0 독립 엔티티 아키텍처 적용
2025-11-20 0.8.0 entity_relationships 링크 테이블 도입