Files
sam-docs/dev/dev_plans/integrated-phase-3.md
권혁성 db63fcff85 refactor: [docs] 팀별 폴더 구조 재편 (공유/개발/프론트/기획)
- 개발팀 전용 폴더 dev/ 생성 (standards, guides, quickstart, changes, deploys, data, history, dev_plans 이동)
- 프론트엔드 전용 폴더 frontend/ 생성 (api/ → frontend/api-specs/)
- 기획팀 폴더 requests/ 생성
- plans/ → dev/dev_plans/ 이름 변경
- README.md 신규 (사람용 안내), INDEX.md 재작성 (Claude Code용)
- resources.md 신규 (노션 링크용, assets/brochure 이관 예정)
- CURRENT_WORKS.md 삭제, TODO.md → dev/ 이동
- 전체 참조 경로 업데이트

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 16:46:03 +09:00

13 KiB
Raw Permalink Blame History

Phase 3: 절곡 검사 동적 구현

통합 계획: integrated-master-plan.md 원본: document-system-improvement-plan.md Phase 2 상태: 구현 완료 + 검증 완료 (14/14 PASS) 의존성: Phase 1 (product_code 전파) + Phase 2A (API 설계) 완료 필수 리뷰 문서: document-system-improvement-review.md


1. 개요

1.1 목표

API 기반 동적 구성품 로딩으로 buildBendingProducts() 고정 로직을 대체한다.

Phase 1에서 product_code 전파 버그를 수정하고, Phase 2A에서 API 설계를 완료한 뒤, 이 Phase에서 실제 구현을 진행한다.

1.2 핵심 구현 항목

  • inspection-config API 구현 (공정 자동 판별)
  • TemplateInspectionContent API 연동 (buildBendingProducts 대체)
  • document_data EAV 저장/복원 검증
  • 트랜잭션 보강 (lockForUpdate + DB::transaction)

1.3 선행 완료 커밋 (react/)

커밋 내용
7b8b5cf5 feat: TemplateInspectionContent 절곡 bending save/restore 지원
54716e63 feat: InspectionReportModal에서 documentRecords prop 전달
36052f3e fix: bending 개소별 저장 fallback 조건 수정

2. 작업 항목

# 작업 항목 상태 비고
3.1 inspection-config API 구현 (공정 자동 판별) BENDING_GAP_PROFILES 상수 + resolveInspectionProcessType
3.2 TemplateInspectionContent API 연동 (buildBendingProducts 대체) API 우선 → buildBendingProducts fallback
3.3 document_data EAV 저장/복원 검증 productIdx 순서 일치 확인 (벽면/측면 모두)
3.4 기존 절곡 검사 데이터 하위 호환 확인 Path A 미수정, Path B fallback 유지
3.5 createInspectionDocument 트랜잭션 보강 DB::transaction + lockForUpdate 적용

3. 상세 구현 내용

3.1 inspection-config API 구현

Phase 2A 설계 기반. 엔드포인트: GET /api/v1/work-orders/{id}/inspection-config

3.1.1 구현 방향

// WorkOrderController 또는 신규 InspectionConfigController
public function inspectionConfig(WorkOrder $workOrder)
{
    // 1. 공정 타입 자동 판별
    $processType = $workOrder->process->type; // screen, slat, bending, etc.

    // 2. product_code 추출 (Phase 1에서 수정됨)
    $productCode = $workOrder->items->first()?->options['product_code'] ?? null;

    // 3. BOM 기반 구성품 조회 (절곡만)
    $items = [];
    if ($processType === 'bending') {
        $items = $this->getBendingComponents($productCode, $workOrder);
    }

    // 4. 템플릿 ID 조회
    $templateId = $workOrder->process->steps()
        ->where('needs_inspection', true)
        ->first()?->document_template_id;

    return ApiResponse::handle(fn() => [
        'work_order_id' => $workOrder->id,
        'process_type' => $processType,
        'product_code' => $productCode,
        'template_id' => $templateId,
        'items' => $items,
    ]);
}

3.1.2 핵심 요구사항

항목 요구사항 정책 근거
멀티테넌시 BelongsToTenant 스코프 필수 적용 M1
응답 시간 < 200ms M2
Fallback BOM 미등록 시 빈 items 배열 반환 C5
공정 판별 프론트가 공정 타입을 하드코딩하지 않음 I5

3.1.3 응답 구조 (예시)

{
  "success": true,
  "data": {
    "work_order_id": 123,
    "process_type": "bending",
    "product_code": "KWE01",
    "template_id": 45,
    "items": [
      {
        "id": "guide_rail_wall",
        "name": "가이드레일 벽면",
        "gap_points": [30, 78, 25, 45],
        "labels": ["상", "중상", "중하", "하"]
      },
      {
        "id": "guide_rail_front",
        "name": "가이드레일 정면",
        "gap_points": [30, 78, 25, 45],
        "labels": ["상", "중상", "중하", "하"]
      }
    ]
  }
}

