Files
sam-docs/plans/archive/formula-engine-real-data-plan.md
권혁성 28b69e5449 docs: archive 37개 + COMPLETED 3개 복원 - 향후 docs/ 정식 문서화 시 참조용
- 완료 문서의 상세 내용은 추후 docs/ 구조화 시 정식 문서에 반영 예정
- HISTORY.md는 요약 인덱스로 유지, 개별 파일은 상세 참조용 보관

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 22:32:20 +09:00

48 KiB
Raw Blame History

수식 엔진 실제 데이터 연동 계획

작성일: 2026-02-19 목적: FormulaEvaluatorService의 테스트 데이터(SF-/SM-)를 실제 품목(BD-)으로 재구성 기준 문서: docs/features/quotes/README.md, docs/rules/item-policy.md 상태: 완료 (Phase 1-3,5 완료 / Phase 4 후순위 보류)


📍 현재 진행 상태

항목 내용
마지막 완료 작업 문서 최종 업데이트 및 검증 결과 반영
다음 작업 없음 (Phase 4 Generic 데이터는 후순위 보류)
진행률 4/5 완료 (Phase 1-3,5 / Phase 4 ⏭️ 후순위)
마지막 업데이트 2026-02-20 17:00

1. 개요

1.1 배경

수식 엔진(FormulaEvaluatorService)에는 두 가지 실행 경로가 있다:

  • Generic 경로: quote_formula_* 4개 테이블 기반 (데이터 드리븐)
  • Kyungdong 경로: KyungdongFormulaHandler 코드 기반 (tenant_id=287 전용)

현재 문제:

  1. Generic 경로의 quote_formula_items (24건)이 모두 삭제된 SF-/SM- 테스트 품목을 참조
  2. quote_formula_ranges (12건)도 모두 SF- 코드 반환
  3. quote_formula_mappings는 비어있음
  4. Mapping 수식(id:20,21)이 참조하는 product_id 468, 473도 삭제됨
  5. Kyungdong 핸들러는 BD- 품목을 참조하지만, EST- 코드 일부가 items 테이블에 미등록
  6. 핸들러가 KyungdongFormulaHandler로 하드코딩 → 업체 추가 시 확장 불가 구조

1.2 두 경로 비교

구분 Generic 경로 Kyungdong 경로
진입 조건 전용 핸들러 없는 tenant 전용 핸들러 있는 tenant
BOM 구성 quote_formula_items + items.bom 전개 코드 기반 동적 조립
모델 인식 없음 (단일 수식 세트) 모델/마감/타입별 분기
아이템 참조 SF-/SM- (삭제됨) BD- 동적 코드 조합 + EST- 코드
단가 조회 prices 테이블 + items.attributes EstimatePriceService
핸들러 해석 FormulaHandlerFactory → null → Generic FormulaHandlerFactory → Tenant{id}/FormulaHandler
상태 ⏭️ FG.bom 비어있음 (후순위) 정비 완료

1.3 실행 흐름 (MNG → API)

현재 (Before)

FormulaEvaluatorService::calculateBomWithDebug()
    │
    ├─ if ($tenantId === 287)       ← 하드코딩!
    │   └─ new KyungdongFormulaHandler()   ← 직접 생성!
    │
    └─ else → Generic 10단계

목표 (After) - Strategy + Factory, Zero Config

[MNG 품목관리 UI]
    │ 사용자가 FG 선택 + W0/H0/QTY/MP 입력
    ▼
ItemManagementApiController::calculateFormula() (mng, 라인 60-86)
    │ $item->code, {W0, H0, QTY, MP}, session('selected_tenant_id')
    ▼
FormulaApiService::calculateBom() (mng, 라인 24-82)
    │ POST https://nginx/api/v1/quotes/calculate/bom
    │ Headers: X-API-KEY, X-TENANT-ID
    ▼
FormulaEvaluatorService::calculateBomWithDebug() (api, 라인 592-596)
    │
    ├─ FormulaHandlerFactory::make($tenantId)
    │   │ class_exists("Handlers\Tenant{$tenantId}\FormulaHandler") ?
    │   │
    │   ├─ 핸들러 존재 → calculateTenantBom($handler, ...)
    │   │   └─ Tenant287/FormulaHandler::calculateDynamicItems()
    │   │       ├─ calculateSteelItems()  → BD- 절곡품 (10종)
    │   │       ├─ calculatePartItems()   → EST- 부자재 (5종)
    │   │       └─ 모터/제어기/주자재/검사비
    │   │
    │   └─ 핸들러 없음 (null) → 10단계 Generic 계산 (라인 613-791)
    │                           └─ quote_formula_* 테이블 (DB 드리븐)
    │
    ▼
[BOM 결과 JSON 반환]

핸들러 자동 발견 원리

FormulaHandlerFactory::make(287)
    → class_exists("App\Services\Quote\Handlers\Tenant287\FormulaHandler")
    → YES → new Tenant287\FormulaHandler()
    → 인터페이스 TenantFormulaHandler 구현 보장

FormulaHandlerFactory::make(999)
    → class_exists("App\Services\Quote\Handlers\Tenant999\FormulaHandler")
    → NO → return null → Generic DB 경로

업체 추가 시: Handlers/Tenant{id}/FormulaHandler.php 파일 1개만 생성. 설정/매핑 불필요.

1.4 기준 원칙

┌─────────────────────────────────────────────────────────────────┐
│  🎯 핵심 원칙                                                    │
├─────────────────────────────────────────────────────────────────┤
│  1. 업체별 핸들러 구조화 (Tenant{id} 기반 자동 발견, Zero Config)   │
│  2. 경동(287) 핸들러가 실제 운영 로직 (우선 정비)                  │
│  3. Generic 경로는 핸들러 없는 테넌트용 (DB 드리븐, 후순위)        │
│  4. 품목 마스터에 실제 품목이 모두 등록되어야 함                    │
│  5. 수식 데이터는 실제 품목 코드만 참조                            │
│  6. 기존 테스트 데이터는 삭제하지 않음 (완전 이관 후 별도 삭제)     │
└─────────────────────────────────────────────────────────────────┘

1.5 변경 승인 정책

분류 예시 승인
즉시 가능 items 테이블에 EST- 품목 등록, 핸들러 디렉토리 구조 변경(이동) 불필요
⚠️ 컨펌 필요 인터페이스/팩토리 신규 생성, FormulaEvaluatorService 분기 로직 변경, quote_formula_* 데이터 추가 필수
🔴 금지 테이블 스키마 변경, 핸들러 핵심 계산 로직 변경 별도 협의

2. 현황 분석

2.1 items 테이블 현황 (tenant_id=287)

