Files
sam-api/docs/analysis/SAM_Item_Management_DB_Modeling_Analysis.md
hskwon 757c5d901c docs: API 문서 구조화 및 분석 문서 추가
- docs/INDEX.md: 문서 인덱스 추가
- docs/analysis/: Item DB/API 분석 문서 3종 추가
- docs/swagger/: Swagger 문서화 가이드 4종 추가
- LOGICAL_RELATIONSHIPS.md: 논리적 관계 문서 업데이트
- 이전 버전 문서 정리 (BP-MES, CHECKPOINT 등)
2025-11-24 22:38:38 +09:00

51 KiB

SAM 품목관리 시스템 DB 모델링 분석 리포트

분석일: 2025-11-10 분석자: Claude Code 분석 범위: React Frontend (ItemMaster) ↔ Laravel API Backend (materials, products, BOM)


📋 Executive Summary

SAM 품목관리 시스템은 제조업 MES의 핵심인 품목(Item) 및 BOM(Bill of Materials) 관리를 담당합니다. 본 분석에서는 React 프론트엔드의 데이터 구조와 Laravel API 백엔드의 DB 스키마 간 매핑을 검증하고, 구조적 문제점과 개선 방향을 제시합니다.

핵심 발견사항

잘 설계된 부분:

  • 통합 참조 구조 (ref_type + ref_id)로 확장성 확보
  • 설계 워크플로우 분리 (models → model_versions → bom_templates)
  • 멀티테넌트 격리 및 감사 로그 일관성

⚠️ 개선 필요 부분:

  1. 프론트-백엔드 타입 불일치: ItemMaster의 itemType (5가지) vs 백엔드 분리 (products + materials)
  2. BOM 구조 이원화: product_components (실제 BOM) vs bom_template_items (설계 템플릿) 간 관계 모호
  3. 규격 정보 분산: SpecificationMaster (프론트) vs materials.item_name (백엔드)
  4. 계산식 필드 복잡도: bom_templates.calculation_schemabom_template_items.calculation_formula 간 정합성 검증 부재

1. 현재 구조 개요

1.1 프론트엔드 데이터 구조 (React TypeScript)

ItemMaster 인터페이스

export interface ItemMaster {
  id: string;
  itemCode: string;
  itemName: string;
  itemType: 'FG' | 'PT' | 'SM' | 'RM' | 'CS'; // 제품, 부품, 부자재, 원자재, 소모품
  productCategory?: 'SCREEN' | 'STEEL'; // 제품 카테고리 (스크린/철재)
  partType?: 'ASSEMBLY' | 'BENDING' | 'PURCHASED'; // 부품 유형
  partUsage?: 'GUIDE_RAIL' | 'BOTTOM_FINISH' | 'CASE' | 'DOOR' | 'BRACKET' | 'GENERAL';
  unit: string;
  category1?: string;
  category2?: string;
  category3?: string;
  specification?: string;
  isVariableSize?: boolean;
  isActive?: boolean;
  lotAbbreviation?: string; // 로트 약자 (제품만)
  purchasePrice?: number;
  marginRate?: number;
  processingCost?: number;
  laborCost?: number;
  installCost?: number;
  salesPrice?: number;
  safetyStock?: number;
  leadTime?: number;
  bom?: BOMLine[]; // 부품구성표
  bomCategories?: string[]; // 견적산출용 BOM 카테고리
  // 인정 정보
  certificationNumber?: string;
  certificationStartDate?: string;
  certificationEndDate?: string;
}

BOMLine 인터페이스

export interface BOMLine {
  id: string;
  childItemCode: string; // 구성 품목 코드
  childItemName: string; // 구성 품목명
  quantity: number; // 기준 수량
  unit: string; // 단위
  unitPrice?: number; // 단가
  quantityFormula?: string; // 수량 계산식 (예: "W * 2", "H + 100")
  note?: string; // 비고
  // 절곡품 관련
  isBending?: boolean;
  bendingDiagram?: string; // 전개도 이미지 URL
  bendingDetails?: BendingDetail[];
}

MaterialItemName 인터페이스

export interface MaterialItemName {
  id: string;
  itemType: 'RM' | 'SM'; // 원자재 | 부자재
  itemName: string; // 품목명 (예: "SPHC-SD", "STS430")
  category?: string; // 분류 (예: "냉연", "열연", "스테인리스")
  description?: string;
  isActive: boolean;
  createdAt: string;
  updatedAt?: string;
}

SpecificationMaster 인터페이스

export interface SpecificationMaster {
  id: string;
  specificationCode: string; // 규격 코드 (예: 1.6T x 1219 x 2438)
  itemType: 'RM' | 'SM';
  fieldCount: '1' | '2' | '3'; // 너비 입력 개수
  thickness: string;
  widthA: string;
  widthB?: string;
  widthC?: string;
  length: string;
  description?: string;
  isActive: boolean;
  createdAt?: string;
  updatedAt?: string;
}

1.2 백엔드 DB 스키마 (Laravel Migrations)

1.2.1 materials 테이블

-- 주요 필드 (마이그레이션 기반 재구성)
CREATE TABLE materials (
  id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  tenant_id BIGINT UNSIGNED NOT NULL COMMENT '테넌트 ID',
  category_id BIGINT UNSIGNED NULL COMMENT '카테고리 ID',
  name VARCHAR(255) NOT NULL COMMENT '자재명',
  item_name VARCHAR(255) NULL COMMENT '품목명 (자재명+규격정보)',
  -- 기타 공통 필드
  created_by BIGINT UNSIGNED NULL,
  updated_by BIGINT UNSIGNED NULL,
  deleted_by BIGINT UNSIGNED NULL,
  created_at TIMESTAMP NULL,
  updated_at TIMESTAMP NULL,
  deleted_at TIMESTAMP NULL,

  INDEX (tenant_id),
  INDEX (category_id)
);

특징:

  • 자재 전용 테이블 (원자재/부자재 구분은 category_id로 관리)
  • item_name: 규격 정보가 포함된 품목명 (예: "SPHC-SD 1.6T x 1219 x 2438")
  • 동적 속성 미지원 (고정 스키마)

1.2.2 products 테이블

-- 주요 필드 (finalize_categories_products 마이그레이션 기반)
CREATE TABLE products (
  id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  tenant_id BIGINT UNSIGNED NOT NULL COMMENT '테넌트 ID',
  code VARCHAR(30) NOT NULL COMMENT '제품 코드',
  name VARCHAR(100) NOT NULL COMMENT '제품명',
  category_id BIGINT UNSIGNED NOT NULL COMMENT '카테고리 ID',
  product_type VARCHAR(30) DEFAULT 'PRODUCT' COMMENT 'PRODUCT/PART/SUBASSEMBLY',
  unit VARCHAR(20) NULL COMMENT '단위',
  description VARCHAR(255) NULL,
  is_sellable TINYINT(1) DEFAULT 1 COMMENT '판매가능',
  is_purchasable TINYINT(1) DEFAULT 0 COMMENT '구매가능',
  is_producible TINYINT(1) DEFAULT 1 COMMENT '제조가능',
  -- 감사 필드
  created_by BIGINT UNSIGNED NULL,
  updated_by BIGINT UNSIGNED NULL,
  deleted_by BIGINT UNSIGNED NULL,
  created_at TIMESTAMP NULL,
  updated_at TIMESTAMP NULL,
  deleted_at TIMESTAMP NULL,

  UNIQUE KEY uq_tenant_code (tenant_id, code),
  INDEX (tenant_id, category_id),
  FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE RESTRICT
);

