# 문서 시스템 개선 계획 — SuperClaude 페르소나 리뷰

> **리뷰 대상**: `docs/dev_plans/document-system-improvement-plan.md`
> **리뷰 일자**: 2026-02-26
> **리뷰어**: Backend Architect, System Architect, Quality Engineer
> **상태**: 정책 결정 완료

---

## 리뷰 요약

| 페르소나 | CRITICAL | IMPORTANT | MINOR |
|----------|:--------:|:---------:|:-----:|
| Backend Architect | 4 | 8 | 6 |
| System Architect | 3 | 5 | 5 |
| Quality Engineer | 4 | 7 | 6 |

교차 검증 후 **중복 제거된 핵심 이슈**: CRITICAL 5건, IMPORTANT 7건, MINOR 4건

---

## 🔴 CRITICAL

### C1. row_index 의미론적 이중성

**지적자**: 3명 모두
**위치**: 계획서 4.2.3절

**문제**: `document_data.row_index`가 공정에 따라 다른 엔티티를 참조한다.
- 스크린/슬랫: row_index = WorkOrderItem(개소)
- 절곡 TO-BE: row_index = BOM 구성품
- 수주별(추후): row_index = 전체 개소

DB에 "이 row가 무엇인지" 구분하는 메타데이터가 없다.

**권장안**:
- A) `document.options`에 `{"row_type": "bom_item"}` 저장
- B) `field_key`에 구성품 식별자 포함: `s{section}_r{row}_item{id}_c{column}`
- C) document_data에 row_mapping 별도 저장

| 정책 결정 | |
|----------|---|
| **선택** | row_index = 개소(WorkOrderItem) 통일. 구성품은 field_key에 인코딩 (`b{idx}_ok`, `b{idx}_p{pt}_n1` 등) |
| **근거** | 기존 스크린/슬랫과 동일한 row_index 의미 유지. 구성품 구분은 field_key 패턴으로 해결. DB 스키마 변경 불필요. 이미 TemplateInspectionContent save/restore에 구현 완료. |
| **결정일** | 2026-02-26 |

---

### C2. 기존 절곡 검사 데이터의 실제 저장 구조 오해

**지적자**: Quality Engineer (핵심 발견)
**위치**: 계획서 4.2.3절 하위호환 단락

**문제**: 계획서는 절곡 검사가 EAV(`document_data`)에 row_index 기반 저장된다고 가정했으나, **실제로는** `work_order_items.options.inspection_data`에 JSON으로 저장되며 `products[].id` 매칭 기반으로 복원된다 (BendingInspectionContent L186-199).

**영향**: 하위호환 전략의 전제 자체가 틀림. 마이그레이션 전략 재설계 필요.

**권장안**:
- A) 기존 options.inspection_data 방식을 유지하고, 동적 구성품도 같은 패턴으로 저장
- B) EAV(document_data)로 전환하되 기존 데이터 마이그레이션 포함
- C) 양쪽 모두 지원 (과도기)

| 정책 결정 | |
|----------|---|
| **선택** | Option B — EAV(document_data)로 전환. 기존 options.inspection_data(InspectionInputModal 경로)는 당분간 병행 유지. |
| **근거** | TemplateInspectionContent가 document_data EAV 저장/복원을 담당. InspectionInputModal은 기존 options 경로 유지 (개소별 빠른 입력). 두 경로가 독립적으로 동작하므로 마이그레이션 불필요. |
| **결정일** | 2026-02-26 |

---

### C3. TemplateInspectionContent에 이미 절곡 동적 로직 존재

**지적자**: System Architect (핵심 발견)
**위치**: 계획서 Phase 2 전체 방향

**문제**: 계획서는 "새 API → BendingInspectionContent 동적화"를 핵심으로 삼았으나, `TemplateInspectionContent.tsx`에 이미 구현됨:
- `buildBendingProducts()` (L209-274): `order.bendingInfo`에서 동적 구성품 생성
- `DEFAULT_GAP_PROFILES`: 제품별 간격 포인트 기본값
- `bendingExpandedRows`: BOM 기반 동적 행 확장

**영향**: Phase 2 방향 자체를 재검토해야 할 수 있음. 두 가지 경로:
- A) TemplateInspectionContent의 bending 지원을 확장 → BendingInspectionContent 레거시 대체 (중복 제거)
- B) BendingInspectionContent를 독립 동적화 (기존 계획 유지, 중복 감수)

**권장안**: A) 통합 방향 (System Architect 강력 권장)

| 정책 결정 | |
|----------|---|
| **선택** | Option A — TemplateInspectionContent 통합 방향. BendingInspectionContent는 레거시로 유지하되 신규 개발은 TemplateInspectionContent에서 진행. |
| **근거** | TemplateInspectionContent에 이미 buildBendingProducts, bendingExpandedRows, DEFAULT_GAP_PROFILES 구현됨. bending save/restore 추가 완료 (커밋 7b8b5cf5, 36052f3e). 중복 개발 방지. |
| **결정일** | 2026-02-26 |