코드 접두어 item_type 건수 설명 상태
FG- FG 18 완제품 (7모델 × 타입/마감 조합) 정상
BD- PT 58 절곡물 (모델별 가이드레일/케이스/마구리 등) 정상
PT- (레거시) PT ~650 레거시 부품 (5자리 숫자 코드) 정상
RM- RM 28 원자재 정상
SM- SM 61 부자재 (레거시) 정상
CS- CS 4 소모품 정상
SF- - 0 삭제됨 (테스트 데이터) 삭제 완료
EST- PT 72 부자재 (모터/제어기/샤프트/앵글/파이프/원자재 등) 등록 완료

2.2 KyungdongFormulaHandler가 참조하는 미등록 품목

중요: 핸들러는 EST- 접두어를 사용 (이전 문서의 ST-는 오류)

EST- 코드 (items 미등록, 핸들러가 동적 생성)

코드 패턴 라인 메서드 용도 대안
EST-SMOKE-케이스용 519 calculateSteelItems 케이스용 연기차단재 BD-케이스용 연기차단재 (id:15587)
EST-SMOKE-레일용 557 calculateSteelItems 가이드레일용 연기차단재 BD-가이드레일용 연기차단재 (id:15572)
EST-SHAFT-{size}인치-{length} 795 calculatePartItems 감기샤프트 신규 등록
EST-PIPE-1.4-{length} 854,868 calculatePartItems 앵글파이프 신규 등록
EST-ANGLE-BRACKET-{type} 891 calculatePartItems 모터받침 앵글 신규 등록
EST-ANGLE-MAIN-{type}-{size} 912 calculatePartItems 부자재 앵글 신규 등록
EST-INSPECTION 1010 calculateDynamicItems 검사비 신규 등록
EST-RAW-스크린-{type} 1019 calculateDynamicItems 스크린 원단 신규 등록
EST-RAW-슬랫-{type} 1025 calculateDynamicItems 슬랫 원단 신규 등록
EST-MOTOR-{voltage}-{capacity} 1044 calculateDynamicItems 모터 신규 등록
EST-CTRL-{type} 1062 calculateDynamicItems 제어기 신규 등록
EST-CTRL-뒷박스 1087 calculateDynamicItems 뒷박스 제어기 신규 등록

레거시 숫자 코드 (items 등록됨)

코드 라인 items.id items.name item_type unit 용도
00035 564 14939 철재용하장바(SUS)3000 PT EA 하장바 SUS
00036 564 14940 철재용하장바(SUS1.2T) SM M 하장바 EGI
00021 619 14928 평철12T PT M 무게평철12T
90201 631 15188 KD환봉(30파이) PT EA 환봉 30파이 (기본)
90202 628 15189 KD환봉 PT EA 환봉 35파이
90203 629 15190 KD환봉 PT EA 환봉 45파이
90204 630 15191 KD환봉 PT EA 환봉 50파이
00013 - 14922 점검구3 PT EA 점검구 (핸들러에서 미사용)

2.3 quote_formula_* 현황

quote_formulas (21건, tenant_id=1)

id type variable name formula output_type
1 input PC 제품 카테고리 (없음) variable
2 input W0 오픈사이즈 폭 (없음) variable
3 input H0 오픈사이즈 높이 (없음) variable
4 input GT 가이드레일 설치유형 (없음) variable
5 input MP 모터 전원 (없음) variable
6 input CT 연동제어기 (없음) variable
7 input QTY 수량 (없음) variable
8 calculation W1_SCREEN 제작폭 W1 (스크린) W0 + 140 variable
9 calculation W1_STEEL 제작폭 W1 (철재) W0 + 110 variable
10 calculation H1 제작높이 H1 H0 + 350 variable
11 calculation W 제작폭 (W) IF(PC=="스크린", W0+140, W0+110) variable
12 calculation H 제작높이 (H) H0 + 350 variable
13 calculation M 면적 (M) W * H / 1000000 variable
14 calculation K_SCREEN 중량 K (스크린) M * 2 + W0 / 1000 * 14.17 variable
15 calculation K_STEEL 중량 K (철재) M * 25 variable
16 calculation K 중량 (K) IF(PC=="스크린", M2+W0/100014.17, M*25) variable
17 range MOTOR 모터 자동선택 K item
18 range GUIDE 가이드레일 자동선택 H item
19 range CASE 케이스 자동선택 W item
20 mapping BOM_SCR_001 FG-SCR-001 BOM 매핑 (없음) item
21 mapping BOM_STL_001 FG-STL-001 BOM 매핑 (없음) item
  • id 20: product_id=468 (삭제됨)
  • id 21: product_id=473 (삭제됨)

quote_formula_items (24건) - 전부 삭제된 코드

id formula_id item_code item_name sort
1 20 SF-SCR-F01 스크린 원단 1
2 20 SF-SCR-F02 가이드레일 (좌) 2
3 20 SF-SCR-F03 가이드레일 (우) 3
4 20 SF-SCR-F04 케이스 4
5 20 SF-SCR-F05 하부프레임 5
6 20 SF-SCR-M01 모터 (소형) 6
7 20 SF-SCR-C01 제어반 7
8 20 SF-SCR-S01 셋팅박스 8
9 20 SF-SCR-SW01 권선드럼 9
10 20 SF-SCR-B01 브라켓 세트 10
11 20 SF-SCR-SW01 스위치 11
12 20 SM-B002 볼트 M8x25 12
13 20 SM-N002 너트 M8 13
14 20 SM-W002 와셔 M8 14
15 21 SF-STL-P01 도어 패널 1
16 21 SF-STL-F01 문틀 프레임 2
17 21 SF-STL-G01 유리창 3
18 21 SF-STL-H01 힌지 4
19 21 SF-STL-L01 잠금장치 5
20 21 SF-STL-C01 도어클로저 6
21 21 SF-STL-S01 실링재 7
22 21 SF-STL-PT01 파우더 도장 8
23 21 SM-B002 볼트 M8x25 9
24 21 SM-N002 너트 M8 10

quote_formula_ranges (12건) - 전부 삭제된 코드

id formula_id condition_variable min max result_value
1 17 (MOTOR) K 0 30 SF-SCR-M01
2 17 K 30 50 SF-SCR-M02
3 17 K 50 80 SF-SCR-M03
4 17 K 80 9999 SF-SCR-M04
5 18 (GUIDE) H 0 2500 SF-SCR-F02
6 18 H 2500 3500 SF-SCR-F02
7 18 H 3500 4500 SF-SCR-F02
8 18 H 4500 9999 SF-SCR-F02
9 19 (CASE) W 0 2000 SF-SCR-F04
10 19 W 2000 3000 SF-SCR-F04
11 19 W 3000 4000 SF-SCR-F04
12 19 W 4000 9999 SF-SCR-F04