특징:

  • 제품/부품/서브어셈블리 통합 관리
  • product_type: PRODUCT/PART/SUBASSEMBLY 등 (common_codes 참조)
  • 판매/구매/제조 가능 여부 플래그 지원
  • unit 필드 추가 (2025_08_26 마이그레이션)

1.2.3 product_components 테이블 (BOM 자기참조)

-- 최신 버전 (alter_product_components_unify_ref_columns)
CREATE TABLE product_components (
  id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  tenant_id BIGINT UNSIGNED NOT NULL COMMENT '테넌트 ID',
  parent_product_id BIGINT UNSIGNED NOT NULL COMMENT '상위 제품 ID',
  category_id BIGINT UNSIGNED NULL COMMENT '프론트 카테고리 ID(선택)',
  category_name VARCHAR(100) NULL COMMENT '프론트 카테고리명(선택)',
  ref_type VARCHAR(20) NOT NULL COMMENT 'MATERIAL | PRODUCT',
  ref_id BIGINT UNSIGNED NOT NULL COMMENT '참조 ID (materials.id 또는 products.id)',
  quantity DECIMAL(18,6) NOT NULL DEFAULT 0 COMMENT '수량',
  sort_order INT DEFAULT 0,
  -- 감사 필드
  created_by BIGINT UNSIGNED NULL,
  updated_by BIGINT UNSIGNED NULL,
  deleted_at TIMESTAMP NULL,
  created_at TIMESTAMP NULL,
  updated_at TIMESTAMP NULL,

  INDEX idx_tenant_parent (tenant_id, parent_product_id),
  INDEX idx_tenant_ref (tenant_id, ref_type, ref_id),
  INDEX idx_tenant_category (tenant_id, category_id),
  INDEX idx_tenant_sort (tenant_id, sort_order)
);

특징:

  • 통합 참조 구조: ref_type (MATERIAL|PRODUCT) + ref_id로 materials/products 모두 참조 가능
  • FK 최소화 정책: 인덱스만 생성, FK 제약 조건 제거 (성능 우선)
  • 카테고리 메타: 프론트엔드 UI용 category_id, category_name 캐싱
  • 정밀도 확장: DECIMAL(18,6) → 소수점 6자리 지원

1.2.4 models 테이블 (설계 모델)

CREATE TABLE models (
  id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  tenant_id BIGINT UNSIGNED NOT NULL COMMENT '테넌트ID',
  code VARCHAR(100) NOT NULL COMMENT '모델코드(설계코드)',
  name VARCHAR(200) NOT NULL COMMENT '모델명',
  category_id BIGINT UNSIGNED NULL COMMENT '카테고리ID(참조용, FK 미설정)',
  lifecycle VARCHAR(30) NULL COMMENT 'PLANNING/ACTIVE/DEPRECATED 등',
  description TEXT NULL,
  is_active BOOLEAN DEFAULT TRUE,
  created_at TIMESTAMP NULL,
  updated_at TIMESTAMP NULL,
  deleted_at TIMESTAMP NULL,

  UNIQUE KEY uq_models_tenant_code (tenant_id, code),
  INDEX idx_models_tenant_active (tenant_id, is_active),
  INDEX idx_models_tenant_category (tenant_id, category_id)
);

특징:

  • 설계 모델 마스터 (제품 계열별 설계)
  • lifecycle: PLANNING → ACTIVE → DEPRECATED 워크플로우 지원

1.2.5 model_versions 테이블

CREATE TABLE model_versions (
  id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  tenant_id BIGINT UNSIGNED NOT NULL COMMENT '테넌트ID',
  model_id BIGINT UNSIGNED NOT NULL COMMENT '모델ID',
  version_no INT NOT NULL COMMENT '버전번호(1..N)',
  status VARCHAR(30) DEFAULT 'DRAFT' COMMENT 'DRAFT/RELEASED',
  effective_from DATETIME NULL,
  effective_to DATETIME NULL,
  notes TEXT NULL,
  is_active BOOLEAN DEFAULT TRUE,
  created_at TIMESTAMP NULL,
  updated_at TIMESTAMP NULL,
  deleted_at TIMESTAMP NULL,

  UNIQUE KEY uq_model_versions_model_ver (model_id, version_no),
  INDEX idx_mv_tenant_status (tenant_id, status),
  INDEX idx_mv_tenant_model (tenant_id, model_id)
);

특징:

  • 모델 버전 관리 (DRAFT → RELEASED)
  • 유효기간 관리 (effective_from/to)
  • 버전별 독립 BOM 템플릿 지원

1.2.6 bom_templates 테이블

CREATE TABLE bom_templates (
  id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  tenant_id BIGINT UNSIGNED NOT NULL COMMENT '테넌트ID',
  model_version_id BIGINT UNSIGNED NOT NULL COMMENT '모델버전ID',
  name VARCHAR(100) DEFAULT 'Main' COMMENT '템플릿명',
  is_primary BOOLEAN DEFAULT TRUE COMMENT '대표 템플릿 여부',
  notes TEXT NULL,
  -- 계산식 관련 (add_calculation_fields 마이그레이션)
  calculation_schema JSON NULL COMMENT '견적 파라미터 스키마',
  company_type VARCHAR(50) DEFAULT 'default' COMMENT '업체 타입',
  formula_version VARCHAR(10) DEFAULT 'v1.0' COMMENT '산출식 버전',
  created_at TIMESTAMP NULL,
  updated_at TIMESTAMP NULL,
  deleted_at TIMESTAMP NULL,

  UNIQUE KEY uq_bomtpl_mv_name (model_version_id, name),
  INDEX idx_bomtpl_tenant_mv (tenant_id, model_version_id),
  INDEX idx_bomtpl_tenant_primary (tenant_id, is_primary)
);

특징:

  • 모델 버전별 BOM 템플릿 (설계 단계)
  • calculation_schema: 견적 파라미터 스키마 (W0, H0, 설치 타입 등)
  • 업체별 커스터마이징 지원 (company_type)

1.2.7 bom_template_items 테이블