---

### C4. BOM 변경 시 기존 검사 문서 데이터 무결성

**지적자**: Backend Architect, Quality Engineer
**위치**: 계획서 4.2.2, 2.4절

**문제**: 검사 문서(DRAFT) 저장 후 BOM이 변경되면 구성품↔데이터 매핑 불일치. APPROVED 문서 조회 시에도 현재 BOM으로 렌더링하면 데이터 어긋남.

**권장안**:
- A) 검사 문서 생성 시점의 BOM 스냅샷을 `document.options.bom_snapshot`에 저장
- B) document_data에 구성품 식별자를 field_key에 포함시켜 순서 독립적 매핑
- C) BOM 변경 시 기존 DRAFT 문서에 경고 표시 + 수동 재매핑 UI

| 정책 결정 | |
|----------|---|
| **선택** | 현행 유지 → BOM 동적화 시점에 Option A+B 병행 (bom_snapshot 저장 + 구성품 식별자 기반 field_key) |
| **근거** | 현재 buildBendingProducts()가 고정 순서로 생성하므로 인덱스 기반 field_key로 충분. BOM API 도입 시 `b{productId}_...` 형태로 전환 + document.options에 스냅샷 저장. |
| **결정일** | 2026-02-26 |

---

### C5. product_code 선행 의존성 fallback 부재

**지적자**: 3명 모두
**위치**: 계획서 8절, 4.1.3절

**문제**: product-code-traceability-plan Phase 1 미완료 시 API가 어떤 제품코드로 구성품을 결정하는지 fallback 없음. product_code 없는 기존 작업지시에 대한 에러 처리도 미정의.

**권장안**:
- A) `order_nodes.options`에서 product_code 추출하는 fallback 경로 추가
- B) product_code 없으면 KWE01 기본값으로 추정 (현행 동작과 동일)
- C) product_code 없으면 빈 배열 반환 → 프론트에서 DEFAULT_PRODUCTS 사용

| 정책 결정 | |
|----------|---|
| **선택** | product_code 전파 누락 수정 (product-code-traceability-plan Phase 1). order_nodes.options → work_order_items.options로 product_code 복사. fallback이 아니라 버그 수정. |
| **근거** | 데이터는 order_nodes.options에 이미 존재. createWorkOrders에서 복사하지 않은 단순 누락. Phase 1 완료 시 해결. 프론트 DEFAULT_PRODUCTS는 Phase 1 완료 전까지 임시 fallback으로 유지. |
| **결정일** | 2026-02-26 |

---

## 🟡 IMPORTANT

### I1. INITIAL_PRODUCTS vs DEFAULT_GAP_PROFILES 기준치 불일치

**지적자**: System Architect
**위치**: BendingInspectionContent L71-135 vs TemplateInspectionContent L176-206

**문제**: 동일 구성품의 치수 기준이 두 파일에서 다름.
- `INITIAL_PRODUCTS` 가이드레일 벽면: 5포인트 (30, 80, 45, 40, 34)
- `DEFAULT_GAP_PROFILES` guideRailWall: 4포인트 (30, 78, 25, 45)

**권장안**: Phase 1 분석에서 5130 레거시와 대조하여 정확한 기준치 확정 → Single Source of Truth로 통합

| 정책 결정 | |
|----------|---|
| **선택** | TemplateInspectionContent의 DEFAULT_GAP_PROFILES를 기준으로 통일. 정확한 수치는 5130 레거시 확인 후 보정. BendingInspectionContent의 INITIAL_PRODUCTS는 레거시로 동결. |
| **근거** | C3에서 TemplateInspectionContent 통합으로 결정. Single Source of Truth = DEFAULT_GAP_PROFILES. |
| **결정일** | 2026-02-26 |

---

### I2. createInspectionDocument 트랜잭션 + Race Condition

**지적자**: Backend Architect
**위치**: WorkOrderService.php L2078-2173

**문제**: 기존 코드 결함. `DB::transaction()` 없이 조회→분기→create/update 실행. 절곡 동적화로 API 호출 추가되면 race window 확대.

**권장안**: Phase 2 전에 `lockForUpdate()` + `DB::transaction()` 선행 수정

| 정책 결정 | |
|----------|---|
| **선택** | 수용 — `lockForUpdate()` + `DB::transaction()` 추가. 별도 작업으로 진행. |
| **근거** | 기존 코드 결함. 절곡 동적화와 무관하게 수정 필요. |
| **결정일** | 2026-02-26 |

---

### I3. InspectionInputModal 절곡 입력 단위 불일치

**지적자**: System Architect, Quality Engineer
**위치**: InspectionInputModal.tsx L869-938