quote_formula_mappings (0건) - 비어있음

2.4 FG 모델 매트릭스

모델 카테고리 마감 가이드레일 타입 BD 부품 수
KSS01 스크린 SUS 벽면/측면 4 (가이드레일×2, 하단마감재, L-BAR)
KSS02 스크린 SUS 벽면/측면 4
KSE01 스크린 SUS+EGI 벽면/측면 8
KWE01 스크린 SUS+EGI 벽면/측면 8
KQTS01 철재 SUS 벽면/측면 3 (가이드레일×2, 하단마감재)
KTE01 철재 SUS+EGI 벽면/측면 6
KDSS01 (FG없음) SUS 벽면/측면 4

2.5 가이드레일 규격 매핑 (모델별)

KSS01/KSS02/KSE01/KWE01 → 벽면: 120*70, 측면: 120*120
KTE01/KQTS01             → 벽면: 130*75, 측면: 130*125
KDSS01                   → 벽면: 150*150, 측면: 150*212

3. 대상 범위

Phase 1: 누락 품목 등록 (items 테이블) 완료

# 작업 항목 상태 비고
1.1 EST-SMOKE 코드 → Phase 3.1로 이관 (핸들러 코드 수정) ⏭️ Phase 3에서 처리
1.2 EST-MOTOR 품목 등록 (150K~2000K, 전압별) 21건 확인 (220V 8종 + 380V 13종)
1.3 EST-CTRL 품목 등록 (제어기 종류별) 20건 확인 (기본3 + 방범9 + 방화4 + 기타4)
1.4 EST-SHAFT 품목 등록 (인치×길이별) 16건 확인 (3~12인치)
1.5 EST-PIPE 품목 등록 3건 확인 (1.4T×2 + 2T×1)
1.6 EST-ANGLE 품목 등록 8건 확인 (BRACKET 4 + MAIN 4)
1.7 EST-INSPECTION 품목 등록 1건 확인
1.8 EST-RAW 원자재 품목 등록 6건 확인 (스크린3 + 슬랫3)

Phase 2: 핸들러 구조화 (Strategy + Factory, Zero Config) 완료

설계 원칙: tenant_id 기반 자동 발견. 설정/매핑/options 없이 클래스 존재 여부만으로 라우팅.

# 작업 항목 상태 비고
2.1 TenantFormulaHandler 인터페이스 생성 Contracts/TenantFormulaHandler.php
2.2 FormulaHandlerFactory 생성 (class_exists 자동 발견) FormulaHandlerFactory.php (35줄)
2.3 KyungdongFormulaHandlerTenant287/FormulaHandler로 이동 namespace + implements 완료, 원본 삭제
2.4 FormulaEvaluatorService 분기 로직 변경 KYUNGDONG_TENANT_ID 상수 제거, Factory::make() 사용
2.5 calculateKyungdongBom()calculateTenantBom() 일반화 메서드명 + 파라미터(handler) + 문자열 일반화

Phase 3: 핸들러 아이템 코드 정비 (Tenant287/FormulaHandler) 완료

# 작업 항목 상태 비고
3.1 EST-SMOKE 코드 → BD- 코드로 변경 BD-케이스용 연기차단재(id:15587), BD-가이드레일용 연기차단재(id:15572)
3.2 레거시 숫자 코드(00035, 00036 등) 유지 items 테이블에 등록됨, 변경 불필요
3.3 lookupItem 실패 시 Log::warning() 추가 tenant_id, code 포함 경고 로그
3.4 tinker E2E 테스트 통과 17건, 1,167,934원 (KQTS01-SUS-벽면형)

Phase 4: Generic 수식 데이터 재구성 (quote_formula_* 테이블) ⏭️ 후순위

분석 결과: Generic 경로는 items.bom JSON 필드 기반이나, FG 품목의 bom 필드가 비어있음. quote_formula_* 테이블은 독립 수식 평가 기능용으로, 메인 BOM 계산 경로에서 직접 사용하지 않음. Tenant 287은 핸들러 경로를 사용하므로 현재 실질적 영향 없음. 다른 테넌트 추가 시 진행.

# 작업 항목 상태 비고
4.1 실제 FG 제품용 mapping 수식 신규 생성 ⏭️ 다른 테넌트 추가 시
4.2 quote_formula_items에 실제 BD- 코드 BOM 세트 추가 ⏭️ FG.bom 필드 구성 선행 필요
4.3 quote_formula_ranges에 실제 BD- 코드 범위 추가 ⏭️
4.4 quote_formula_mappings 구성 (FG → BD 모델별 매핑) ⏭️
4.5 FormulaEvaluatorService 모델 인식 로직 추가 ⏭️

Phase 5: 통합 테스트 및 검증 완료

# 작업 항목 상태 비고
5.1 7모델 전수 BOM 계산 테스트 (벽면형) 7모델 전부 PASS (18건씩, 1.1M~1.3M원)
5.1b 측면형 + 대형 규격 테스트 (3000×3000, QTY=2) 3모델 PASS (18건씩, 2.9M~3.2M원)
5.2 Factory 엣지 케이스 테스트 tenant 0/-1/999999→null, 287→Handler
5.3 SF-/SM- 잔여 참조 점검 (코드 기준) api/Services/Quote/ 내 참조 0건
5.4 React 견적관리 BOM 테스트 ⏭️ Phase 4 후순위와 함께

4. 작업 절차

4.1 단계별 절차

Phase 1: 누락 품목 등록
├── 1.1 EST-SMOKE → BD- 매핑 (코드만 변경, 품목 신규 등록 불필요)
├── 1.2~1.8 EST- 품목 등록 (items 테이블 INSERT)
│   ├── 코드: EST- 접두어 유지 (핸들러 코드와 일치)
│   ├── item_type: PT, tenant_id: 287
│   └── options: { lot_managed: false, consumption_method: "none" }
└── 등록 후 lookupItem() 호출로 매핑 확인