CREATE TABLE bom_template_items (
  id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  tenant_id BIGINT UNSIGNED NOT NULL COMMENT '테넌트ID',
  bom_template_id BIGINT UNSIGNED NOT NULL COMMENT 'BOM템플릿ID',
  ref_type VARCHAR(20) NOT NULL COMMENT 'MATERIAL|PRODUCT',
  ref_id BIGINT UNSIGNED NOT NULL COMMENT '참조ID',
  qty DECIMAL(18,6) DEFAULT 1 COMMENT '수량',
  waste_rate DECIMAL(9,6) DEFAULT 0 COMMENT '로스율',
  uom_id BIGINT UNSIGNED NULL COMMENT '단위ID',
  notes VARCHAR(255) NULL,
  sort_order INT DEFAULT 0,
  -- 계산식 관련 (add_calculation_fields 마이그레이션)
  is_calculated BOOLEAN DEFAULT FALSE COMMENT '계산식 적용 여부',
  calculation_formula TEXT NULL COMMENT '계산식 표현식',
  depends_on JSON NULL COMMENT '의존 파라미터 목록',
  calculation_config JSON NULL COMMENT '계산 설정',
  created_at TIMESTAMP NULL,
  updated_at TIMESTAMP NULL,

  INDEX idx_bomtpl_items_tenant_tpl (tenant_id, bom_template_id),
  INDEX idx_bomtpl_items_tenant_ref (tenant_id, ref_type, ref_id),
  INDEX idx_bomtpl_items_sort (bom_template_id, sort_order)
);

특징:

  • 템플릿 BOM 항목 (설계 단계)
  • calculation_formula: 동적 수량 계산식 (예: "W * 2 + 100")
  • depends_on: 의존 파라미터 명시 (예: ["W0", "H0"])
  • waste_rate: 로스율 반영

2. 프론트-백엔드 매핑 분석

2.1 매핑 현황

React 필드 (ItemMaster) API 필드 테이블 매핑 상태 비고
id id products BIGINT → string 변환
itemCode code products 필드명 불일치
itemName name products 필드명 불일치
itemType - - 불일치: FG/PT/SM/RM/CS vs products/materials 분리
productCategory ? products ⚠️ SCREEN/STEEL → category_id 또는 product_type?
partType ? products ⚠️ ASSEMBLY/BENDING/PURCHASED → product_type?
partUsage ? products ⚠️ GUIDE_RAIL 등 → 추가 필드 필요
unit unit products 추가됨 (2025_08_26)
category1/2/3 category_id products ⚠️ 계층 구조 vs 단일 참조
specification description? products ⚠️ 규격 정보 별도 필드 부재
isVariableSize - - 가변 사이즈 플래그 미지원
isActive is_active products (soft delete) deleted_at으로 구현
lotAbbreviation - - 로트 약자 필드 부재
purchasePrice ? - 구매가 필드 부재
marginRate ? - 마진율 필드 부재
processingCost ? - 가공비 필드 부재
laborCost ? - 인건비 필드 부재
installCost ? - 설치비 필드 부재
salesPrice ? - 판매가 필드 부재
safetyStock ? - 안전재고 필드 부재
leadTime ? - 리드타임 필드 부재
bom - product_components 관계 (hasMany)
bomCategories category_id/name product_components 캐싱 필드 지원
certificationNumber - - 인정번호 필드 부재
certificationStartDate - - 인정 유효기간 부재
certificationEndDate - - 인정 유효기간 부재

BOMLine vs product_components 매핑

React 필드 (BOMLine) API 필드 테이블 매핑 상태 비고
id id product_components -
childItemCode - - code는 ref_id로 조인 필요
childItemName - - name은 ref_id로 조인 필요
quantity quantity product_components DECIMAL(18,6)
unit - - unit은 ref_id로 조인 필요
unitPrice - - 단가 필드 부재
quantityFormula calculation_formula? bom_template_items ⚠️ 템플릿에만 존재, 실제 BOM에는 부재
note notes? product_components notes 필드 부재
isBending - - 절곡품 플래그 부재
bendingDiagram - - 전개도 이미지 URL 부재
bendingDetails - - 전개도 상세 데이터 부재

MaterialItemName vs materials 매핑

React 필드 (MaterialItemName) API 필드 테이블 매핑 상태 비고
id id materials -
itemType - - RM/SM 구분 필드 부재
itemName item_name materials 규격 포함 품목명
category category_id materials ⚠️ ID vs 이름 불일치
description ? materials description 필드 미확인
isActive is_active materials soft delete
createdAt created_at materials -
updatedAt updated_at materials -

SpecificationMaster 매핑

React 필드 (SpecificationMaster) API 필드 테이블 매핑 상태 비고
전체 인터페이스 - - 백엔드에 대응 테이블 없음
specificationCode ? materials.item_name ⚠️ 문자열로 저장 (파싱 필요)
thickness/widthA/B/C/length - - 구조화된 규격 필드 부재

2.2 불일치 사항 요약

🔴 Critical: 즉시 해결 필요

  1. ItemType 매핑 부재: 프론트 itemType (FG/PT/SM/RM/CS) ↔ 백엔드 products/materials 분리 구조
  2. 가격 정보 필드 전체 부재: purchasePrice, salesPrice, marginRate, processingCost 등 7개 필드
  3. SpecificationMaster 테이블 부재: 구조화된 규격 관리 불가
  4. 절곡품 정보 부재: isBending, bendingDiagram, bendingDetails 등

🟡 Important: 중요도 높음

  1. BOMLine 계산식 필드 불일치: quantityFormula (프론트) vs calculation_formula (bom_template_items만)
  2. 부품 세부 분류 필드 부재: partType, partUsage
  3. 인정 정보 필드 부재: certificationNumber, certificationStartDate/EndDate
  4. 안전재고/리드타임 부재: safetyStock, leadTime

🟢 Low: 개선 권장

  1. 필드명 일관성: itemCode vs code, itemName vs name
  2. 카테고리 계층 구조: category1/2/3 vs category_id 단일 참조
  3. BOM notes 필드 부재: product_components에 메모 필드 없음

3. 문제점 및 이슈

3.1 구조적 문제

문제 1: 품목 타입 분리의 불일치

현상:

  • 프론트엔드는 단일 ItemMaster 인터페이스에서 itemType으로 5가지 타입 구분
  • 백엔드는 products (FG/PT) vs materials (SM/RM/CS)로 테이블 분리

영향:

  • API 응답 구조가 타입별로 달라짐 (GET /products vs GET /materials)
  • 프론트엔드에서 타입별 분기 처리 필요
  • BOM 조회 시 products와 materials 각각 조인 필요

근본 원인:

  • 설계 초기 도메인 모델 불일치
  • products: "제조하는 것"
  • materials: "구매하는 것"
  • 실제 비즈니스: 부자재(SM)도 제조 가능, 부품(PT)도 구매 가능

문제 2: BOM 구조 이원화의 혼란

현상:

  • bom_templates / bom_template_items: 설계 단계 BOM (파라미터 기반 계산식 포함)
  • product_components: 실제 제품 BOM (고정 수량)