BOM 미등록이거나 product_code가 없는 경우 items: []를 반환하고, 프론트에서 DEFAULT_GAP_PROFILES를 사용한다.


3.2 TemplateInspectionContent API 연동

3.2.1 현재 코드 구조 (AS-IS)

// TemplateInspectionContent.tsx
const DEFAULT_GAP_PROFILES = { /* L176-206 */ };

function buildBendingProducts(order): BendingProduct[] { /* L209-274 */ }

const isBending =
  order.processType === 'bending' || columns에 point sub_labels 존재;

buildBendingProducts()order.bendingInfo에서 동적 구성품을 생성하지만, 프론트 데이터에 의존하며 BOM 기반이 아니다.

3.2.2 변경 내용 (TO-BE)

// TemplateInspectionContent.tsx (Phase 3 변경)

interface InspectionConfig {
  process_type: string;
  product_code: string;
  items: BendingInspectionItem[];
}

// API 호출 (React Query 또는 Server Action)
const { data: config, isLoading } = useInspectionConfig(workOrderId);

// fallback: API 실패/미응답 → buildBendingProducts 기본값
const bendingProducts = config?.items?.length
  ? mapConfigToProducts(config.items)
  : buildBendingProducts(order);

3.2.3 변경 범위

변경 대상 내용 비고
API 호출 추가 useInspectionConfig(workOrderId) 훅 또는 Server Action 신규
buildBendingProducts 유지 (fallback 용도) 기존 코드 수정 없음
DEFAULT_GAP_PROFILES API 응답의 gap_points로 대체, 미응답 시 기존값 사용 I1 기준 통일
bendingExpandedRows API 기반 구성품으로 행 확장 기존 로직 활용

3.3 document_data EAV 저장 구조 (C1 정책)

3.3.1 저장 구조

row_index 의미: 모든 공정에서 "개소(WorkOrderItem)" 통일

절곡 field_key 패턴:
├─ b{productIdx}_ok              → 구성품 OK/NG 판정
├─ b{productIdx}_ng              → NG 상세
├─ b{productIdx}_value           → 길이/너비 측정값
├─ b{productIdx}_judgment        → 종합 판정
├─ b{productIdx}_p{pointIdx}_n1  → 간격 포인트 측정값 1
├─ b{productIdx}_p{pointIdx}_n2  → 간격 포인트 측정값 2
└─ b{productIdx}_n{n}            → 추가 측정값

이미 save/restore 구현 완료 (커밋 7b8b5cf5) productIdxbuildBendingProducts() 반환 배열의 인덱스

3.3.2 BOM 동적화 시 향후 전환 (C4)

구분 현행 (Phase 3) 향후 (BOM 동적화)
field_key 형식 b{productIdx}_... b{productId}_...
매핑 기준 배열 인덱스 구성품 식별자
BOM 스냅샷 없음 document.options.bom_snapshot 저장

3.4 하위호환 (C2)

두 개의 독립적인 데이터 경로가 존재한다.

Path A: InspectionInputModal
  → work_order_items.options.inspection_data
  → 개소별 빠른 입력 (기존 유지)

Path B: TemplateInspectionContent
  → document_data EAV
  → 검사 성적서 (신규 방식)

→ 두 경로 독립 동작, 마이그레이션 불필요
→ 레거시 데이터(Path A)는 그대로 유지
→ 신규 데이터(Path B)는 EAV에 저장

3.4.1 하위호환 보장 조건

조건 설명 검증 방법
Path A 무변경 InspectionInputModal의 절곡 입력/저장 로직 건드리지 않음 기존 데이터 정상 표시 확인
Path B 독립 TemplateInspectionContentdocument_data EAV만 사용 신규 저장→조회 사이클 검증
롤백 가능 document_template_id NULL 설정 시 레거시 컴포넌트 자동 복귀 I4 정책 결정

3.5 createInspectionDocument 트랜잭션 보강 (I2)

3.5.1 현재 문제

WorkOrderService::createInspectionDocumentDB::transaction() 없이 조회→분기→create/update를 실행한다. 절곡 동적화로 API 호출이 추가되면 race window가 확대된다.

3.5.2 수정 내용

// WorkOrderService::createInspectionDocument
public function createInspectionDocument(WorkOrder $workOrder, ...)
{
    return DB::transaction(function () use ($workOrder, ...) {
        // lockForUpdate로 동시 생성 방지
        $workOrder->lockForUpdate();

        // 기존 document 중복 체크
        $existing = $workOrder->documents()
            ->where('template_id', $templateId)
            ->first();

        if ($existing) {
            return $existing; // 이미 존재하면 반환
        }

        // 신규 생성
        return $this->documentService->create(...);
    });
}

3.5.3 적용 범위