**문제**: InspectionInputModal의 절곡은 "개소 단위 단순 입력" (bendingStatus + length + gapPoints 5개 고정), BendingInspectionContent는 "구성품 단위 상세 입력" (7항목). Phase 2.3의 구체적 UI 설계가 없음.

**권장안**:
- A) 구성품별로 InspectionInputModal 여러 번 호출
- B) InspectionInputModal 내부에 구성품 탭/아코디언 추가
- C) InspectionInputModal은 절곡에서 사용하지 않고, 검사 성적서에서 직접 입력 (편집 모드 추가)

**문제 재정의**: "입력 단위 불일치"가 아니라 **개소별 입력 → 구성품별 성적서 매핑 로직 자체가 미설계**. 구성품의 수량이 10개이고 5개소면 개소당 2개인데, 1번 개소만 검사 완료 시 성적서에서 2/10을 어떻게 보여줄지 설계가 없음.

| 정책 결정 | |
|----------|---|
| **선택** | 절곡은 구성품별/개소별/수주별 3가지 관점 지원. 주 입력은 구성품별. 개소별·수주별은 조회+부분 입력 가능. 각 관점마다 작업일지 + 검사 성적서 보기. |
| **근거** | 실제 업무는 구성품 단위 검사. 부분 출하(10개 중 2개 선출하) 시 개소별 필요. 수주별은 전체 현황 조회. 화면 구성·데이터 매핑·UI 설계는 기획자와 별도 협의 후 진행. |
| **결정일** | 2026-02-27 |

---

### I4. 롤백 전략 부재

**지적자**: Quality Engineer
**위치**: 계획서 전체

**문제**: Phase 2 전환 실패 시 복구 방법 없음.

**권장안**:
- feature flag로 레거시/신규 모드 전환
- 데이터 복원 스크립트
- 단계별 canary 배포

| 정책 결정 | |
|----------|---|
| **선택** | 기각 — 롤백 메커니즘이 이미 존재. 템플릿 유무(document_template_id)로 TemplateInspectionContent ↔ 레거시 컴포넌트 자동 전환됨. |
| **근거** | InspectionReportModal L386-418: activeTemplate 있으면 신규, 없으면 레거시. 공정의 document_template_id를 NULL로 변경하면 즉시 레거시로 롤백. 별도 feature flag 불필요. |
| **결정일** | 2026-02-26 |

---

### I5. API 엔드포인트 설계 — 공정 하드코딩

**지적자**: Backend Architect, System Architect
**위치**: 계획서 4.1.3절

**문제**: `/work-orders/{id}/bending-inspection-items`에 "bending" 하드코딩. 확장성 부족.

**권장안**:
- A) 범용: `GET /work-orders/{id}/inspection-items?type=bending`
- B) 제품 기반: `GET /products/{code}/inspection-items?finish_type=S1`
- C) 검사 설정: `GET /work-orders/{id}/inspection-config` (공정 자동 판별)

| 정책 결정 | |
|----------|---|
| **선택** | Option C — `GET /work-orders/{id}/inspection-config` (공정 자동 판별). 작업지시 ID만으로 공정 타입 + 구성품 목록 반환. |
| **근거** | 프론트가 공정 타입을 하드코딩할 필요 없음. 작업지시 → 공정 → 템플릿/BOM 자동 결정. 확장성 확보. |
| **결정일** | 2026-02-26 |

---

### I6. 검증 테스트 케이스 부족

**지적자**: Quality Engineer
**위치**: 계획서 13.2절

**누락 케이스**:
- KSS02 제품코드
- 마감유형 S1/S2/S3 각각
- 구성품 수 7개 미만/초과
- 저장→조회→재저장 사이클
- mng show.blade.php 렌더링
- 인쇄 레이아웃
- API 에러 시나리오 (BOM 미등록, 타임아웃, 빈 배열)

| 정책 결정 | |
|----------|---|
| **선택** | 수용 — 누락 테스트 케이스 목록을 테스트 계획에 추가. 구현 시점에 반영. |
| **근거** | KSS02, 마감유형별, 저장→조회→재저장, mng 렌더링, 인쇄, API 에러 시나리오 모두 필요. |
| **결정일** | 2026-02-26 |

---

### I7. 수주별 검사 Option A 권장 근거 불충분

**지적자**: System Architect
**위치**: 계획서 4.3.1절

**문제**: Option A(linkable=Order)의 트레이드오프 분석 부족. 개소별↔수주별 데이터 중복, 여러 WorkOrder 통합 시 row_index 비결정 등.

**권장안**: "입력은 개소별, 출력은 수주별" (scenario_1) 기본 채택. 수주별 Document는 읽기 전용 뷰로 설계.