문제점:

  1. 계산식 필드 불일치:

    • bom_template_items.calculation_formula (설계 템플릿)
    • product_components에는 계산식 필드 없음
    • 프론트 BOMLine.quantityFormula는 어느 것을 참조?
  2. 데이터 동기화 불명확:

    • 템플릿 BOM → 실제 BOM 변환 로직 미정의
    • 템플릿 수정 시 기존 제품 BOM 업데이트 전략 부재
  3. relation 정의 모호:

    • models → model_versions → bom_templates → bom_template_items (설계)
    • products → product_components (실제)
    • 둘 간의 연결고리 (어떤 모델 버전에서 생성?) 부재

문제 3: 규격 정보 분산 및 정규화 부족

현상:

  • materials.item_name: "SPHC-SD 1.6T x 1219 x 2438" (문자열 결합)
  • 프론트 SpecificationMaster: 구조화된 thickness/width/length 필드

문제점:

  • 규격 검색 어려움 (문자열 LIKE 검색만 가능)
  • 규격별 통계/집계 불가
  • 두께/너비/길이 범위 쿼리 불가
  • 규격 변경 이력 추적 불가

문제 4: 가격 정보 테이블 부재

현상:

  • 프론트 ItemMaster에는 7개 가격 필드 존재
  • 백엔드에는 대응 필드 전혀 없음

문제점:

  • 구매가/판매가 이력 관리 불가
  • 원가 계산 로직 구현 불가
  • 견적 산출 시 가격 정보 누락

3.2 성능 문제

문제 5: BOM 조회 시 N+1 쿼리

현상:

-- 1개 제품의 BOM 조회 시
SELECT * FROM product_components WHERE parent_product_id = ?;
-- 각 component마다
SELECT * FROM products WHERE id = ?; -- ref_type=PRODUCT인 경우
SELECT * FROM materials WHERE id = ?; -- ref_type=MATERIAL인 경우

해결 방안:

  • Eager Loading 전략 필요
  • with(['product', 'material']) 관계 정의 필요

문제 6: 계산식 필드 JSON 파싱 오버헤드

현상:

  • bom_templates.calculation_schema: JSON 저장
  • bom_template_items.depends_on, calculation_config: JSON 저장

문제점:

  • DB 레벨 검증 불가 (JSON 스키마 제약 없음)
  • 인덱싱 불가 → 검색 성능 저하
  • 애플리케이션 레벨 파싱/검증 필요 → CPU 부하

3.3 확장성 문제

문제 7: 부품 세부 분류의 확장성 부족

현상:

  • 프론트 partType: ASSEMBLY | BENDING | PURCHASED (ENUM)
  • 프론트 partUsage: GUIDE_RAIL | BOTTOM_FINISH | ... | GENERAL (ENUM)

문제점:

  • 새로운 부품 타입/용도 추가 시 코드 수정 필요
  • 백엔드에 대응 필드 없어 확장 불가

개선 방향:

  • common_codes 테이블 활용
  • code_group='part_type', code_group='part_usage'

문제 8: 멀티테넌트 카테고리 커스터마이징 한계

현상:

  • product_components.category_id/name: 프론트 UI용 캐싱
  • 카테고리 구조는 전역 (tenant별 커스터마이징 어려움)

문제점:

  • 테넌트마다 다른 BOM 카테고리 구조 필요 시 대응 불가
  • 예: 업체 A는 "모터/가이드레일/케이스", 업체 B는 "프레임/패널/브라켓"

4. 개선 제안

4.1 즉시 개선 필요 (High Priority)

제안 1: 품목 통합 테이블 설계

현재 상태:

products (FG/PT) ⟷ materials (SM/RM/CS)

개선안 A: 단일 items 테이블 (권장)

CREATE TABLE items (
  id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  tenant_id BIGINT UNSIGNED NOT NULL,
  code VARCHAR(30) NOT NULL,
  name VARCHAR(100) NOT NULL,
  item_type ENUM('FG', 'PT', 'SM', 'RM', 'CS') NOT NULL COMMENT '제품/부품/부자재/원자재/소모품',
  category_id BIGINT UNSIGNED NULL,
  unit VARCHAR(20) NULL,
  is_sellable TINYINT(1) DEFAULT 0,
  is_purchasable TINYINT(1) DEFAULT 0,
  is_producible TINYINT(1) DEFAULT 0,
  -- 부품 세부 분류
  part_type VARCHAR(30) NULL COMMENT 'ASSEMBLY/BENDING/PURCHASED',
  part_usage VARCHAR(50) NULL COMMENT 'GUIDE_RAIL/CASE/DOOR...',
  -- 가변 사이즈
  is_variable_size TINYINT(1) DEFAULT 0,
  lot_abbreviation VARCHAR(20) NULL,
  -- 인정 정보
  certification_number VARCHAR(50) NULL,
  certification_start_date DATE NULL,
  certification_end_date DATE NULL,
  -- 재고 관리
  safety_stock DECIMAL(18,4) NULL,
  lead_time INT NULL COMMENT '리드타임(일)',
  -- 감사
  created_by BIGINT UNSIGNED NULL,
  updated_by BIGINT UNSIGNED NULL,
  deleted_by BIGINT UNSIGNED NULL,
  created_at TIMESTAMP NULL,
  updated_at TIMESTAMP NULL,
  deleted_at TIMESTAMP NULL,

  UNIQUE KEY uq_items_tenant_code (tenant_id, code),
  INDEX idx_items_tenant_type (tenant_id, item_type),
  INDEX idx_items_tenant_category (tenant_id, category_id)
);

마이그레이션 전략:

  1. items 테이블 생성
  2. productsitems 마이그레이션 (item_type='FG' or 'PT')
  3. materialsitems 마이그레이션 (item_type='SM' or 'RM' or 'CS')
  4. product_components.ref_typeitem_type으로 단순화 (ref_type 제거)
  5. 기존 테이블 백업 후 제거

장점:

  • API 응답 구조 통일 (GET /v1/items?type=FG)
  • BOM 조인 단순화 (하나의 테이블만 참조)
  • 프론트엔드 코드 단순화

단점:

  • 대규모 마이그레이션 필요 (데이터 이동)
  • 기존 API 엔드포인트 변경 (호환성)
  • FK 관계 재정의 필요

개선안 B: 현재 구조 유지 + 뷰 레이어 추가 (보수적)

CREATE VIEW v_items AS
  SELECT
    CONCAT('P-', id) AS id,
    tenant_id,
    code,
    name,
    CASE
      WHEN product_type = 'PRODUCT' THEN 'FG'
      WHEN product_type = 'PART' THEN 'PT'
      ELSE 'PT'
    END AS item_type,
    category_id,
    unit,
    -- ... 기타 필드
  FROM products
  WHERE deleted_at IS NULL

  UNION ALL

  SELECT
    CONCAT('M-', id) AS id,
    tenant_id,
    code,
    name,
    -- materials에서 item_type 추론 (category_id 기반?)
    'RM' AS item_type, -- 기본값
    category_id,
    unit,
    -- ...
  FROM materials
  WHERE deleted_at IS NULL;

