docs: 품목 기준 데이터 정비 Phase 2 완료

Phase 1 분석 + Phase 2 실행 결과를 포함한 계획 문서.

DB 변경 사항 (tenant_id=287):
- item_fields 4건 생성 (id:177-180, FG attributes 매핑)
- item_fields 10건 비활성화 (섀도잉/null key/미연결 정리)
- item_fields 9건 options 갱신 (RM/SM/PT 실데이터)
- entity_relationships 7건 삭제, 8건 추가 (is_active 통합 + FG 필드 연결)
- item_details 18건 생성 (id:524-541, FG 제품 상세)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-31 02:41:29 +09:00
parent 38411ad8b9
commit 925e8f13ff

View File

@@ -0,0 +1,831 @@
# 품목 기준 데이터 정비 계획
> **작성일**: 2026-01-31
> **목적**: 5130 레거시 시스템에서 이관된 품목 데이터가 SAM 품목기준관리(item-master-data-management)에서 올바르게 표시되고, 견적 문서에 정확히 반영되도록 설정을 정비한다.
> **기준 문서**: `docs/specs/database-schema.md`, `docs/rules/item-policy.md`
> **상태**: 🔄 진행중
---
## 📍 현재 진행 상태
| 항목 | 내용 |
|------|------|
| **마지막 완료 작업** | Phase 2 전체 완료 (6/6) |
| **다음 작업** | 프론트엔드 표시 검증 |
| **진행률** | Phase 1 완료, Phase 2 완료 (6/6) |
| **마지막 업데이트** | 2026-01-31 |
---
## 1. 개요
### 1.1 배경
5130 레거시 시스템(chandj DB)의 품목 데이터를 SAM으로 이관 완료하였으나, SAM의 품목기준관리(item-master-data-management) 설정이 이관된 데이터 구조와 맞지 않아 프론트엔드에서 품목 정보가 올바르게 표시되지 않는다. 품목 데이터가 정확히 표시되어야 견적 문서에 데이터를 뿌려줄 수 있다.
**핵심 문제:**
- `item_pages/item_sections/item_fields`의 현재 필드 정의가 이관된 데이터의 실제 `attributes` JSON 구조와 불일치
- `item_details` 테이블에 FG 18개 품목의 상세 정보가 없음 (PT 129개만 존재)
- 드롭다운 필드의 `options` 값이 실제 데이터와 매핑되지 않음
- `setting_field_defs``tenant_field_settings` 테이블이 비어있음
### 1.2 기준 원칙
```
┌─────────────────────────────────────────────────────────────────┐
│ 🎯 핵심 원칙 │
├─────────────────────────────────────────────────────────────────┤
│ 1. 견적 로직(FormulaEvaluatorService)에 영향 없는 범위에서만 수정 │
│ 2. items.bom JSON 구조, items.code 체계는 변경 금지 │
│ 3. item_pages/sections/fields 설정만 조정하여 데이터 표시 정합성 확보│
│ 4. 5130 데이터와 SAM 데이터 간 매핑 관계를 명확히 문서화 │
└─────────────────────────────────────────────────────────────────┘
```
### 1.3 변경 승인 정책
| 분류 | 예시 | 승인 |
|------|------|------|
| ✅ 즉시 가능 | item_fields options 값 수정, field_name 변경, 필드 순서 변경 | 불필요 |
| ⚠️ 컨펌 필요 | item_fields 추가/삭제, item_sections 구조 변경, entity_relationships 변경 | **필수** |
| 🔴 금지 | items.bom JSON 구조 변경, items.code 체계 변경, FormulaEvaluatorService 수정 | 별도 협의 |
### 1.4 준수 규칙
- `docs/specs/database-schema.md` - DB 스키마 참조
- `docs/rules/item-policy.md` - 품목 정책
- `docs/standards/quality-checklist.md` - 품질 체크리스트
---
## 2. 대상 범위
### 2.1 Phase 1: 분석 (4단계)
| # | 작업 항목 | 상태 | 비고 |
|---|----------|:----:|------|
| 1.1 | 품목기준관리 구조 파악 | ✅ | 아래 섹션 4.1 참조 |
| 1.2 | 현재 설정 현황 분석 | ✅ | 아래 섹션 4.2 참조 |
| 1.3 | 이관된 제품 데이터 구조 분석 | ✅ | 아래 섹션 4.3 참조 |
| 1.4 | BOM 관계 구조 분석 | ✅ | 아래 섹션 4.4 참조 |
### 2.2 Phase 2: 설정 수정 (견적 영향 없는 범위)
| # | 작업 항목 | 상태 | 비고 |
|---|----------|:----:|------|
| 2.1 | FG(제품) 필드 설정 정비 | ✅ | 완료: 4개 필드 추가(id:177-180), section 102 연결, order_no 정렬 |
| 2.2 | PT(부품) 필드 설정 정비 | ✅ | 완료: Part_type options 갱신(BD부품 추가, 라벨 간소화). display_condition은 5.6.4로 이관 |
| 2.3 | SM/RM/CS 필드 설정 정비 | ✅ | 완료: RM options 실데이터(SUS/EGI/두께/폭/길이), SM 11개 카테고리, SM규격 빈 배열 |
| 2.4 | FG item_details 데이터 보완 | ✅ | 완료: 18건 INSERT (id:524-541), product_category/item_name/specification 설정 |
| 2.5 | 드롭다운 options 실데이터 매핑 | ✅ | 완료: 2.1~2.3에서 함께 처리 (FG 4개, PT Part_type, RM 5개, SM 3개) |
| 2.6 | 고정컬럼-동적필드 섀도잉 정리 | ✅ | 완료: null key 1건, 미연결 5건 비활성화, is_active 중복 4건 통합 |
---
## 3. 작업 절차
### 3.1 단계별 절차
```
Step 1: 구조 분석 (Phase 1.1)
├── item_pages → item_sections → item_fields 계층 구조 파악
├── entity_relationships 연결 관계 매핑
└── React 프론트엔드 렌더링 로직 확인
Step 2: 현재 설정 현황 (Phase 1.2)
├── 5개 item_pages (CS/RM/SM/PT/FG) 설정 현황
├── 26개 page→section, 66개 section→field 관계 확인
└── 각 필드의 options, validation_rules 점검
Step 3: 이관 데이터 구조 (Phase 1.3)
├── items 테이블 780건 (FG:18, PT:669, SM:61, RM:28, CS:4)
├── items.attributes JSON 구조 분석
├── item_details 129건 (PT만 존재) 분석
└── 5130 원본 데이터와 대조
Step 4: BOM 관계 구조 (Phase 1.4)
├── FG→PT BOM JSON 관계 매핑
├── 5130 models→parts→parts_sub 3계층 대응
├── BD계열(가이드레일, 하단마감재, L-BAR) 매핑
└── 견적 FormulaEvaluatorService 의존성 확인
Step 5: 설정 수정 (Phase 2)
├── FG 필드: attributes 키와 item_fields 매핑
├── PT 필드: part_type별 분기 필드 정의
├── 드롭다운 options: 실제 사용 값으로 갱신
├── FG item_details 18건 생성
├── 고정컬럼-동적필드 섀도잉 정리 (is_active 중복 통합, null key, 미연결 필드)
└── 프론트엔드 표시 검증
```
---
## 4. 상세 분석 결과
### 4.1 Phase 1.1: 품목기준관리 구조
#### 아키텍처 개요
```
┌─────────────────────────────────────────────────────────────────┐
│ item_pages (품목유형별 폼 정의) │
│ - CS/RM/SM/PT/FG 각 1개 페이지 │
│ - tenant_id=287, group_id=1 │
├─────────────────────────────────────────────────────────────────┤
│ entity_relationships (parent_type='page' → child_type='section')│
│ - 26개 관계 (페이지→섹션) │
│ - 66개 관계 (섹션→필드) │
├─────────────────────────────────────────────────────────────────┤
│ item_sections (섹션) │
│ - type: 'fields' (일반 필드 그룹) │
│ - type: 'bom' (BOM 구성 섹션) │
├─────────────────────────────────────────────────────────────────┤
│ item_fields (필드 정의) │
│ - field_type: textbox/number/dropdown/checkbox/date/textarea │
│ - storage_type: 'column' (items 컬럼) / 'json' (attributes) │
│ - options: JSON [{label, value}] (드롭다운용) │
└─────────────────────────────────────────────────────────────────┘
```
#### 테이블 스키마
**item_pages**: `id, tenant_id, group_id, page_name, item_type(ENUM), source_table, absolute_path, is_active`
**item_sections**: `id, tenant_id, group_id, title, type(ENUM: fields/bom), order_no, is_template, is_default, description`
**item_fields**: `id, tenant_id, group_id, field_name, field_key, field_type(ENUM), order_no, is_required, default_value, placeholder, display_condition(JSON), validation_rules(JSON), options(JSON), properties(JSON), source_table, source_column, storage_type(ENUM: column/json), json_path, category, description, is_common, is_active, is_locked`
**entity_relationships**: `id, tenant_id, group_id, parent_type, parent_id, child_type, child_id, order_no, metadata(JSON), is_locked`
---
### 4.2 Phase 1.2: 현재 설정 현황
#### 4.2.1 페이지별 구조
**CS (소모품) - page id:1015**
```
└ Section: 기본정보 (id:92, type:fields)
├ 품목명 (key:item_name, textbox)
├ 규격(사양) (key:specification, textbox)
├ 단위 (key:unit, dropdown)
└ 비고 (key:note1, textbox)
```
**RM (원자재) - page id:1016**
```
└ Section: 기본 정보 (id:93, type:fields)
├ 품목명 (key:100_item_name, dropdown) → options: [철판, 알루미늄, 스테인리스, 아연도금강판]
├ 규격 (key:101_specification_1, dropdown) → options: [옵션1-1, 옵션1-2, 옵션1-3, 옵션120]
├ 규격 (key:102_specification_2, dropdown) → options: [옵션2-1, 옵션2-2]
├ 규격 (key:103_specification_3, dropdown) → options: [옵션3-1, 옵션3-2, 옵션3-3]
├ 규격 (key:104_specification_4, dropdown)
├ 활성 여부 (key:is_active, dropdown)
├ 단위 (key:unit, dropdown)
└ 비고 (key:note1, textbox)
```
**SM (부자재) - page id:1017**
```
└ Section: 기본 정보 (id:94, type:fields)
├ 품목명 (key:107_item_name, dropdown)
├ 규격 (key:108_specification_1, dropdown)
├ 규격 (key:109_specification_2, dropdown)
├ 활성 여부 (key:field_163, dropdown)
├ 단위 (key:unit, dropdown)
└ 비고 (key:note1, textbox)
```
**PT (부품) - page id:1018**
```
├ Section: 기본 정보 (id:95, type:fields)
│ ├ 부품 유형 (key:Part_type, dropdown)
│ ├ 품목명 (key:itemNameAssemblyPart, dropdown)
│ ├ 설치유형 (key:119~121_Installation_type_1~3, dropdown x3)
│ ├ 마감 (key:112_deadline, dropdown)
│ ├ 품목명 (key:122_bending_parts, dropdown)
│ ├ 종류 (key:123~125_type_1~3, dropdown x3)
│ ├ 재질 (key:126_texture, dropdown)
│ ├ 폭 합계 (key:127_width_total, number)
│ ├ 모양&길이 (key:128_Shape_Length, dropdown)
│ ├ 비고 (key:note2, textbox)
│ ├ 품목명 (key:132_PurchasedItemName, dropdown)
│ ├ 품목상태 (key:138_state, dropdown)
│ ├ 전원 (key:134_power, dropdown)
│ ├ 용량 (key:135_capacity, dropdown)
│ ├ 비고 (key:note3, textbox)
│ ├ 품목상태 (key:, dropdown) ← ⚠️ key 비어있음
│ └ 단위 (key:unit, dropdown)
├ Section: 측면 규격 및 길이 (id:99, type:fields)
│ ├ 측면 규격 (가로) (key:113_side_dimensions_horizontal, number)
│ ├ 측면 규격 (세로) (key:114_side_dimensions_vertical, number)
│ ├ 길이 (key:115_length, dropdown)
│ ├ 품목 상태 (key:105_state, dropdown)
│ └ 품목상태 (key:133_state, dropdown)
├ Section: BOM (id:100, type:fields)
│ └ 부품구성 (BOM) 필요 (key:118_bom, checkbox)
└ Section: 부품 구성 (BOM) (id:101, type:bom)
```
**FG (제품) - page id:1019**
```
├ Section: 기본 정보 (id:102, type:fields)
│ ├ 상품명 (key:139_productName, textbox)
│ ├ 품목명 (key:140_field_96, textbox)
│ ├ 로트 약자 (key:141_lotNum, textbox)
│ ├ 품목상태 (key:138_state, dropdown)
│ ├ 비고 (key:note3, textbox)
│ ├ 인정번호 (key:142_accreditationNumber, textbox)
│ ├ 인정 유효기간 시작일 (key:143_accreditationStart, date)
│ ├ 인정 유효기간 종료일 (key:144_accreditationEnd, date)
│ └ 비고 (key:145_field_137, textbox)
├ Section: BOM (id:100, type:fields) ← PT와 공유
│ └ 부품구성 (BOM) 필요 (key:118_bom, checkbox)
└ Section: 부품 구성 (BOM) (id:101, type:bom) ← PT와 공유
```
#### 4.2.2 발견된 문제점
| # | 문제 | 영향 | 심각도 |
|---|------|------|--------|
| 1 | FG 필드의 field_key가 이관 데이터의 attributes 키와 불일치 | FG 품목 표시 불가 | 🔴 |
| 2 | PT 필드에 field_key가 빈 필드 존재 | 데이터 매핑 실패 | 🟡 |
| 3 | RM/SM 드롭다운 options가 플레이스홀더 값 (옵션1-1 등) | 실제 데이터와 불일치 | 🟡 |
| 4 | FG item_details 0건 (18개 FG 품목 상세 없음) | 제품 상세 표시 불가 | 🔴 |
| 5 | `setting_field_defs`, `tenant_field_settings` 테이블 비어있음 | 필드 커스터마이징 미설정 | 🟢 |
| 6 | FG attributes에 `model_name`, `finishing_type` 등 레거시 키 사용 | 필드 매핑 필요 | 🟡 |
| 7 | 고정컬럼과 동적필드 간 섀도잉 중복 다수 존재 | 향후 일괄 기능 오작동 위험 | 🔴 |
#### 4.2.3 고정컬럼-동적필드 섀도잉 분석
`is_common=1` 필드 11개가 `items` 테이블 고정 컬럼과 직접 매핑됨.
`is_common=0` 동적 필드 중 고정 컬럼과 **의미적으로 동일한** 필드가 별도 key로 생성되어 있어, 고정 컬럼 기반 기능(필터링, 일괄 변경 등)이 이 동적 필드를 사용하는 품목에 적용되지 않는 위험 존재.
##### 고정 컬럼 매핑 현황 (is_common=1, 정상)
| field id | field_key | source_column | 비고 |
|----------|-----------|---------------|------|
| 153 | `item_type` | `items.item_type` | |
| 154 | `code` | `items.code` | |
| 155 | `name` | `items.name` | |
| 156 | `items_unit` | `items.unit` | |
| 157 | `category_id` | `items.category_id` | |
| 158 | `bom` | `items.bom` | |
| 159 | `attributes` | `items.attributes` | |
| 160 | `attributes_archive` | `items.attributes_archive` | |
| 161 | `options` | `items.options` | |
| 162 | `description` | `items.description` | |
| 163 | `is_active` | `items.is_active` | |
##### 섀도잉 유형 A: `is_active` 중복 (6건) — 🔴 이번에 정리
| field id | field_key | field_name | 소속 page | 섀도잉 대상 |
|----------|-----------|-----------|-----------|------------|
| 163 | `is_active` | 활성 여부 | 공통(is_common=1) | `items.is_active` (정상 매핑) |
| **164** | `field_163` | 활성 여부 | **SM** | `items.is_active` 중복 |
| **105** | `105_state` | 품목 상태 | **PT** (측면규격 섹션) | `items.is_active` 중복 |
| **131** | `131_state` | 품목 상태 | **미연결** | `items.is_active` 중복 |
| **133** | `133_state` | 품목상태 | **PT** (측면규격 섹션) | `items.is_active` 중복 |
| **138** | `138_state` | 품목상태 | **PT, FG** | `items.is_active` 중복 |
| **152** | (null) | 품목상태 | **PT** | `items.is_active` 중복 + key 없음 |
**위험:** `items.is_active` 기반 비활성 필터링 시 동적 필드 `138_state` 등을 사용하는 품목은 필터 미적용
##### 섀도잉 유형 B: null key 필드 (1건) — 🔴 이번에 정리
| field id | field_key | field_name | 소속 page |
|----------|-----------|-----------|-----------|
| **152** | **(null)** | 품목상태 | PT |
**위험:** field_key가 없어서 데이터 저장/조회 자체 불가. 폼에 표시는 되지만 값 매핑 실패.
##### 섀도잉 유형 C: 미연결 필드 (5건) — 🟡 이번에 정리
어떤 page에도 entity_relationship으로 연결되지 않아 사용 불가 상태인 필드:
| field id | field_key | field_name | 비고 |
|----------|-----------|-----------|------|
| **116** | `116_bending_parts` | 품목명 | 미연결, 122와 중복 |
| **117** | `117_purchase_parts` | 품목명 | 미연결, 132와 중복 |
| **129** | `unit_2` | 단위_2 | 미연결, 98(unit)과 중복 |
| **131** | `131_state` | 품목 상태 | 미연결, is_active 중복 |
| **136** | `unit_3` | 단위_3 | 미연결, 98(unit)과 중복 |
**위험:** 미연결 상태이므로 즉각적 문제는 없으나, 향후 혼동 유발 가능.
##### 섀도잉 유형 D: name/unit/specification/description 중복 — 📌 다음에 정리
| 고정 컬럼 | 동적 필드 중복 수 | 소속 pages | 다음에 정리하는 이유 |
|----------|------------------|------------|---------------------|
| `items.name` | 8건 (id:96,100,107,111,122,132,139,140) | CS,RM,SM,PT,FG | PT 부품유형별 분기 UI와 맞물림. 렌더링 로직 변경 필요 |
| `items.unit` | 2건 (id:98,129,136) | CS,RM,SM,PT + 미연결 | unit(id:98)은 4개 page에서 활발히 사용 중 |
| `item_details.specification` | 6건 (id:97,101~104,108~109) | CS,RM,SM | 원자재 4단 규격은 의도된 설계. 통합 시 정책 결정 필요 |
| `items.description` | 4건 (id:99,130,137,145) | CS,RM,SM,PT,FG | 용도별 비고 분리가 의도된 것일 수 있음 |
| `item_details.part_type` | 1건 (id:110) | PT | 현재 정상 사용 중 |
| `item_details.certification_*` | 3건 (id:142~144) | FG | FG item_details 생성 후 연계 검토 |
---
### 4.3 Phase 1.3: 이관된 제품 데이터 구조
#### 4.3.1 SAM items 테이블 현황 (tenant_id=287)
| item_type | 건수 | 설명 |
|-----------|------|------|
| FG (완제품) | 18 | 5130 models 18개와 1:1 매핑 |
| PT (부품) | 669 | 5130 parts/parts_sub + BDmodels 이관 |
| SM (부자재) | 61 | 5130 item_list 중 부자재 |
| RM (원자재) | 28 | 5130 item_list 중 원자재 |
| CS (소모품) | 4 | 소모품 |
| **합계** | **780** | |
#### 4.3.2 FG 품목 attributes 구조
FG 18개 품목의 `attributes` JSON 구조 (예: FG-KSS01-벽면형-SUS):
```json
{
"model_name": "KSS01", // 5130 모델명
"legacy_source": "models", // 원본 출처
"finishing_type": "SUS마감", // 마감 유형
"guiderail_type": "벽면형", // 가이드레일 유형 (설치유형)
"major_category": "스크린", // 대분류 (스크린/철재)
"legacy_model_id": 12 // 5130 model_id
}
```
**FG attributes 키 ↔ FG item_fields 매핑 현황:**
| attributes 키 | 현재 FG field_key | 매핑 상태 |
|---------------|-------------------|-----------|
| `model_name` | `139_productName` (상품명) | ❌ 불일치 - textbox로 직접 입력 |
| `major_category` | 없음 | ❌ 필드 없음 |
| `finishing_type` | 없음 | ❌ 필드 없음 |
| `guiderail_type` | 없음 | ❌ 필드 없음 |
| `legacy_model_id` | 없음 | 내부 참조용, 표시 불필요 |
| `legacy_source` | 없음 | 내부 참조용, 표시 불필요 |
#### 4.3.3 PT 품목 attributes 구조
PT 품목의 `attributes` JSON (예: PT-가이드레일):
```json
{
"base_price": "25000.00", // 기본 단가
"legacy_num": 6, // 5130 번호
"legacy_source": "item_list" // 원본 출처
}
```
#### 4.3.4 item_details 현황
| item_type | item_details 건수 | 비고 |
|-----------|-------------------|------|
| FG | 0 | ⚠️ 없음 - 생성 필요 |
| PT | 129 | BD계열 부품 (마구리, 케이스 등) |
| SM | 0 | |
| RM | 0 | |
| CS | 0 | |
PT item_details 예시 (BD-마구리-505*355):
```
part_type: 마구리
item_name: 마구리 505*355
specification: 505*355
is_sellable: 1, is_purchasable: 1, is_producible: 0
```
---
### 4.4 Phase 1.4: BOM 관계 구조
#### 4.4.1 SAM BOM 구조
FG 품목의 `bom` JSON 형태:
```json
// FG-KSE01-벽면형-EGI (스크린, bom_items: 3)
[
{"quantity": 2, "child_item_id": 13170}, // PT-가이드레일
{"quantity": 1, "child_item_id": 13174}, // PT-하단마감재
{"quantity": 2, "child_item_id": 13175} // PT-L-BAR
]
```
**BOM 패턴 요약:**
| 대분류 | FG 품목 수 | BOM 구성 |
|--------|-----------|----------|
| 스크린 (KSS01/KSE01/KWE01/KSS02) | 12 | PT 3개 (가이드레일×2 + 하단마감재×1 + L-BAR×2) |
| 철재 (KQTS01/KTE01) | 6 | PT 2개 (가이드레일×2 + 하단마감재×1) |
#### 4.4.2 5130 레거시 구조 비교
**5130 models (18개 활성):**
| 모델명 | 대분류 | 마감 유형 | 가이드레일 |
|--------|--------|-----------|-----------|
| KSS01 | 스크린 | SUS마감 | 벽면형/측면형 |
| KSE01 | 스크린 | SUS마감/EGI마감 | 벽면형/측면형 |
| KWE01 | 스크린 | SUS마감/EGI마감 | 벽면형/측면형 |
| KQTS01 | 철재 | SUS마감 | 벽면형/측면형 |
| KTE01 | 철재 | SUS마감/EGI마감 | 벽면형/측면형 |
| KSS02 | 스크린 | SUS마감 | 벽면형/측면형 |
**5130 BDmodels (59개 활성):**
| model_name | 건수 | 설명 |
|-----------|------|------|
| KSS01 | 4 | 가이드레일/하단마감재/L-BAR 등 |
| KSE01 | 8 | |
| KWE01 | 7 | |
| KQTS01 | 3 | |
| KTE01 | 6 | |
| KSS02 | 4 | |
| KDSS01 | 4 | |
| (빈값) | 23 | 공용 부품 |
#### 4.4.3 견적 의존성 분석
**FormulaEvaluatorService가 의존하는 데이터:**
1. `items.bom` JSON → `child_item_id`로 PT 품목 조회 → PT의 `item_category``CategoryGroup` 매핑
2. `items.code` → 품목 식별에 사용
3. `items.item_category` → 카테고리 그룹 가격 산출에 사용
**⚠️ 수정 금지 영역:**
- `items.bom` JSON 구조 (`[{child_item_id, quantity}]`)
- `items.code` 체계 (`FG-모델명-가이드레일-마감`, `PT-부품명`)
- `items.item_category` 값 (`스크린`, `철재`)
- `FormulaEvaluatorService` 로직
**✅ 안전한 수정 영역:**
- `item_pages/item_sections/item_fields` (폼 표시 설정)
- `entity_relationships` (폼 구성 관계)
- `item_fields.options` (드롭다운 선택 값)
- `item_details` (표시용 상세 정보)
- `items.attributes` JSON (프론트엔드 표시용, 견적 미사용)
---
## 5. Phase 2 수정 계획
### 5.1 FG(제품) 필드 설정 정비
**현재 문제:** FG의 `attributes` 키(`model_name`, `finishing_type`, `guiderail_type`, `major_category`)와 FG page의 `item_fields` field_key가 매핑되지 않음
**수정 방안:**
| 순서 | 작업 | field_key | field_type | options |
|------|------|-----------|-----------|---------|
| 1 | 모델명 필드 추가/수정 | `model_name` | dropdown | KSS01, KSE01, KWE01, KQTS01, KTE01, KSS02 |
| 2 | 대분류 필드 추가 | `major_category` | dropdown | 스크린, 철재 |
| 3 | 마감유형 필드 추가 | `finishing_type` | dropdown | SUS마감, EGI마감 |
| 4 | 설치유형 필드 추가 | `guiderail_type` | dropdown | 벽면형, 측면형 |
| 5 | 기존 필드 유지 | `139_productName` → 상품명 | textbox | - |
| 6 | 기존 필드 유지 | `141_lotNum` → 로트 약자 | textbox | - |
| 7 | 인정 관련 필드 유지 | `142~145_*` | textbox/date | - |
**storage_type 설정:** `json` (attributes JSON에서 읽기)
### 5.2 PT(부품) 필드 설정 정비
**현재 문제:** 빈 field_key 필드 존재, 부품유형별 조건부 필드가 복잡
**수정 방안:**
- 빈 field_key 필드 수정 또는 비활성화
- `Part_type` 드롭다운 options를 실제 부품 유형으로 갱신
- 부품유형별 `display_condition` JSON 설정으로 조건부 표시
### 5.3 RM/SM/CS 필드 설정 정비
**현재 문제:** 드롭다운 options가 플레이스홀더 값
**수정 방안:**
- RM: `100_item_name` options → 실제 원자재명 (철판, 알루미늄, 스테인리스, 아연도금강판 등)
- RM: `101~104_specification_*` options → 실제 규격 값으로 교체
- SM: `107_item_name` options → 실제 부자재명
- SM: `108~109_specification_*` options → 실제 규격 값
### 5.4 FG item_details 데이터 보완
**현재 문제:** FG 18개 품목의 `item_details` 레코드 없음
**수정 방안:** FG 18개 품목에 대해 `item_details` 생성
```
item_id: (각 FG item id)
is_sellable: 1
is_purchasable: 0
is_producible: 1
product_category: (스크린/철재)
item_name: (SAM name)
specification: (마감유형-가이드레일유형)
```
### 5.5 드롭다운 options 실데이터 매핑
**5130 데이터 기반으로 매핑할 값:**
| 필드 | 현재 options | 목표 options |
|------|-------------|-------------|
| FG model_name | 없음 | KSS01, KSE01, KWE01, KQTS01, KTE01, KSS02 |
| FG major_category | 없음 | 스크린, 철재 |
| FG finishing_type | 없음 | SUS마감, EGI마감 |
| FG guiderail_type | 없음 | 벽면형, 측면형 |
| PT Part_type | 확인 필요 | 조립부품, 벤딩부품, 구매부품, BD부품 |
| RM 100_item_name | 기존값 유지 | 철판, 알루미늄, 스테인리스, 아연도금강판 |
| RM 규격 101~104 | 옵션1-1 등 (임시값) | 실제 규격 값으로 교체 필요 |
### 5.6 고정컬럼-동적필드 섀도잉 정리
> 상세 분석: 섹션 4.2.3 참조
#### 5.6.1 is_active 중복 정리 (⚠️ 컨펌 필요)
**목표:** `items.is_active` 고정 컬럼만으로 활성/비활성 상태를 관리하도록 통합
**방안 A (권장): 동적 필드를 is_common=1 공통필드(id:163)로 교체**
각 page에서 중복 동적 필드를 제거하고, 공통 필드 `is_active`(id:163)를 entity_relationship으로 연결:
| 대상 page | 제거할 동적 필드 | 대체 | 작업 내용 |
|-----------|-----------------|------|----------|
| SM (id:1017) | id:164 (`field_163`) | id:163 (`is_active`) | 1) section(id:94)→field(id:164) 관계 삭제 2) section(id:94)→field(id:163) 관계 추가 |
| PT 측면규격 (id:99) | id:105 (`105_state`) | id:163 (`is_active`) | 1) section(id:99)→field(id:105) 관계 삭제 2) section(id:99)→field(id:163) 관계 추가 |
| PT 측면규격 (id:99) | id:133 (`133_state`) | id:163 (`is_active`) | 1) section(id:99)→field(id:133) 관계 삭제 (id:163 이미 추가됨) |
| PT 기본정보 (id:95) | id:138 (`138_state`) | id:163 (`is_active`) | 1) section(id:95)→field(id:138) 관계 삭제 2) section(id:95)→field(id:163) 관계 추가 |
| FG 기본정보 (id:102) | id:138 (`138_state`) | id:163 (`is_active`) | 1) section(id:102)→field(id:138) 관계 삭제 2) section(id:102)→field(id:163) 관계 추가 |
| PT 기본정보 (id:95) | id:152 (`null` key) | id:163 (`is_active`) | 1) section(id:95)→field(id:152) 관계 삭제 (id:163 이미 추가됨) |
**추가 작업:**
- 제거된 동적 필드(id:105,131,133,138,152,164)는 `is_active=0`으로 비활성화 (삭제 대신 비활성화하여 안전)
- 기존 `items.attributes` JSON에 `105_state`, `138_state` 등의 키로 저장된 값이 있다면, 해당 값을 `items.is_active` 컬럼과 동기화 필요 여부 확인
**방안 B (보수적): 동적 필드의 field_key를 is_active로 변경만**
동적 필드를 유지하되, `source_column``is_active`로, `storage_type``column`으로 변경하여 같은 컬럼을 가리키게 함. 관계 변경 없이 안전하지만, 불필요한 필드가 잔존함.
**실행 전 확인 사항:**
```sql
-- 동적 필드에 실제 데이터가 저장되어 있는지 확인
SELECT id, code, name,
JSON_EXTRACT(attributes, '$.105_state') as s105,
JSON_EXTRACT(attributes, '$.131_state') as s131,
JSON_EXTRACT(attributes, '$.133_state') as s133,
JSON_EXTRACT(attributes, '$.138_state') as s138,
JSON_EXTRACT(attributes, '$.field_163') as f163
FROM items
WHERE tenant_id = 287 AND deleted_at IS NULL
AND (
JSON_EXTRACT(attributes, '$.105_state') IS NOT NULL
OR JSON_EXTRACT(attributes, '$.131_state') IS NOT NULL
OR JSON_EXTRACT(attributes, '$.133_state') IS NOT NULL
OR JSON_EXTRACT(attributes, '$.138_state') IS NOT NULL
OR JSON_EXTRACT(attributes, '$.field_163') IS NOT NULL
);
```
#### 5.6.2 null key 필드 비활성화 (✅ 즉시 가능)
```sql
-- item_fields id:152 (field_key=NULL, 품목상태, PT page)
-- key가 없어서 데이터 매핑 불가. 비활성화 처리.
UPDATE item_fields SET is_active = 0 WHERE id = 152 AND tenant_id = 287;
```
#### 5.6.3 미연결 필드 비활성화 (✅ 즉시 가능)
entity_relationship에 연결되지 않아 어떤 폼에도 표시되지 않는 필드:
```sql
-- 미연결 필드 5건 비활성화
UPDATE item_fields SET is_active = 0
WHERE id IN (116, 117, 129, 131, 136) AND tenant_id = 287;
-- 확인: 이 필드들이 실제 미연결인지 재검증
SELECT f.id, f.field_key, f.field_name, er.id as rel_id
FROM item_fields f
LEFT JOIN entity_relationships er
ON er.child_type = 'field' AND er.child_id = f.id AND er.tenant_id = 287
WHERE f.id IN (116, 117, 129, 131, 136) AND f.tenant_id = 287;
-- rel_id가 모두 NULL이면 확실히 미연결
```
| field id | field_key | 비활성화 이유 |
|----------|-----------|--------------|
| 116 | `116_bending_parts` | 미연결. id:122(`122_bending_parts`)와 중복 |
| 117 | `117_purchase_parts` | 미연결. id:132(`132_PurchasedItemName`)와 중복 |
| 129 | `unit_2` | 미연결. id:98(`unit`)과 중복 |
| 131 | `131_state` | 미연결. is_active 중복 |
| 136 | `unit_3` | 미연결. id:98(`unit`)과 중복 |
#### 5.6.4 다음에 정리할 항목 (이번 범위 밖)
| 유형 | 건수 | 다음에 정리하는 이유 |
|------|------|---------------------|
| `name` (품목명) 중복 | 8건 | PT 부품유형별 분기 UI(조립부품명/벤딩부품명/구매부품명)와 맞물림. `display_condition` 기반 렌더링 로직 변경 필요 |
| `unit` (단위) 중복 | 2건 (활성) | id:98이 4개 page에서 사용 중. 공통필드(id:156)와의 역할 분담 정책 결정 필요 |
| `specification` (규격) 중복 | 6건 | 원자재 4단 규격은 의도된 설계. `item_details.specification`과의 동기화 정책 필요 |
| `description` (비고) 중복 | 4건 | `note1/note2/note3` 용도별 분리가 의도일 수 있음 |
| `certification_*` 중복 | 3건 | FG item_details 생성(5.4) 후 연계 검토 |
| `part_type` 중복 | 1건 | PT에서 정상 사용 중 |
---
## 6. 컨펌 대기 목록
| # | 항목 | 변경 내용 | 영향 범위 | 상태 |
|---|------|----------|----------|------|
| 1 | FG 필드 추가 | model_name, major_category, finishing_type, guiderail_type 4개 필드 추가 | item_fields, entity_relationships | ⏳ 대기 |
| 2 | PT 빈 field_key 처리 | id:152 비활성화 | item_fields | ⏳ 대기 |
| 3 | RM/SM options 교체 | 플레이스홀더 → 실제 값 | item_fields.options | ⏳ 대기 |
| 4 | FG item_details 생성 | 18건 INSERT | item_details | ⏳ 대기 |
| 5 | is_active 중복 필드 통합 | 6개 동적필드 → 공통필드(id:163)로 교체 | entity_relationships, item_fields | ⏳ 대기 |
| 6 | 미연결 필드 비활성화 | id:116,117,129,131,136 비활성화 | item_fields | ⏳ 대기 |
---
## 7. 변경 이력
| 날짜 | 항목 | 변경 내용 | 파일 | 승인 |
|------|------|----------|------|------|
| 2026-01-31 | - | 문서 초안 작성 및 Phase 1 분석 완료 | - | - |
| 2026-01-31 | 4.2.3 | 고정컬럼-동적필드 섀도잉 분석 추가 (is_active 6건, null key 1건, 미연결 5건) | - | - |
| 2026-01-31 | 5.6 | Phase 2에 섀도잉 정리 작업(2.6) 추가. 구체적 SQL, field id, entity_relationship 변경 계획 명시 | - | - |
| 2026-01-31 | 2.6 | Phase 2.6 실행 완료: null key(id:152) 비활성화, 미연결 5건(id:116,117,129,131,136) 비활성화, is_active 중복 통합(rel 7건 삭제, 공통필드 163을 section 94/95/99/102에 연결, 동적필드 105/133/138/164 비활성화). id:116,117은 계획과 달리 고아 section(97,98)에 연결 확인됨 | - | ✅ |
| 2026-01-31 | 2.1 | FG 필드 4개 추가(id:177-180: model_name/major_category/finishing_type/guiderail_type), section 102 연결, order_no 정렬 | item_fields, entity_relationships | ✅ |
| 2026-01-31 | 2.2 | PT Part_type options 갱신: BD부품 추가, 라벨 간소화 (4개: 조립/벤딩/구매/BD) | item_fields id:110 | ✅ |
| 2026-01-31 | 2.3 | RM options 실데이터 교체(재질4종, 두께7종, 폭1종, 길이6종), SM options 11개 카테고리. SM 규격 빈 배열 설정 | item_fields id:100-104,107-109 | ✅ |
| 2026-01-31 | 2.5 | 드롭다운 options 매핑 2.1~2.3에서 함께 완료 | - | ✅ |
| 2026-01-31 | 2.4 | FG item_details 18건 생성(id:524-541). is_sellable=1, is_producible=1, product_category/item_name/specification 설정 | item_details | ✅ |
---
## 8. 참고 문서
- **DB 스키마**: `docs/specs/database-schema.md`
- **품목 정책**: `docs/rules/item-policy.md`
- **품질 체크리스트**: `docs/standards/quality-checklist.md`
- **빠른 시작**: `docs/quickstart/quick-start.md`
- **API 규칙**: `docs/standards/api-rules.md`
- **기존 이관 계획**: `docs/plans/items-migration-kyungdong-plan.md`
- **MNG 필드 관리 계획**: `docs/plans/mng-item-field-management-plan.md`
---
## 9. 새 세션 작업 시작 가이드
### 9.0 이 문서만으로 작업을 시작하는 방법
**1단계: 현재 상태 확인**
- 이 문서의 "📍 현재 진행 상태" 섹션에서 마지막 완료 작업과 다음 작업 확인
- Phase 2 대상 범위(섹션 2.2) 테이블에서 ⏳/✅ 상태 확인
**2단계: 환경 확인**
```bash
# Docker 컨테이너 실행 확인
docker ps | grep sam
# 테넌트 데이터 접근 확인 (tenant_id=287)
docker exec sam-api-1 php artisan tinker --execute="echo DB::table('items')->where('tenant_id', 287)->count();"
# 기대값: 780
```
**3단계: 작업 실행 순서 (Phase 2)**
```
┌─────────────────────────────────────────────────────────────────┐
│ Phase 2 실행 순서 (권장) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 2.6 섀도잉 정리 (안전, 선행 작업) │
│ ├─ 5.6.2 null key 비활성화 (즉시 가능) │
│ ├─ 5.6.3 미연결 필드 비활성화 (즉시 가능) │
│ └─ 5.6.1 is_active 중복 통합 (⚠️ 컨펌 필요) │
│ └─ 사전: 5.6.1 확인 SQL 실행하여 기존 데이터 유무 확인 │
│ │
│ 2.1 FG 필드 설정 정비 (⚠️ 컨펌 필요) │
│ └─ 섹션 5.1 참조: 4개 필드 추가 + entity_relationship 연결 │
│ │
│ 2.2 PT 필드 설정 정비 (⚠️ 컨펌 필요) │
│ └─ 섹션 5.2 참조 │
│ │
│ 2.3 SM/RM/CS 필드 설정 정비 │
│ └─ 섹션 5.3 참조 │
│ │
│ 2.5 드롭다운 options 실데이터 매핑 │
│ └─ 섹션 5.5 참조: FG 4개 + PT/RM 교체 │
│ │
│ 2.4 FG item_details 데이터 보완 │
│ └─ 섹션 5.4 참조: 18건 INSERT │
│ │
│ 검증 │
│ └─ 섹션 10 테스트 케이스 실행 │
│ │
└─────────────────────────────────────────────────────────────────┘
```
**4단계: 작업 대상 테이블/ID 빠른 참조**
| 테이블 | tenant_id | 주요 ID 범위 | 용도 |
|--------|-----------|-------------|------|
| `item_pages` | 287 | 1015~1019 (CS/RM/SM/PT/FG) | 품목유형별 폼 |
| `item_sections` | 287 | 92~102 | 섹션 그룹 |
| `item_fields` | 287 | 96~164 | 필드 정의 |
| `entity_relationships` | 287 | page→section 26건, section→field 66건 | 계층 연결 |
| `items` | 287 | 13147~13164 (FG 18건) | 품목 데이터 |
| `item_details` | - | PT 129건, FG 0건 | 품목 상세 |
**5단계: 수정 금지 영역 (반드시 확인)**
- `items.bom` JSON 구조 변경 금지
- `items.code` 체계 변경 금지
- `items.item_category` 값 변경 금지
- `FormulaEvaluatorService` 수정 금지
- 위 항목은 견적 로직에 직접 영향. 상세: 섹션 4.4.3
---
## 10. Serena 메모리 관리 정책
### 10.1 세션 시작 시 (Load Strategy)
```javascript
read_memory("item-data-alignment-state") // 1. 상태 파악
read_memory("item-data-alignment-snapshot") // 2. 사고 흐름 복구
read_memory("item-data-alignment-active-symbols") // 3. 작업 대상 파악
```
### 10.2 작업 중 관리 (Context Defense)
| 컨텍스트 잔량 | Action | 내용 |
|--------------|--------|------|
| **30% 이하** | Snapshot | `write_memory("item-data-alignment-snapshot", "코드변경+논의요약")` |
| **20% 이하** | Context Purge | `write_memory("item-data-alignment-active-symbols", "주요 수정 파일/함수")` |
| **10% 이하** | Stop & Save | 최종 상태 저장 후 세션 교체 권고 |
### 10.3 Serena 메모리 구조
- `item-data-alignment-state`: { phase, progress, next_step, last_decision } (JSON 구조)
- `item-data-alignment-snapshot`: 현재까지의 논의 및 코드 변경점 요약 (Text)
- `item-data-alignment-rules`: 견적 영향 금지 등 불변 규칙 (Text)
- `item-data-alignment-active-symbols`: 현재 수정 중인 파일/심볼 리스트 (List)
---
## 11. 검증 결과
> 작업 완료 후 이 섹션에 검증 결과 추가
### 11.1 테스트 케이스
| 입력값 | 예상 결과 | 실제 결과 | 상태 |
|--------|----------|----------|------|
| FG-KSE01-벽면형-EGI 조회 | model_name=KSE01, finishing_type=EGI마감 등 표시 | - | ⏳ |
| PT-가이드레일 조회 | base_price, 부품유형 등 표시 | - | ⏳ |
| FG BOM 표시 | 가이드레일×2, 하단마감재×1, L-BAR×2 표시 | - | ⏳ |
| 견적 생성 (수정 후) | 기존과 동일한 견적 금액 산출 | - | ⏳ |
| SM 품목 활성 여부 | `items.is_active` 컬럼 값 표시 (field_163 아님) | - | ⏳ |
| PT 품목상태 필드 | `items.is_active` 공통 필드로 통합 표시 | - | ⏳ |
| FG 품목상태 필드 | `items.is_active` 공통 필드로 통합 표시 | - | ⏳ |
| id:152 (null key) | 폼에 표시되지 않음 (비활성화) | - | ⏳ |
| 미연결 필드 5건 | 폼에 표시되지 않음 (비활성화) | - | ⏳ |
### 11.2 성공 기준 달성 현황
| 기준 | 달성 | 비고 |
|------|------|------|
| FG 18개 품목이 모든 필드 정상 표시 | ⏳ | |
| PT 부품유형별 조건부 필드 정상 작동 | ⏳ | |
| RM/SM 드롭다운 실제 값 표시 | ⏳ | |
| 견적 금액 변동 없음 (회귀 테스트) | ⏳ | |
| 품목 목록 → 상세 → 문서 데이터 흐름 정상 | ⏳ | |
| is_active 중복 필드 통합 완료 (6건 → 공통필드 1개) | ⏳ | |
| null key 필드(id:152) 비활성화 | ⏳ | |
| 미연결 필드(id:116,117,129,131,136) 비활성화 | ⏳ | |
---
## 12. 자기완결성 점검 결과
### 12.1 체크리스트 검증
| # | 검증 항목 | 상태 | 비고 |
|---|----------|:----:|------|
| 1 | 작업 목적이 명확한가? | ✅ | 섹션 1.1 배경 |
| 2 | 성공 기준이 정의되어 있는가? | ✅ | 섹션 11.2 |
| 3 | 작업 범위가 구체적인가? | ✅ | 섹션 2 대상 범위 |
| 4 | 의존성이 명시되어 있는가? | ✅ | 견적 금지 영역 명시 (4.4.3), 수정금지 (9.0 5단계) |
| 5 | 참고 파일 경로가 정확한가? | ✅ | 섹션 8 |
| 6 | 단계별 절차가 실행 가능한가? | ✅ | 섹션 9.0 (새 세션 가이드), 섹션 5 (상세 수정 계획) |
| 7 | 검증 방법이 명시되어 있는가? | ✅ | 섹션 11.1 |
| 8 | 모호한 표현이 없는가? | ✅ | 구체적 테이블/필드/ID, SQL 쿼리 명시 |
### 12.2 새 세션 시뮬레이션 테스트
| 질문 | 답변 가능 | 참조 섹션 |
|------|:--------:|----------|
| Q1. 이 작업의 목적은 무엇인가? | ✅ | 1.1 배경 |
| Q2. 어디서부터 시작해야 하는가? | ✅ | 9.0 3단계 (실행 순서 다이어그램) |
| Q3. 어떤 데이터를 수정해야 하는가? | ✅ | 5.1~5.6 (상세 수정 계획 + SQL) |
| Q4. 절대 건드리면 안 되는 것은? | ✅ | 9.0 5단계 + 4.4.3 (수정 금지 영역) |
| Q5. 작업 완료 확인 방법은? | ✅ | 11.1 테스트 케이스, 11.2 성공 기준 |
| Q6. 막혔을 때 참고 문서는? | ✅ | 8. 참고 문서 |
| Q7. DB 접속/환경 세팅은? | ✅ | 9.0 2단계 (환경 확인 명령어) |
| Q8. 테이블/ID 범위는? | ✅ | 9.0 4단계 (빠른 참조 테이블) |
**결과**: 8/8 통과 → ✅ 자기완결성 확보
---
*이 문서는 /sc:plan 스킬로 생성되었습니다.*