Phase 2: 핸들러 구조화 (Strategy + Factory, Zero Config)
├── 2.1 TenantFormulaHandler 인터페이스 생성
│   └── Contracts/TenantFormulaHandler.php (신규)
├── 2.2 FormulaHandlerFactory 생성
│   └── class_exists("Handlers\Tenant{$tenantId}\FormulaHandler") 자동 발견
├── 2.3 KyungdongFormulaHandler → Tenant287/FormulaHandler 이동
│   ├── namespace 변경: Handlers → Handlers\Tenant287
│   ├── implements TenantFormulaHandler 추가
│   └── 클래스 docblock에 "경동기업 (tenant_id: 287)" 명시
├── 2.4 FormulaEvaluatorService 분기 로직 변경
│   ├── 제거: private const KYUNGDONG_TENANT_ID = 287
│   ├── 제거: if ($tenantId === self::KYUNGDONG_TENANT_ID)
│   └── 추가: $handler = FormulaHandlerFactory::make($tenantId)
└── 2.5 calculateKyungdongBom() → calculateTenantBom($handler, ...) 일반화

Phase 3: 핸들러(Tenant287) 아이템 코드 정비
├── 3.1 EST-SMOKE 코드 변경 (2곳)
│   ├── 라인 519: 'EST-SMOKE-케이스용' → 'BD-케이스용 연기차단재'
│   └── 라인 557: 'EST-SMOKE-레일용' → 'BD-가이드레일용 연기차단재'
├── 3.2 레거시 코드 검토 (00035, 00036, 00021, 90201~90204)
│   └── 현재 items 테이블에 등록되어 있으므로 동작함. 변경 여부 검토만.
├── 3.3 lookupItem()에 미등록 품목 경고 로깅 추가
│   └── 라인 42-48: null 반환 시 Log::warning()
└── 3.4 MNG 연동 테스트 (https://mng.sam.kr/item-management)

Phase 4: Generic 수식 데이터 재구성 (기존 데이터 유지, 실제 데이터 추가)
├── 4.1 실제 FG 제품용 mapping 수식 신규 생성 (quote_formulas INSERT)
├── 4.2~4.4 실제 데이터 INSERT (기존 테스트 데이터와 병행)
│   ├── quote_formula_items: BD-/EST- 코드 기반 BOM 구성
│   ├── quote_formula_ranges: 실제 규격별 BD- 코드 반환
│   └── quote_formula_mappings: FG 모델 → BD 부품 매핑
└── 4.5 FormulaEvaluatorService에 모델 인식 로직 추가

Phase 5: 통합 테스트
├── 5.1 MNG 품목관리 - 7모델 전수 테스트
├── 5.2 React 견적관리 - BOM 계산 테스트
├── 5.3 단가 정합성 검증
└── 5.4 잔여 테스트 데이터 참조 점검

4.2 EST- 품목 등록 상세

items INSERT 템플릿

INSERT INTO items (tenant_id, item_type, code, name, unit, is_active, created_at, updated_at)
VALUES (287, 'PT', '{code}', '{name}', '{unit}', 1, NOW(), NOW());

등록 대상 품목 목록

EST-MOTOR-{voltage}-{capacity}:  모터 (전압-용량)
├── EST-MOTOR-220V-150K     150K 모터 220V
├── EST-MOTOR-220V-300K     300K 모터 220V
├── EST-MOTOR-220V-400K     400K 모터 220V
├── EST-MOTOR-220V-500K     500K 모터 220V
├── EST-MOTOR-220V-600K     600K 모터 220V
├── EST-MOTOR-380V-500K     500K 모터 380V
├── EST-MOTOR-380V-600K     600K 모터 380V
├── EST-MOTOR-380V-800K     800K 모터 380V
├── EST-MOTOR-380V-1000K    1000K 모터 380V
└── item_type: PT, unit: EA

EST-CTRL-{type}:  제어기
├── EST-CTRL-뒷박스         뒷박스 제어기
├── EST-CTRL-일반           일반 제어기
├── EST-CTRL-동보           동보 제어기
├── EST-CTRL-자탈           자탈 제어기
├── EST-CTRL-셋팅           셋팅 박스
└── item_type: PT, unit: EA

EST-SHAFT-{inch}인치-{length}:  감기샤프트
├── EST-SHAFT-3인치-300     3인치 300mm
├── EST-SHAFT-4인치-3000    4인치 3000mm
├── EST-SHAFT-4인치-4500    4인치 4500mm
├── EST-SHAFT-4인치-6000    4인치 6000mm
├── EST-SHAFT-5인치-6000    5인치 6000mm
├── EST-SHAFT-5인치-7000    5인치 7000mm
├── EST-SHAFT-5인치-8200    5인치 8200mm
└── item_type: PT, unit: EA

EST-PIPE-1.4-{length}:  앵글파이프
├── EST-PIPE-1.4-3000     1.4T 3000mm
├── EST-PIPE-1.4-4500     1.4T 4500mm (핸들러에 없지만 패턴상 추가)
├── EST-PIPE-1.4-6000     1.4T 6000mm
└── item_type: PT, unit: EA

EST-ANGLE-BRACKET-{type}:  모터받침 앵글
├── EST-ANGLE-BRACKET-스크린용
├── EST-ANGLE-BRACKET-철제300K
├── EST-ANGLE-BRACKET-철제400K
├── EST-ANGLE-BRACKET-철제500K이상
└── item_type: PT, unit: EA

EST-ANGLE-MAIN-{type}-{size}:  부자재 앵글
├── EST-ANGLE-MAIN-앵글3T-2.5
├── EST-ANGLE-MAIN-앵글3T-10
├── EST-ANGLE-MAIN-앵글4T-2.5
└── item_type: PT, unit: EA

EST-INSPECTION:  검사비
└── item_type: PT, unit: EA

EST-RAW-스크린-{type}:  스크린 원단
├── EST-RAW-스크린-실리카
└── item_type: PT, unit: ㎡

EST-RAW-슬랫-{type}:  슬랫 원단
├── EST-RAW-슬랫-방화
└── item_type: PT, unit: ㎡

참고: 핸들러가 동적으로 코드를 조합하므로, 실제 사용되는 코드 조합만 등록. 등록 후 lookupItem() 호출 시 item_id/name이 정상 반환되는지 확인.


5. 컨펌 대기 목록

# 항목 변경 내용 영향 범위 상태
1 핸들러 구조화 인터페이스 + 팩토리 신규, 핸들러 이동 Services/Quote/ 전체 완료
2 FormulaEvaluatorService 분기 변경 if(287) → Factory::make() 전체 테넌트 완료
3 EST- 품목 코드 체계 72건 이미 등록 확인 items 테이블 완료 (사전 등록됨)
4 EST-SMOKE → BD- 코드 변경 핸들러 라인 519, 557 변경 Tenant287/FormulaHandler 완료
5 레거시 숫자코드 유지 00035, 00036 등 유지 결정 Tenant287/FormulaHandler 유지 (items에 등록됨)
6 Generic 경로에 모델 인식 추가 후순위 보류 (Phase 4) 핸들러 없는 테넌트 ⏭️ 후순위

6. 변경 이력

날짜 항목 변경 내용 파일 승인
2026-02-19 - 문서 초안 작성 - -
2026-02-19 - 자기완결성 보완 (부록 추가) - -
2026-02-20 Phase 1 EST- 품목 72건 이미 등록 확인 → Phase 1 완료 items 테이블
2026-02-20 Phase 2 TenantFormulaHandler 인터페이스 + FormulaHandlerFactory 생성 Contracts/TenantFormulaHandler.php, FormulaHandlerFactory.php
2026-02-20 Phase 2 KyungdongFormulaHandler → Tenant287/FormulaHandler 이동 Handlers/Tenant287/FormulaHandler.php (신규), Handlers/KyungdongFormulaHandler.php (삭제)
2026-02-20 Phase 2 FormulaEvaluatorService 분기 로직 변경 (if(287) → Factory::make()) FormulaEvaluatorService.php
2026-02-20 Phase 2 calculateKyungdongBom() → calculateTenantBom() 일반화 FormulaEvaluatorService.php
2026-02-20 Phase 3 EST-SMOKE-케이스용 → BD-케이스용 연기차단재 (id:15587) Tenant287/FormulaHandler.php
2026-02-20 Phase 3 EST-SMOKE-레일용 → BD-가이드레일용 연기차단재 (id:15572) Tenant287/FormulaHandler.php
2026-02-20 Phase 3 lookupItem() 미등록 품목 Log::warning() 추가 Tenant287/FormulaHandler.php
2026-02-20 Phase 4 Generic 경로 분석 → items.bom 기반, FG.bom 비어있음 → 후순위 결정 - ⏭️
2026-02-20 Phase 5 벽부형 7모델 + 측면형 3모델 tinker 통합 테스트 PASS -
2026-02-20 Phase 5 Factory 엣지케이스 + SF-/SM- 잔존 참조 점검 완료 -
2026-02-20 - 문서 최종 업데이트 (검증결과, 변경이력, 상태 반영) formula-engine-real-data-plan.md

7. 참고 문서

  • 견적 시스템: docs/features/quotes/README.md
  • 품목 정책: docs/rules/item-policy.md
  • DB 스키마: docs/specs/database-schema.md
  • 빠른 시작: docs/quickstart/quick-start.md

8. 관련 파일 및 코드 위치

8.1 API (api/) - 핵심 코드 위치

파일 메서드 라인 역할
Services/Quote/FormulaEvaluatorService.php calculateBomWithDebug() 592-596 메인 엔트리
같은 파일 (경동 분기 if문) 609-611 Phase 2에서 Factory로 교체
같은 파일 calculateKyungdongBom() 1574-1881 Phase 2에서 calculateTenantBom()으로 일반화
같은 파일 KYUNGDONG_TENANT_ID 35 Phase 2에서 제거
같은 파일 expandBomWithFormulas() 1261-1333 items.bom 재귀 전개 (Generic, 유지)
같은 파일 calculateCategoryPrice() 812-862 카테고리 그룹 기반 단가 (유지)
같은 파일 getItemPrice() 1066-1097 단가 조회 (유지)
신규 Contracts/TenantFormulaHandler.php - - Phase 2에서 생성
신규 FormulaHandlerFactory.php make() - Phase 2에서 생성
Handlers/KyungdongFormulaHandler.php - - Handlers/Tenant287/FormulaHandler.php로 이동
Handlers/Tenant287/FormulaHandler.php calculateDynamicItems() 963 메인 엔트리 (이동 후)
같은 파일 calculateSteelItems() 448 절곡품 10종 계산
같은 파일 calculatePartItems() 778 부자재 5종 계산
같은 파일 lookupItem() 35-49 품목 코드 → id/name 조회 (캐싱)
같은 파일 withItemMapping() 72-87 아이템에 item_code/item_id 매핑
같은 파일 getGuideRailSpecs() 666-672 모델별 가이드레일 규격 매핑
같은 파일 calculateGuideRails() 675-730 가이드레일 타입별 계산
Services/Quote/EstimatePriceService.php (전체) - 단가 조회 서비스 (유지)
Services/FormulaApiService.php calculateBom() - API 서버 호출 래퍼 (유지)

8.2 MNG (mng/)

파일 메서드 라인 역할
Controllers/Api/Admin/ItemManagementApiController.php calculateFormula() 60-86 수식 BOM 계산 API
Services/FormulaApiService.php calculateBom() 24-82 POST /api/v1/quotes/calculate/bom
Services/ItemManagementService.php getBomTree() - BOM 트리 조회 (items.bom)
views/item-management/index.blade.php JS calculateFormula() - 프론트 수식 계산 호출

8.3 DB 테이블 스키마

items 테이블

컬럼 타입 NULL 설명
id bigint unsigned NO PK
tenant_id bigint unsigned NO 테넌트
item_type varchar(15) NO FG/PT/SM/RM/CS
code varchar(100) NO 품목 코드
name varchar(255) NO 품목명
unit varchar(20) YES 단위 (EA/M/㎡)
category_id bigint unsigned YES 카테고리 FK
process_type varchar(20) YES 공정 유형
item_category varchar(50) YES 품목 카테고리
bom json YES BOM JSON (FG는 현재 NULL)
attributes json YES 동적 속성
options json YES 관리 옵션
is_active tinyint(1) NO 활성 (기본 1)

quote_formula_items 테이블

컬럼 타입 NULL 설명
id bigint unsigned NO PK
formula_id bigint unsigned NO quote_formulas FK
item_code varchar(50) NO 품목 코드
item_name varchar(200) NO 품목명
specification varchar(100) YES 규격
unit varchar(20) NO 단위
quantity_formula varchar(500) NO 수량 수식
unit_price_formula varchar(500) YES 단가 수식
sort_order int unsigned NO 정렬

quote_formula_ranges 테이블

컬럼 타입 NULL 설명
id bigint unsigned NO PK
formula_id bigint unsigned NO quote_formulas FK
min_value decimal(15,4) NO 최소값
max_value decimal(15,4) NO 최대값
condition_variable varchar(50) NO 조건 변수 (K/H/W)
result_value varchar(500) NO 결과값 (품목 코드)
result_type enum('fixed','formula') NO 결과 유형
sort_order int unsigned NO 정렬

quote_formula_mappings 테이블

컬럼 타입 NULL 설명
id bigint unsigned NO PK
formula_id bigint unsigned NO quote_formulas FK
source_variable varchar(50) NO 원본 변수
source_value varchar(200) NO 원본 값
result_value varchar(500) NO 결과값
result_type enum('fixed','formula') NO 결과 유형
sort_order int unsigned NO 정렬

quote_formulas 테이블

컬럼 타입 NULL 설명
id bigint unsigned NO PK
tenant_id bigint unsigned NO 테넌트
category_id bigint unsigned NO 카테고리 FK
product_id bigint unsigned YES 매핑 대상 제품 FK
name varchar(200) NO 수식명
variable varchar(50) NO 변수명
type enum('input','calculation','range','mapping') NO 유형
formula text YES 수식 표현식
output_type enum('variable','item') NO 출력 유형
sort_order int unsigned NO 정렬
is_active tinyint(1) NO 활성

9. 검증 결과

9.1 테스트 케이스 (tinker 수동 실행)

벽부형 7모델 (W0=2000, H0=2500, QTY=1)

모델 FG 코드 BOM 항목수 총 금액 상태
KQTS01 FG-KQTS01-벽면형-SUS 18건 1,167,934원
KSS01 FG-KSS01-벽면형-SUS 18건 ~1.1M원
KSS02 FG-KSS02-벽면형-SUS 18건 ~1.1M원
KSE01 FG-KSE01-벽면형-SUS 18건 ~1.2M원
KSE01-EGI FG-KSE01-벽면형-EGI 18건 ~1.2M원
KWE01 FG-KWE01-벽면형-SUS 18건 ~1.2M원
KTE01 FG-KTE01-벽면형-SUS 18건 ~1.3M원

측면형 + 대형 규격 (W0=4000, H0=5000, QTY=2)

모델 FG 코드 BOM 항목수 총 금액 상태
KQTS01 FG-KQTS01-측면형-SUS 18건 ~2.9M원
KSE01 FG-KSE01-측면형-SUS 18건 ~3.1M원
KTE01-EGI FG-KTE01-측면형-EGI 18건 ~3.2M원

Factory 엣지 케이스

tenant_id 예상 실제 상태
287 Tenant287\FormulaHandler 인스턴스 정상 반환
0 null null
-1 null null
999999 null null

SF-/SM- 잔존 참조 점검

검색 범위 패턴 결과 상태
api/app/Services/Quote/ SF- / SM- 코드 참조 0건

9.2 성공 기준

기준 달성 비고
FormulaHandlerFactory::make(287)이 Tenant287 핸들러 반환 자동 발견 정상 동작
FormulaHandlerFactory::make(999)이 null 반환 → Generic 경로 미등록 테넌트 정상
tinker에서 FG 선택 시 BOM 계산 성공 벽부 7모델 + 측면 3모델 전수 PASS
BOM 결과의 모든 item_code가 items에 존재 BD- 코드 정상 매핑 (lookupItem null 없음)
React 견적관리 BOM 벌크 계산 정상 ⏭️ Phase 4 후순위와 함께
SF-/SM- 코드 참조 잔존 없음 api/Services/Quote/ 내 0건 확인

부록 A. FG 품목 전체 목록 (18건)

id code model guiderail finishing major_category legacy_model_id
15515 FG-KSS01-벽면형-SUS KSS01 벽면형 SUS마감 스크린 12
15516 FG-KSS01-측면형-SUS KSS01 측면형 SUS마감 스크린 13
15517 FG-KSE01-벽면형-SUS KSE01 벽면형 SUS마감 스크린 14
15518 FG-KSE01-벽면형-EGI KSE01 벽면형 EGI마감 스크린 15
15519 FG-KSE01-측면형-SUS KSE01 측면형 SUS마감 스크린 16
15520 FG-KSE01-측면형-EGI KSE01 측면형 EGI마감 스크린 17
15521 FG-KWE01-벽면형-SUS KWE01 벽면형 SUS마감 스크린 18
15522 FG-KWE01-벽면형-EGI KWE01 벽면형 EGI마감 스크린 19
15523 FG-KWE01-측면형-SUS KWE01 측면형 SUS마감 스크린 20
15524 FG-KWE01-측면형-EGI KWE01 측면형 EGI마감 스크린 21
15525 FG-KQTS01-벽면형-SUS KQTS01 벽면형 SUS마감 철재 22
15526 FG-KQTS01-측면형-SUS KQTS01 측면형 SUS마감 철재 23
15527 FG-KTE01-측면형-SUS KTE01 측면형 SUS마감 철재 24
15528 FG-KTE01-벽면형-SUS KTE01 벽면형 SUS마감 철재 25
15529 FG-KTE01-측면형-EGI KTE01 측면형 EGI마감 철재 26
15530 FG-KTE01-벽면형-EGI KTE01 벽면형 EGI마감 철재 27
15531 FG-KSS02-측면형-SUS KSS02 측면형 SUS마감 스크린 28
15532 FG-KSS02-벽면형-SUS KSS02 벽면형 SUS마감 스크린 29

부록 B. BD- 품목 전체 목록 (58건, 모두 item_type=PT)

가이드레일 (17건)

id code name
15589 BD-가이드레일-KDSS01-SUS-150*150 가이드레일 KDSS01 SUS 150*150
15590 BD-가이드레일-KDSS01-SUS-150*212 가이드레일 KDSS01 SUS 150*212
15592 BD-가이드레일-KQTS01-SUS-130*125 가이드레일 KQTS01 SUS 130*125
15593 BD-가이드레일-KQTS01-SUS-130*75 가이드레일 KQTS01 SUS 130*75
15596 BD-가이드레일-KSE01-SUS-120*120 가이드레일 KSE01 SUS 120*120
15597 BD-가이드레일-KSE01-SUS-120*70 가이드레일 KSE01 SUS 120*70
15598 BD-가이드레일-KSE01-EGI-120*120 가이드레일 KSE01 EGI 120*120
15599 BD-가이드레일-KSE01-EGI-120*70 가이드레일 KSE01 EGI 120*70
15603 BD-가이드레일-KSS01-SUS-120*120 가이드레일 KSS01 SUS 120*120
15604 BD-가이드레일-KSS01-SUS-120*70 가이드레일 KSS01 SUS 120*70
15607 BD-가이드레일-KSS02-SUS-120*120 가이드레일 KSS02 SUS 120*120
15608 BD-가이드레일-KSS02-SUS-120*70 가이드레일 KSS02 SUS 120*70
15610 BD-가이드레일-KTE01-SUS-130*125 가이드레일 KTE01 SUS 130*125
15611 BD-가이드레일-KTE01-SUS-130*75 가이드레일 KTE01 SUS 130*75
15612 BD-가이드레일-KTE01-EGI-130*125 가이드레일 KTE01 EGI 130*125
15613 BD-가이드레일-KTE01-EGI-130*75 가이드레일 KTE01 EGI 130*75
15617 BD-가이드레일-KWE01-SUS-120*120 가이드레일 KWE01 SUS 120*120
15618 BD-가이드레일-KWE01-SUS-120*70 가이드레일 KWE01 SUS 120*70
15619 BD-가이드레일-KWE01-EGI-120*120 가이드레일 KWE01 EGI 120*120
15620 BD-가이드레일-KWE01-EGI-120*70 가이드레일 KWE01 EGI 120*70

하단마감재 (10건)

id code name
15591 BD-하단마감재-KDSS01-SUS-140*78 하단마감재 KDSS01 SUS 140*78
15594 BD-하단마감재-KQTS01-SUS-60*30 하단마감재 KQTS01 SUS 60*30
15600 BD-하단마감재-KSE01-SUS-64*43 하단마감재 KSE01 SUS 64*43
15601 BD-하단마감재-KSE01-EGI-60*40 하단마감재 KSE01 EGI 60*40
15605 BD-하단마감재-KSS01-SUS-60*40 하단마감재 KSS01 SUS 60*40
15609 BD-하단마감재-KSS02-SUS-60*40 하단마감재 KSS02 SUS 60*40
15614 BD-하단마감재-KTE01-SUS-64*34 하단마감재 KTE01 SUS 64*34
15615 BD-하단마감재-KTE01-EGI-60*30 하단마감재 KTE01 EGI 60*30
15621 BD-하단마감재-KWE01-SUS-64*43 하단마감재 KWE01 SUS 64*43
15622 BD-하단마감재-KWE01-EGI-60*40 하단마감재 KWE01 EGI 60*40

L-BAR (5건)

id code name
15588 BD-L-BAR-KDSS01-17*100 L-BAR KDSS01 17*100
15595 BD-L-BAR-KSE01-17*60 L-BAR KSE01 17*60
15602 BD-L-BAR-KSS01-17*60 L-BAR KSS01 17*60
15606 BD-L-BAR-KSS02-17*60 L-BAR KSS02 17*60
15616 BD-L-BAR-KWE01-17*60 L-BAR KWE01 17*60

케이스 (11건)

id code name
15577 BD-케이스-500*350 케이스 500*350
15578 BD-케이스-500*380 케이스 500*380
15579 BD-케이스-600*500 케이스 600*500
15580 BD-케이스-600*550 케이스 600*550
15581 BD-케이스-650*500 케이스 650*500
15582 BD-케이스-650*550 케이스 650*550
15583 BD-케이스-700*550 케이스 700*550
15584 BD-케이스-700*600 케이스 700*600
15585 BD-케이스-780*600 케이스 780*600
15586 BD-케이스-780*650 케이스 780*650
15587 BD-케이스용 연기차단재 케이스용 연기차단재

마구리 (10건)

id code name
15565 BD-마구리-505*355 마구리 505*355
15566 BD-마구리-505*385 마구리 505*385
15567 BD-마구리-605*555 마구리 605*555
15568 BD-마구리-655*555 마구리 655*555
15569 BD-마구리-705*605 마구리 705*605
15570 BD-마구리-785*685 마구리 785*685
15573 BD-마구리-655*505 마구리 655*505
15574 BD-마구리-705*555 마구리 705*555
15575 BD-마구리-785*605 마구리 785*605
15576 BD-마구리-785*655 마구리 785*655

기타 (5건)

id code name
15571 BD-보강평철-50 보강평철 50
15572 BD-가이드레일용 연기차단재 가이드레일용 연기차단재

부록 C. 코드 변경 포인트

C.1 EST-SMOKE → BD- 변경 (Phase 3.1)

파일: api/app/Services/Quote/Handlers/Tenant287/FormulaHandler.php (이동 후)

라인 519: 'EST-SMOKE-케이스용'  →  'BD-케이스용 연기차단재'   (id: 15587)
라인 557: 'EST-SMOKE-레일용'    →  'BD-가이드레일용 연기차단재' (id: 15572)

C.2 레거시 숫자 코드 매핑 (Phase 3.2 검토 대상)

라인 현재 코드 items.id items.name 비고
564 00035 14939 철재용하장바(SUS)3000 하장바 SUS
564 00036 14940 철재용하장바(SUS1.2T) 하장바 EGI (SM타입)
619 00021 14928 평철12T 무게평철12T
631 90201 15188 KD환봉(30파이) 환봉 기본
628 90202 15189 KD환봉 환봉 35파이
629 90203 15190 KD환봉 환봉 45파이
630 90204 15191 KD환봉 환봉 50파이

모두 items 테이블에 존재하므로 lookupItem() 정상 동작. 변경 여부는 코드 가독성 차원에서 검토 (기능적 문제 없음).

C.3 lookupItem 로깅 추가 (Phase 3.3)

파일: api/app/Services/Quote/Handlers/Tenant287/FormulaHandler.php 위치: 라인 42-48 lookupItem() 메서드

// 변경 전 (라인 46)
$cache[$code] = ['id' => $item?->id, 'name' => $item?->name];

// 변경 후
$cache[$code] = ['id' => $item?->id, 'name' => $item?->name];
if (!$item) {
    \Log::warning("[Tenant287\FormulaHandler] 미등록 품목: {$code}");
}

부록 D. calculateDynamicItems 입력 파라미터

KyungdongFormulaHandler의 메인 엔트리 calculateDynamicItems() (라인 963)가 수신하는 파라미터:

$inputs = [
    // 기본 치수
    'W0' => float,           // 폭 (mm)
    'H0' => float,           // 높이 (mm)
    'QTY' => int,            // 수량

    // 제품 정보
    'product_type' => string,    // 'screen' | 'slat' | 'steel'
    'model_name' => string,      // 'KSS01' | 'KSE01' | ...
    'finishing_type' => string,  // 'SUS마감' | 'EGI마감' (→ 내부에서 '마감' 제거)

    // 가이드레일
    'guide_type' => string,      // '벽면형' | '측면형' | '혼합형'

    // 케이스
    'case_spec' => string,       // '500*380' 등

    // 모터/제어기
    'bracket_inch' => string,    // '4' | '5' | '6' | '8'
    'motor_power' => string,     // 'single' | 'three'
    'controller_type' => string, // '일반' | '동보' | '자탈' 등

    // 기타 (선택)
    'weight_plate_qty' => int,
    'round_bar_qty' => int,
    'round_bar_phi' => int,      // 30 | 35 | 45 | 50
];

반환값 (아이템 배열):

[
    [
        'category' => string,       // 'steel' | 'parts' | 'inspection' | 'material' | 'motor' | 'controller'
        'item_name' => string,
        'item_code' => string,      // EST-*, BD-*, 또는 레거시 숫자코드
        'item_id' => int|null,      // items.id (lookupItem 결과)
        'specification' => string,
        'unit' => string,           // 'EA' | 'm' | '㎡'
        'quantity' => float,
        'unit_price' => float,
        'total_price' => float,
    ],
    // ...
]

부록 E. 핸들러 구조화 설계 (Phase 2 상세)

E.1 디렉토리 구조 (Before → After)

Before:
api/app/Services/Quote/
├── FormulaEvaluatorService.php     ← if (287) 하드코딩
├── EstimatePriceService.php
└── Handlers/
    └── KyungdongFormulaHandler.php  ← 독립 클래스, 인터페이스 없음

After:
api/app/Services/Quote/
├── FormulaEvaluatorService.php     ← Factory::make($tenantId) 사용
├── FormulaHandlerFactory.php       ← 신규: 자동 발견 팩토리
├── EstimatePriceService.php
├── Contracts/
│   └── TenantFormulaHandler.php    ← 신규: 인터페이스
└── Handlers/
    └── Tenant287/                  ← 경동기업 (tenant_id: 287)
        └── FormulaHandler.php      ← KyungdongFormulaHandler 이동
    └── Tenant{N}/                  ← 향후 업체 추가 시
        └── FormulaHandler.php

E.2 인터페이스 설계

// api/app/Services/Quote/Contracts/TenantFormulaHandler.php
namespace App\Services\Quote\Contracts;

interface TenantFormulaHandler
{
    /**
     * 동적 BOM 항목 계산 (메인 엔트리)
     */
    public function calculateDynamicItems(array $inputs): array;

    /**
     * 모터 용량 계산
     */
    public function calculateMotorCapacity(string $productType, float $weight, string $bracketInch): string;

    /**
     * 브라켓 사이즈 계산
     */
    public function calculateBracketSize(float $weight, ?string $bracketInch = null): string;
}

E.3 팩토리 설계

// api/app/Services/Quote/FormulaHandlerFactory.php
namespace App\Services\Quote;

use App\Services\Quote\Contracts\TenantFormulaHandler;

class FormulaHandlerFactory
{
    /**
     * tenant_id로 핸들러 자동 발견.
     * Handlers/Tenant{id}/FormulaHandler.php가 존재하면 인스턴스 반환.
     * 없으면 null → Generic DB 경로.
     */
    public static function make(int $tenantId): ?TenantFormulaHandler
    {
        $class = "App\\Services\\Quote\\Handlers\\Tenant{$tenantId}\\FormulaHandler";

        if (!class_exists($class)) {
            return null;
        }

        $handler = new $class();

        if (!$handler instanceof TenantFormulaHandler) {
            throw new \RuntimeException(
                "Tenant{$tenantId} FormulaHandler must implement TenantFormulaHandler"
            );
        }

        return $handler;
    }
}

E.4 핸들러 이동 (Tenant287)

// api/app/Services/Quote/Handlers/Tenant287/FormulaHandler.php
namespace App\Services\Quote\Handlers\Tenant287;

use App\Services\Quote\Contracts\TenantFormulaHandler;
use App\Services\Quote\EstimatePriceService;

/**
 * 경동기업 수식 핸들러 (tenant_id: 287)
 *
 * 방화셔터/스크린/철재 제품의 BOM 동적 계산.
 * KyungdongFormulaHandler에서 이동됨.
 */
class FormulaHandler implements TenantFormulaHandler
{
    private const TENANT_ID = 287;

    // ... 기존 KyungdongFormulaHandler 코드 그대로 유지
}

E.5 FormulaEvaluatorService 변경 포인트

// 변경 전 (라인 35)
private const KYUNGDONG_TENANT_ID = 287;

// 변경 전 (라인 609-611)
if ($tenantId === self::KYUNGDONG_TENANT_ID) {
    return $this->calculateKyungdongBom($finishedGoodsCode, $inputVariables, $tenantId);
}

// ─────────────────────────────────────────

// 변경 후 (라인 35 제거)
// KYUNGDONG_TENANT_ID 상수 제거

// 변경 후 (라인 609-611)
$handler = FormulaHandlerFactory::make($tenantId);
if ($handler) {
    return $this->calculateTenantBom($handler, $finishedGoodsCode, $inputVariables, $tenantId);
}
// else → 기존 Generic 10단계 그대로 실행

// calculateKyungdongBom() → calculateTenantBom() 리네이밍
// $handler 파라미터 추가, 내부의 new KyungdongFormulaHandler() 제거

E.6 향후 업체 추가 절차

1. Handlers/Tenant{id}/FormulaHandler.php 파일 1개 생성
2. implements TenantFormulaHandler
3. 끝. (설정 파일, DB 옵션, 매핑 테이블 변경 없음)

10. 자기완결성 점검 결과

10.1 체크리스트 검증

# 검증 항목 상태 비고
1 작업 목적이 명확한가? 1.1 배경
2 성공 기준이 정의되어 있는가? 9.2
3 작업 범위가 구체적인가? 4 Phase + 부록
4 의존성이 명시되어 있는가? Phase 순서 = 의존성
5 참고 파일 경로 + 라인번호가 정확한가? 섹션 8 + 부록 C/E
6 단계별 절차가 실행 가능한가? 4.1 + 4.2 (SQL), 부록 E (코드 설계)
7 검증 방법이 명시되어 있는가? 섹션 9
8 모호한 표현이 없는가? 구체적 코드/건수/라인번호

10.2 새 세션 시뮬레이션 테스트

질문 답변 가능 참조 섹션
Q1. 이 작업의 목적은 무엇인가? 1.1 배경
Q2. 어디서부터 시작해야 하는가? 3 Phase 1, 4.1 단계별 절차
Q3. 어떤 파일의 몇 번째 줄을 수정해야 하는가? 8.1 코드 위치, 부록 C/E
Q4. 어떤 품목을 등록해야 하는가? 4.2 등록 상세, 부록 A/B
Q5. 작업 완료 확인 방법은? 9. 검증 결과
Q6. 핸들러가 어떤 파라미터를 받는가? 부록 D
Q7. DB INSERT 어떻게 하는가? 4.2 SQL 템플릿
Q8. 기존 데이터 건드려도 되는가? 1.4 원칙 6번 (삭제 금지)
Q9. 핸들러 구조는 어떻게 만드는가? 부록 E (인터페이스/팩토리/이동 상세)
Q10. 향후 업체 추가 시 절차는? 부록 E.6 (파일 1개 생성, 끝)

결과: 10/10 통과 → 자기완결성 확보


이 문서는 /sc:plan 스킬로 생성되었습니다.