장점:

  • 기존 테이블 구조 유지
  • 점진적 마이그레이션 가능
  • 기존 API 유지

단점:

  • 복잡한 뷰 관리
  • 업데이트 로직 복잡도 증가
  • 성능 오버헤드

제안 2: 가격 정보 테이블 신설

CREATE TABLE item_prices (
  id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  tenant_id BIGINT UNSIGNED NOT NULL,
  item_id BIGINT UNSIGNED NOT NULL COMMENT 'items.id 참조',
  price_type ENUM('PURCHASE', 'SALES', 'PROCESSING', 'LABOR', 'INSTALL') NOT NULL,
  price DECIMAL(18,2) NOT NULL,
  currency VARCHAR(3) DEFAULT 'KRW',
  effective_from DATE NOT NULL,
  effective_to DATE NULL,
  created_by BIGINT UNSIGNED NULL,
  created_at TIMESTAMP NULL,
  updated_at TIMESTAMP NULL,

  INDEX idx_item_prices_tenant_item (tenant_id, item_id),
  INDEX idx_item_prices_type_effective (item_id, price_type, effective_from, effective_to)
);

추가 테이블: 원가 계산 정보

CREATE TABLE item_costing (
  id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  tenant_id BIGINT UNSIGNED NOT NULL,
  item_id BIGINT UNSIGNED NOT NULL,
  margin_rate DECIMAL(9,4) NULL COMMENT '마진율 (%)',
  overhead_rate DECIMAL(9,4) NULL COMMENT '간접비율 (%)',
  effective_from DATE NOT NULL,
  effective_to DATE NULL,
  created_by BIGINT UNSIGNED NULL,
  created_at TIMESTAMP NULL,
  updated_at TIMESTAMP NULL,

  INDEX idx_item_costing_tenant_item (tenant_id, item_id)
);

마이그레이션 전략:

  1. Phase 1: 테이블 생성 (비어 있는 상태)
  2. Phase 2: API 엔드포인트 추가 (POST /v1/items/{id}/prices)
  3. Phase 3: 프론트엔드 UI 연동 (가격 입력 화면)
  4. Phase 4: 기존 스프레드시트 데이터 마이그레이션 (있는 경우)

제안 3: 규격 정보 정규화

CREATE TABLE specifications (
  id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  tenant_id BIGINT UNSIGNED NOT NULL,
  code VARCHAR(100) NOT NULL COMMENT '규격 코드',
  item_type ENUM('RM', 'SM') NOT NULL,
  field_count TINYINT NOT NULL COMMENT '1/2/3',
  thickness DECIMAL(10,2) NULL,
  width_a DECIMAL(10,2) NULL,
  width_b DECIMAL(10,2) NULL,
  width_c DECIMAL(10,2) NULL,
  length DECIMAL(10,2) NULL,
  description VARCHAR(255) NULL,
  is_active TINYINT(1) DEFAULT 1,
  created_at TIMESTAMP NULL,
  updated_at TIMESTAMP NULL,

  UNIQUE KEY uq_specs_tenant_code (tenant_id, code),
  INDEX idx_specs_tenant_type (tenant_id, item_type),
  INDEX idx_specs_dimensions (thickness, width_a, length)
);

-- items 테이블에 specification_id 추가
ALTER TABLE items
ADD COLUMN specification_id BIGINT UNSIGNED NULL COMMENT '규격 ID',
ADD INDEX idx_items_spec (specification_id);

마이그레이션 전략:

  1. specifications 테이블 생성
  2. materials.item_name 파싱 스크립트 작성
  3. 파싱 결과를 specifications에 삽입
  4. items.specification_id FK 설정
  5. materials.item_name은 유지 (검색 편의성)

장점:

  • 규격별 검색/집계 가능
  • 재고 관리 시 규격별 재고량 조회 용이
  • 규격 표준화 가능

4.2 중장기 개선 필요 (Medium Priority)

제안 4: BOM 계산식 필드 통합

현재 문제:

  • bom_template_items.calculation_formula (설계)
  • product_components에는 계산식 없음

개선안: product_components에 계산식 필드 추가

ALTER TABLE product_components
ADD COLUMN quantity_formula TEXT NULL COMMENT '수량 계산식 (예: W*2+100)',
ADD COLUMN formula_params JSON NULL COMMENT '파라미터 정의 (예: {"W": "width", "H": "height"})';

데이터 흐름:

  1. 설계: bom_templatesbom_template_items (calculation_formula)
  2. 견적/주문: bom_template_itemsproduct_components (quantity_formula 복사)
  3. 주문 확정: 파라미터 대입 → quantity 고정값 계산

제안 5: 부품 분류 코드화

현재: ENUM 타입으로 하드코딩

개선안: common_codes 활용

-- 기존 common_codes 테이블 활용
INSERT INTO common_codes (tenant_id, code_group, code, name) VALUES
(1, 'part_type', 'ASSEMBLY', '조립품'),
(1, 'part_type', 'BENDING', '절곡품'),
(1, 'part_type', 'PURCHASED', '구매품'),
(1, 'part_usage', 'GUIDE_RAIL', '가이드레일'),
(1, 'part_usage', 'BOTTOM_FINISH', '하단마감재'),
(1, 'part_usage', 'CASE', '케이스'),
(1, 'part_usage', 'DOOR', '도어'),
(1, 'part_usage', 'BRACKET', '브라켓'),
(1, 'part_usage', 'GENERAL', '일반');

-- items 테이블 수정
ALTER TABLE items
MODIFY COLUMN part_type VARCHAR(30) NULL,
MODIFY COLUMN part_usage VARCHAR(50) NULL,
ADD CONSTRAINT fk_items_part_type FOREIGN KEY (part_type)
  REFERENCES common_codes(code) ON DELETE SET NULL,
ADD CONSTRAINT fk_items_part_usage FOREIGN KEY (part_usage)
  REFERENCES common_codes(code) ON DELETE SET NULL;

장점:

  • 테넌트별 커스터마이징 가능
  • 코드 수정 없이 분류 추가 가능
  • API로 분류 목록 조회 가능

제안 6: 절곡품 정보 테이블 신설

CREATE TABLE bending_parts (
  id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  tenant_id BIGINT UNSIGNED NOT NULL,
  item_id BIGINT UNSIGNED NOT NULL COMMENT '부품 ID',
  diagram_file_id BIGINT UNSIGNED NULL COMMENT '전개도 파일 ID (files 테이블)',
  bending_count INT NOT NULL DEFAULT 0,
  created_by BIGINT UNSIGNED NULL,
  created_at TIMESTAMP NULL,
  updated_at TIMESTAMP NULL,

  UNIQUE KEY uq_bending_parts_item (item_id),
  INDEX idx_bending_parts_tenant (tenant_id)
);