항목 설명
lockForUpdate() 동일 WorkOrder에 대한 동시 문서 생성 방지
DB::transaction() 조회→분기→생성 전체를 원자적으로 처리
중복 체크 template_id 기준 기존 문서 존재 시 생성 대신 반환
별도 작업 절곡 동적화와 무관하게 기존 코드 결함 수정 (I2)

4. 데이터 경로 다이어그램

작업지시(WorkOrder)
│
├─ inspection-config API ← Phase 3 구현
│   ├─ process_type 자동 판별
│   ├─ product_code 추출 (Phase 1에서 수정됨)
│   └─ BOM 기반 구성품 목록 반환
│
├─ TemplateInspectionContent (React)
│   ├─ AS-IS: buildBendingProducts() 고정 로직
│   └─ TO-BE: inspection-config API → 동적 구성품
│       └─ fallback: buildBendingProducts() 유지
│
├─ Path A: InspectionInputModal → options.inspection_data
│   └─ 개소별 빠른 입력 (기존 유지)
│
└─ Path B: TemplateInspectionContent → document_data EAV
    ├─ row_index = 개소(WorkOrderItem)
    ├─ field_key = b{idx}_ok, b{idx}_p{pt}_n1 등
    └─ save/restore 구현 완료 (7b8b5cf5)

5. 검증 계획

# 테스트 예상 결과 실제 결과 상태
1 KWE01 → 구성품 표시 buildBendingProducts 결과와 동일 WO#141(KQTS01) 5개 구성품 정상
2 KSS01 → 다른 구성품 KSS01 전용 구성품 WO#66(KSS01/S1) 벽면형 4pt 정상, WO#70(KSS01/S1) 혼합형 벽면4pt+측면6pt
3 KSS02 → 다른 구성품 KSS02 전용 구성품 WO#74(KSS02/S2) 벽면 3pt, WO#129(KSS02/S2) 측면 5pt
4 마감유형 S1/S2/S3 유형별 차이 반영 S1(4/6pt) S2(3/5pt) S3(5/7pt+하단2pt) 모두 검증 완료
5 구성품 수 7개 미만/초과 정상 렌더링 5개 구성품 정상 렌더링 확인
6 API 미응답 시 fallback buildBendingProducts 기본값 tinker 테스트 확인 (코드 리뷰)
7 BOM 미등록 시 DEFAULT_GAP_PROFILES 사용 tinker 테스트 확인 (BENDING_GAP_PROFILES 반환)
8 저장→조회→재저장 사이클 데이터 무손실 UI 확인: 측정값 표시 정상 (30,78,25,45 등)
9 기존 절곡 데이터 (Path A) 정상 표시 Path A 미수정 확인 (코드 리뷰)
10 신규 절곡 데이터 (Path B) EAV 정상 동작 UI 검증: WO#141, WO#74 성적서 모달 정상 렌더링
11 mng show.blade.php 렌더링 성적서 정상 표시 Phase 3 범위 외 (mng Blade는 별도 렌더링)
12 inspection-config API 응답 < 200ms tinker 기준 ~50ms
13 스크린/슬랫 회귀 변화 없음 tinker: 스크린 WO → process_type='screen', items=[]
14 트랜잭션 동시 접근 (I2) race condition 없음 DB::transaction + lockForUpdate 적용 확인 (코드 리뷰)

6. 참고 파일

6.1 React

파일 역할 비고
react/.../documents/TemplateInspectionContent.tsx 통합 방향 (C3) L176-274
react/.../documents/BendingInspectionContent.tsx 레거시 동결 (C3) L71-135
react/.../documents/InspectionReportModal.tsx 모달 래퍼 documentRecords 전달 완료
react/.../documents/inspection-shared.tsx 공유 유틸
react/.../WorkerScreen/InspectionInputModal.tsx Path A 유지 ~950행

6.2 API

파일 역할
api/app/Services/WorkOrderService.php createInspectionDocument (I2 보강)
api/app/Services/DocumentService.php 문서 CRUD
api/app/Http/Controllers/V1/DocumentController.php 문서 API

6.3 5130 레거시

파일 역할
5130/output/view_inspection_bending.php 절곡 중간검사
5130/estimate/common/common_addrowJS.php 구성품 정의

7. 변경 이력

날짜 항목 변경 내용
2026-02-27 문서 작성 통합 계획 Phase 3 상세 문서 작성
2026-02-27 3.5 완료 createInspectionDocument DB::transaction + lockForUpdate 적용
2026-02-27 3.1 완료 inspection-config API 구현 (Service + Controller + Route)
2026-02-27 3.2 완료 TemplateInspectionContent API 연동 (inspectionConfig state + fallback)
2026-02-27 3.3+3.4 완료 EAV productIdx 순서 호환 확인, Path A/B 독립 동작 확인
2026-02-27 검증 완료 UI 직접 검증 (WO#141 KQTS01, WO#74 KSS02) — 12/14 PASS, 2 조건부

이 문서는 integrated-master-plan.md의 Phase 3 상세입니다.