| 정책 결정 | |
|----------|---|
| **선택** | "입력은 개소별, 출력은 수주별" 채택. 수주별 Document는 읽기 전용 뷰로 설계. |
| **근거** | 개소별 입력이 현재 워크플로우와 일치. 수주별 통합 조회는 별도 뷰로 분리하면 데이터 중복 없음. |
| **결정일** | 2026-02-26 |

---

## 🟢 MINOR

### M1. 멀티테넌시 — 신규 API의 tenant_id 격리 명시 필요

**지적자**: Backend Architect
**위치**: 계획서 4.1.3절

BOM 조회 시 BelongsToTenant 스코프 적용이 명시되지 않음. 계획서에 한 줄 추가 필요.

| 정책 결정 | |
|----------|---|
| **선택** | 수용 — 신규 API에 BelongsToTenant 스코프 필수 적용. SAM 기본 원칙. |
| **결정일** | 2026-02-27 |

---

### M2. 성공 기준에 성능 지표 누락

**지적자**: Backend Architect
**위치**: 계획서 9절

BOM 동적 로딩 추가로 응답 시간 증가 가능. "구성품 API 응답 시간 < 200ms" 같은 기준 필요.

| 정책 결정 | |
|----------|---|
| **선택** | 수용 — 성공 기준에 "구성품 API 응답 시간 < 200ms" 추가. |
| **결정일** | 2026-02-27 |

---

### M3. 마스터 문서와 상태 동기화 누락

**지적자**: Quality Engineer
**위치**: document-system-master.md

이 개선 계획이 마스터 문서의 어디에 위치하는지(Phase 5.4? 6?) 미정의.

| 정책 결정 | |
|----------|---|
| **선택** | 구현 시점에 계획서에 마스터 문서 위치 명시. |
| **결정일** | 2026-02-27 |

---

### M4. getInspectionData() 반환 타입 이질성 + 네이밍 불일치

**지적자**: System Architect
**위치**: BendingInspectionContent vs TemplateInspectionContent

- BendingInspectionContent: `{ products: [...] }` (커스텀)
- TemplateInspectionContent: `{ template_id, records: [...] }` (EAV 정규화)
- `ProductRow` vs `BendingProduct`: 동일 개념 다른 타입명
- `gapPoints` 구조가 두 파일에서 완전히 다름

| 정책 결정 | |
|----------|---|
| **선택** | 통일 불필요 — C3에서 TemplateInspectionContent 통합 결정. BendingInspectionContent의 타입(ProductRow, INITIAL_PRODUCTS 등)은 레거시로 동결. 신규 개발은 TemplateInspectionContent의 BendingProduct, DEFAULT_GAP_PROFILES 사용. |
| **결정일** | 2026-02-27 |

---

## 정책 결정 진행 현황

| # | 이슈 | 정책 결정 | 상태 |
|---|------|----------|:----:|
| C1 | row_index 의미론 | row_index=개소 통일, 구성품은 field_key 인코딩 | ✅ |
| C2 | 실제 저장 구조 오해 | EAV 전환, options 경로 병행 유지 | ✅ |
| C3 | TemplateInspectionContent 기존 동적 로직 | 통합 방향 (Option A) | ✅ |
| C4 | BOM 변경 시 데이터 무결성 | 현행 유지 → BOM 동적화 시 A+B 병행 | ✅ |
| C5 | product_code fallback | 버그 수정 (전파 누락), fallback 아님 | ✅ |
| I1 | 기준치 불일치 | DEFAULT_GAP_PROFILES 기준, 5130 대조 후 보정 | ✅ |
| I2 | 트랜잭션 Race Condition | 수용 — lockForUpdate + transaction 추가 | ✅ |
| I3 | 개소→구성품 매핑 미설계 | 3관점(구성품/개소/수주) 지원, 주 입력=구성품별. 화면 설계는 별도 기획. | ✅ |
| I4 | 롤백 전략 | 기각 — 템플릿 유무로 이미 전환 가능 | ✅ |
| I5 | API 엔드포인트 설계 | Option C — inspection-config (공정 자동 판별) | ✅ |
| I6 | 테스트 케이스 보강 | 수용 — 누락 케이스 구현 시 반영 | ✅ |
| I7 | 수주별 검사 Option | 입력=개소별, 출력=수주별 읽기 전용 뷰 | ✅ |
| M1 | 멀티테넌시 명시 | 수용 — BelongsToTenant 필수 | ✅ |
| M2 | 성능 지표 | 수용 — API 응답 < 200ms 기준 추가 | ✅ |
| M3 | 마스터 문서 동기화 | 구현 시점에 마스터 문서 위치 명시 | ✅ |
| M4 | 타입/네이밍 통일 | 통일 불필요 — 레거시 동결, 신규는 TemplateInspectionContent 타입 사용 | ✅ |

---

*모든 정책 결정 완료 (16/16) → 계획서(document-system-improvement-plan.md) v2로 반영*