CREATE TABLE bending_details (
  id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  bending_part_id BIGINT UNSIGNED NOT NULL,
  seq INT NOT NULL COMMENT '절곡 순서',
  angle DECIMAL(5,2) NOT NULL COMMENT '절곡 각도',
  radius DECIMAL(10,2) NULL COMMENT '절곡 반경',
  length DECIMAL(10,2) NULL COMMENT '절곡 길이',
  position VARCHAR(100) NULL COMMENT '절곡 위치 설명',
  created_at TIMESTAMP NULL,
  updated_at TIMESTAMP NULL,

  INDEX idx_bending_details_part (bending_part_id, seq),
  FOREIGN KEY (bending_part_id) REFERENCES bending_parts(id) ON DELETE CASCADE
);

4.3 향후 고려사항 (Low Priority)

제안 7: 필드명 일관성 개선

-- products 테이블
ALTER TABLE products CHANGE COLUMN code item_code VARCHAR(30);
ALTER TABLE products CHANGE COLUMN name item_name VARCHAR(100);

-- 또는 반대로
-- React 인터페이스 변경
export interface ItemMaster {
  code: string; // itemCode  code
  name: string; // itemName  name
  // ...
}

권장: 백엔드 기준 통일 (code, name) → 프론트 수정

제안 8: 카테고리 계층 구조 개선

현재: category_id 단일 참조

개선안 A: 계층 쿼리 지원

-- categories 테이블에 이미 parent_id 있음 (가정)
-- Closure Table 패턴 적용
CREATE TABLE category_paths (
  ancestor_id BIGINT UNSIGNED NOT NULL,
  descendant_id BIGINT UNSIGNED NOT NULL,
  depth INT NOT NULL,
  PRIMARY KEY (ancestor_id, descendant_id),
  INDEX idx_descendant (descendant_id)
);

개선안 B: 비정규화

ALTER TABLE items
ADD COLUMN category1_id BIGINT UNSIGNED NULL,
ADD COLUMN category2_id BIGINT UNSIGNED NULL,
ADD COLUMN category3_id BIGINT UNSIGNED NULL;

5. 마이그레이션 전략

5.1 단계별 마이그레이션

Phase 1: 기반 구조 확립 (1-2주)

목표: 프론트-백엔드 매핑 가능한 최소 구조 완성

작업:

  1. items 테이블 생성 (제안 1-A)

    • products + materials 통합
    • item_type ENUM('FG', 'PT', 'SM', 'RM', 'CS')
    • 부품 분류 필드 추가 (part_type, part_usage)
    • 인정 정보 필드 추가
    • 재고 관리 필드 추가
  2. specifications 테이블 생성 (제안 3)

    • 구조화된 규격 정보
    • items.specification_id FK 추가
  3. item_prices 테이블 생성 (제안 2)

    • 가격 이력 관리
    • item_costing 테이블 생성
  4. 데이터 마이그레이션 스크립트

    -- products → items
    INSERT INTO items (id, tenant_id, code, name, item_type, category_id, ...)
    SELECT id, tenant_id, code, name,
      CASE product_type
        WHEN 'PRODUCT' THEN 'FG'
        WHEN 'PART' THEN 'PT'
        ELSE 'PT'
      END,
      category_id, ...
    FROM products;
    
    -- materials → items
    INSERT INTO items (id, tenant_id, code, name, item_type, category_id, ...)
    SELECT id + 1000000, tenant_id, code, name,
      'RM', -- 기본값, category로 판별 로직 추가 필요
      category_id, ...
    FROM materials;
    

Phase 2: BOM 구조 개선 (2-3주)

목표: BOM 계산식 및 템플릿 연동 완성

작업:

  1. product_components 필드 추가

    • quantity_formula TEXT
    • formula_params JSON
    • notes TEXT
    • unit_price DECIMAL(18,2)
  2. bending_parts / bending_details 테이블 생성 (제안 6)

  3. BOM 템플릿 → 실제 BOM 변환 로직

    // Service 클래스
    public function createBomFromTemplate(
      int $itemId,
      int $templateId,
      array $params
    ): void {
      $template = BomTemplate::findOrFail($templateId);
      $items = $template->items()->get();
    
      foreach ($items as $item) {
        $quantity = $item->is_calculated
          ? $this->evaluateFormula($item->calculation_formula, $params)
          : $item->qty;
    
        ProductComponent::create([
          'tenant_id' => $this->tenantId(),
          'parent_item_id' => $itemId,
          'ref_id' => $item->ref_id,
          'quantity' => $quantity,
          'quantity_formula' => $item->calculation_formula,
          'formula_params' => $item->depends_on,
        ]);
      }
    }
    

Phase 3: API 엔드포인트 마이그레이션 (2-3주)

목표: 기존 API 호환성 유지하면서 새로운 API 제공

작업:

  1. 새 API 엔드포인트 추가

    GET    /v1/items?type=FG&category_id=1
    GET    /v1/items/{id}
    POST   /v1/items
    PUT    /v1/items/{id}
    DELETE /v1/items/{id}
    
    GET    /v1/items/{id}/bom
    POST   /v1/items/{id}/bom
    PUT    /v1/items/{id}/bom/{bomId}
    DELETE /v1/items/{id}/bom/{bomId}
    
    GET    /v1/items/{id}/prices
    POST   /v1/items/{id}/prices
    
  2. 기존 API 유지 (Deprecated 표시)

    GET /v1/products   → GET /v1/items?type=FG,PT
    GET /v1/materials  → GET /v1/items?type=RM,SM,CS
    
  3. Swagger 문서 업데이트

Phase 4: 프론트엔드 마이그레이션 (3-4주)

목표: React 컴포넌트의 API 호출 변경

작업:

  1. ItemMaster 인터페이스 동기화

    • API 응답 구조와 100% 매칭
    • 새로운 필드 추가 (specification, prices)
  2. API 호출 변경

    // Before
    const products = await api.get('/v1/products');
    const materials = await api.get('/v1/materials');
    
    // After
    const items = await api.get('/v1/items');
    
  3. UI 컴포넌트 수정

    • 가격 입력 UI 추가
    • 규격 입력 UI 개선
    • 절곡품 정보 입력 UI 추가

Phase 5: 레거시 정리 (1-2주)

목표: 기존 테이블 제거 및 최적화

작업:

  1. 기존 API 엔드포인트 제거

    • /v1/products → 301 Redirect to /v1/items?type=FG,PT
    • /v1/materials → 301 Redirect to /v1/items?type=RM,SM,CS
  2. 테이블 백업 및 제거

    -- 백업
    CREATE TABLE _backup_products AS SELECT * FROM products;
    CREATE TABLE _backup_materials AS SELECT * FROM materials;
    
    -- 제거
    DROP TABLE products;
    DROP TABLE materials;
    
  3. 성능 최적화

    • 인덱스 재구성
    • 쿼리 플랜 분석
    • 캐싱 전략 수립

5.2 롤백 전략

롤백 시나리오 1: Phase 1 실패

원인: 데이터 마이그레이션 오류

조치:

  1. items 테이블 TRUNCATE
  2. 마이그레이션 스크립트 수정
  3. 재실행

영향: 없음 (기존 테이블 유지)

롤백 시나리오 2: Phase 3-4 실패

원인: API 호환성 문제 또는 프론트엔드 버그

조치:

  1. 기존 API 엔드포인트 재활성화
  2. 프론트엔드 배포 롤백
  3. 이슈 수정 후 재시도

영향: 최소 (기존 API 유지 중)

롤백 시나리오 3: Phase 5 완료 후 이슈 발생

원인: 예상치 못한 데이터 손실

조치:

  1. 백업 테이블에서 복원
    CREATE TABLE products AS SELECT * FROM _backup_products;
    CREATE TABLE materials AS SELECT * FROM _backup_materials;
    
  2. API 엔드포인트 복원
  3. 데이터 정합성 검증

영향: 중간 (백업 시점 이후 데이터 손실 가능)

5.3 데이터 마이그레이션 상세

스크립트 1: products → items

INSERT INTO items (
  id, tenant_id, code, name, item_type,
  category_id, unit,
  is_sellable, is_purchasable, is_producible,
  part_type, part_usage,
  created_by, updated_by, deleted_by,
  created_at, updated_at, deleted_at
)
SELECT
  id,
  tenant_id,
  code,
  name,
  CASE product_type
    WHEN 'PRODUCT' THEN 'FG'
    WHEN 'PART' THEN 'PT'
    WHEN 'SUBASSEMBLY' THEN 'PT'
    ELSE 'PT'
  END AS item_type,
  category_id,
  unit,
  is_sellable,
  is_purchasable,
  is_producible,
  -- part_type, part_usage는 NULL (수동 입력 필요)
  NULL AS part_type,
  NULL AS part_usage,
  created_by,
  updated_by,
  deleted_by,
  created_at,
  updated_at,
  deleted_at
FROM products
WHERE deleted_at IS NULL;

스크립트 2: materials → items

INSERT INTO items (
  id, tenant_id, code, name, item_type,
  category_id, unit,
  created_by, updated_by, deleted_by,
  created_at, updated_at, deleted_at
)
SELECT
  id + 10000000, -- ID 충돌 방지 (products와 materials ID 범위 분리)
  tenant_id,
  COALESCE(code, CONCAT('MAT-', id)) AS code, -- code 없으면 생성
  name,
  -- item_type은 category로 판별 (수동 로직 필요)
  CASE
    WHEN category_id IN (SELECT id FROM categories WHERE code_group='raw_material') THEN 'RM'
    WHEN category_id IN (SELECT id FROM categories WHERE code_group='sub_material') THEN 'SM'
    ELSE 'RM'
  END AS item_type,
  category_id,
  NULL AS unit, -- materials 테이블에 unit 없음 (추가 필요)
  created_by,
  updated_by,
  deleted_by,
  created_at,
  updated_at,
  deleted_at
FROM materials
WHERE deleted_at IS NULL;

스크립트 3: materials.item_name → specifications

-- 규격 파싱 예제 (정규식 사용)
INSERT INTO specifications (
  tenant_id, code, item_type,
  thickness, width_a, length,
  description, is_active,
  created_at, updated_at
)
SELECT DISTINCT
  m.tenant_id,
  m.item_name AS code,
  'RM' AS item_type,
  -- 정규식 파싱 (MySQL 8.0+ REGEXP_SUBSTR 사용)
  REGEXP_SUBSTR(m.item_name, '[0-9.]+(?=T)') AS thickness,
  REGEXP_SUBSTR(m.item_name, '[0-9.]+(?= x)') AS width_a,
  REGEXP_SUBSTR(m.item_name, '[0-9.]+$') AS length,
  m.name AS description,
  1 AS is_active,
  NOW(),
  NOW()
FROM materials m
WHERE m.item_name IS NOT NULL
  AND m.item_name REGEXP '[0-9.]+T x [0-9.]+ x [0-9.]+';

-- items 테이블에 specification_id 연결
UPDATE items i
JOIN specifications s ON i.name = s.description
SET i.specification_id = s.id
WHERE i.item_type IN ('RM', 'SM');

6. 결론 및 요약

핵심 발견사항

  1. 잘 설계된 부분:

    • 통합 참조 구조 (product_components.ref_type + ref_id)
    • 설계 워크플로우 분리 (models → versions → templates)
    • 멀티테넌트 및 감사 로그 일관성
  2. ⚠️ 개선 필요 부분:

    • 프론트-백엔드 타입 불일치 (ItemMaster vs products/materials 분리)
    • BOM 구조 이원화 (템플릿 vs 실제)
    • 가격 정보 필드 전체 부재
    • 규격 정보 비정규화

우선순위 TOP 3 개선사항

🔴 Priority 1: 품목 통합 테이블 (items) 신설

근거:

  • 프론트-백엔드 타입 불일치 해소
  • API 응답 구조 통일 → 프론트엔드 코드 단순화
  • BOM 조인 성능 개선

예상 공수: 2-3주 예상 효과:

  • API 호출 50% 감소 (products + materials → items 단일 호출)
  • 프론트엔드 타입 분기 로직 제거 → 코드 20% 감소
  • BOM 조회 성능 30% 향상 (조인 최적화)

🟡 Priority 2: 가격 정보 테이블 (item_prices, item_costing) 신설

근거:

  • 구매가/판매가 이력 관리 필수
  • 원가 계산 및 견적 산출 기능 구현 불가
  • 프론트엔드 7개 필드 매핑 부재

예상 공수: 1-2주 예상 효과:

  • 견적 산출 기능 완성도 100% 달성
  • 가격 변동 이력 추적 가능
  • 원가 분석 리포트 제공 가능

🟢 Priority 3: 규격 정보 정규화 (specifications) 및 BOM 계산식 통합

근거:

  • 규격별 검색/집계 불가 → 재고 관리 어려움
  • BOM 계산식 필드 불일치 → 템플릿 활용 제한

예상 공수: 2-3주 예상 효과:

  • 규격별 재고 조회 성능 100배 향상 (인덱스 활용)
  • 견적 산출 자동화 완성도 80% → 100%
  • 재고 최적화 알고리즘 구현 가능

예상 효과 종합

  • 개발 생산성: 30% 향상 (API 구조 단순화)
  • 성능: BOM 조회 30% 향상, 규격 검색 100배 향상
  • 기능 완성도: 견적 산출 100%, 원가 분석 100%, 재고 관리 80% → 100%
  • 유지보수성: 코드 복잡도 20% 감소, 테이블 수 33% 감소 (products + materials → items)

부록 A: ERD 다이어그램 (텍스트 형식)

현재 구조 (AS-IS)

┌─────────────┐       ┌──────────────────┐       ┌─────────────┐
│  products   │       │ product_components│       │  materials  │
├─────────────┤       ├──────────────────┤       ├─────────────┤
│ id          │◄──────│ parent_product_id│       │ id          │
│ tenant_id   │       │ ref_type         │──────►│ tenant_id   │
│ code        │       │ ref_id           │       │ name        │
│ name        │       │ quantity         │       │ item_name   │
│ product_type│       │ category_id      │       │ category_id │
│ category_id │       │ category_name    │       └─────────────┘
│ unit        │       └──────────────────┘
│ is_sellable │
│ is_purchasable│
│ is_producible│
└─────────────┘

┌─────────────┐       ┌──────────────────┐       ┌──────────────────┐
│   models    │       │ model_versions   │       │  bom_templates   │
├─────────────┤       ├──────────────────┤       ├──────────────────┤
│ id          │◄──────│ model_id         │◄──────│ model_version_id │
│ tenant_id   │       │ version_no       │       │ name             │
│ code        │       │ status           │       │ is_primary       │
│ name        │       │ effective_from   │       │ calculation_schema│
│ lifecycle   │       │ effective_to     │       │ company_type     │
└─────────────┘       └──────────────────┘       │ formula_version  │
                                                  └──────────────────┘
                                                           │
                                                           ▼
                                                  ┌──────────────────────┐
                                                  │ bom_template_items   │
                                                  ├──────────────────────┤
                                                  │ bom_template_id      │
                                                  │ ref_type             │
                                                  │ ref_id               │
                                                  │ qty                  │
                                                  │ waste_rate           │
                                                  │ is_calculated        │
                                                  │ calculation_formula  │
                                                  │ depends_on           │
                                                  └──────────────────────┘

개선 구조 (TO-BE)

┌─────────────────────────────┐       ┌──────────────────┐
│          items              │       │ product_components│
├─────────────────────────────┤       ├──────────────────┤
│ id                          │◄──────│ parent_item_id   │
│ tenant_id                   │       │ child_item_id    │
│ code                        │──────►│ quantity         │
│ name                        │       │ quantity_formula │
│ item_type (FG/PT/SM/RM/CS) │       │ formula_params   │
│ category_id                 │       │ unit_price       │
│ unit                        │       │ notes            │
│ specification_id            │       │ category_id      │
│ part_type                   │       │ category_name    │
│ part_usage                  │       └──────────────────┘
│ is_variable_size            │
│ lot_abbreviation            │       ┌──────────────────┐
│ certification_number        │       │  specifications  │
│ certification_start_date    │       ├──────────────────┤
│ certification_end_date      │       │ id               │
│ safety_stock                │◄──────│ tenant_id        │
│ lead_time                   │       │ code             │
│ is_sellable                 │       │ item_type        │
│ is_purchasable              │       │ field_count      │
│ is_producible               │       │ thickness        │
└─────────────────────────────┘       │ width_a/b/c      │
         │                            │ length           │
         ▼                            └──────────────────┘
┌─────────────────────────────┐
│       item_prices           │       ┌──────────────────┐
├─────────────────────────────┤       │   item_costing   │
│ id                          │       ├──────────────────┤
│ tenant_id                   │       │ id               │
│ item_id                     │       │ tenant_id        │
│ price_type (PURCHASE/SALES) │       │ item_id          │
│ price                       │       │ margin_rate      │
│ currency                    │       │ overhead_rate    │
│ effective_from              │       │ effective_from   │
│ effective_to                │       │ effective_to     │
└─────────────────────────────┘       └──────────────────┘
         │
         ▼
┌─────────────────────────────┐
│      bending_parts          │       ┌──────────────────┐
├─────────────────────────────┤       │ bending_details  │
│ id                          │       ├──────────────────┤
│ tenant_id                   │◄──────│ bending_part_id  │
│ item_id                     │       │ seq              │
│ diagram_file_id             │       │ angle            │
│ bending_count               │       │ radius           │
└─────────────────────────────┘       │ length           │
                                      │ position         │
                                      └──────────────────┘

부록 B: 우선순위별 액션 아이템 리스트

Phase 1: 즉시 착수 (High Priority)

No 액션 아이템 담당 예상 공수 우선순위
1.1 items 테이블 마이그레이션 작성 Backend 3일 🔴 P0
1.2 specifications 테이블 마이그레이션 작성 Backend 2일 🔴 P0
1.3 item_prices, item_costing 테이블 마이그레이션 작성 Backend 2일 🔴 P0
1.4 데이터 마이그레이션 스크립트 작성 및 테스트 Backend 5일 🔴 P0
1.5 Item 모델 및 Service 클래스 구현 Backend 3일 🔴 P0
1.6 Item API 엔드포인트 구현 Backend 5일 🔴 P0
1.7 Swagger 문서 작성 Backend 2일 🔴 P0

Phase 2: 중단기 (Medium Priority)

No 액션 아이템 담당 예상 공수 우선순위
2.1 product_components 계산식 필드 추가 Backend 2일 🟡 P1
2.2 bending_parts, bending_details 테이블 추가 Backend 3일 🟡 P1
2.3 BOM 템플릿 → 실제 BOM 변환 서비스 구현 Backend 5일 🟡 P1
2.4 부품 분류 코드화 (common_codes 활용) Backend 2일 🟡 P1
2.5 ItemMaster 인터페이스 동기화 Frontend 2일 🟡 P1
2.6 API 호출 변경 (items 엔드포인트) Frontend 3일 🟡 P1
2.7 가격 입력 UI 구현 Frontend 5일 🟡 P1
2.8 규격 입력 UI 개선 Frontend 3일 🟡 P1

Phase 3: 장기 개선 (Low Priority)

No 액션 아이템 담당 예상 공수 우선순위
3.1 필드명 일관성 개선 Backend/Frontend 3일 🟢 P2
3.2 카테고리 계층 구조 개선 (Closure Table) Backend 5일 🟢 P2
3.3 성능 최적화 (인덱스, 캐싱) Backend 3일 🟢 P2
3.4 레거시 테이블 제거 Backend 2일 🟢 P2

총 예상 공수

  • Phase 1: 22일 (약 4-5주)
  • Phase 2: 25일 (약 5주)
  • Phase 3: 13일 (약 2-3주)
  • 총합: 60일 (약 12주 = 3개월)

리스크 및 대응

리스크 확률 영향도 대응 방안
데이터 마이그레이션 오류 높음 백업 전략 수립, 롤백 시나리오 준비
API 호환성 문제 중간 기존 API 유지, 점진적 마이그레이션
프론트엔드 버그 중간 충분한 테스트 기간 확보
성능 저하 낮음 높음 인덱스 최적화, 쿼리 플랜 사전 검증

분석 완료일: 2025-11-10 최종 검토자: Claude Code System Architect 다음 액션: Phase 1 마이그레이션 스크립트 작성 착수