Files
sam-docs/plans/fg-code-consolidation-plan.md
권혁성 3fff99095e docs: 절곡/품목 관련 신규 계획 문서 추가
- 절곡 정보 자동 생성 계획 (bending-info-auto-generation)
- 절곡 자재투입 매핑 GAP 분석 (bending-material-input-mapping)
- FG 코드 통합 계획 (fg-code-consolidation)
- 품목 재고 관리 계획 (item-inventory-management)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 10:02:47 +09:00

26 KiB
Raw Blame History

FG 제품코드 통합 계획

작성일: 2026-02-19 목적: FG 제품코드에서 설치유형/마감재질을 분리하여 위치별 설정으로 이동, 18개 FG 품목을 6개로 통합 기준 문서: docs/rules/item-policy.md, docs/features/quotes/README.md 상태: 🔄 진행중


📍 현재 진행 상태

항목 내용
마지막 완료 작업 영향도 분석 완료, 혼합형 validation 수정 커밋 완료
다음 작업 Phase 1: DB 마이그레이션
진행률 0/8 (0%)
마지막 업데이트 2026-02-19

1. 개요

1.1 배경

현재 경동기업(tenant_id=287) FG 품목 코드 체계:

FG-KWE01-벽면형-SUS    (모델: KWE01, 설치유형: 벽면형, 마감재질: SUS)
FG-KWE01-벽면형-EGI    (모델: KWE01, 설치유형: 벽면형, 마감재질: EGI)
FG-KWE01-측면형-SUS    (모델: KWE01, 설치유형: 측면형, 마감재질: SUS)
... (총 18개 = 6모델 × {벽면형,측면형} × {SUS,EGI} + 혼합형 추가 예정)

문제점:

  • 설치유형/마감재질은 위치(Location)별 설정이지 제품 자체의 속성이 아님
  • 같은 모델(KWE01)인데 FG 코드가 4개 이상으로 분산
  • 혼합형 추가 시 FG 품목이 계속 늘어남 (6모델 × 3설치유형 × 2마감재질 = 36개)

1.2 목표 코드 체계

AS-IS: FG-KWE01-벽면형-SUS  →  TO-BE: KWE01
  • "FG-" 접두사 제거: item_type = 'FG' 컬럼이 이미 완제품 구분 담당
  • 설치유형(벽면형/측면형/혼합형) 제거: 위치별 guideRailType 파라미터로 전달
  • 마감재질(SUS/EGI) 제거: 위치별 finishingType 파라미터로 전달

1.3 기준 원칙

┌─────────────────────────────────────────────────────────────────┐
│  🎯 핵심 원칙                                                    │
├─────────────────────────────────────────────────────────────────┤
│  1. 코어 계산 로직(KyungdongFormulaHandler) 변경 없음             │
│  2. BOM은 child_item_id FK 기반 → 코드 변경에 안전               │
│  3. product_model/finishing_type은 이미 별도 파라미터 전달 중      │
│  4. 기존 quote_items에 FG 코드 참조 데이터 없음 (마이그레이션 부담 ↓) │
└─────────────────────────────────────────────────────────────────┘

1.4 변경 승인 정책

분류 예시 승인
즉시 가능 React UI에 마감재질 Select 추가, validation 규칙 수정 불필요
⚠️ 컨펌 필요 items 테이블 데이터 통합, BOM parent_item_id 재매핑, 시더 수정 필수
🔴 금지 items 테이블 스키마 변경, 기존 BOM 삭제, 견적 계산 코어 로직 변경 별도 협의

1.5 준수 규칙

  • docs/rules/item-policy.md - 품목 정책
  • docs/standards/quality-checklist.md - 품질 체크리스트
  • docs/features/quotes/README.md - 견적 시스템

2. 대상 범위

2.1 Phase 1: DB 마이그레이션 (items 통합)

# 작업 항목 상태 비고
1.1 18개 FG 품목 → 6개로 통합 마이그레이션 스크립트 items.code 변경
1.2 BOM parent_item_id 재매핑 통합된 item_id로 변경
1.3 통합 대상 외 12개 FG 품목 soft delete 연결된 BOM 확인 후
1.4 MapItemsToProcesses globalExcludes 수정 'FG-%' → item_type 기반

2.2 Phase 2: API 수정

# 작업 항목 상태 비고
2.1 FormulaEvaluatorService: finishing_type 파라미터 수신 마감재질 매핑 추가
2.2 QuoteBomCalculateRequest: finishingType validation 추가 SUS/EGI
2.3 QuoteBomBulkCalculateRequest: finishingType validation 추가 SUS/EGI
2.4 KyungdongItemSeeder 수정 (향후 시딩용) FG-코드 생성 로직

2.3 Phase 3: React 프론트엔드

# 작업 항목 상태 비고
3.1 LocationDetailPanel: 마감재질 Select UI 추가 SUS/EGI 선택
3.2 LocationListPanel: 마감재질 컬럼/폼필드 추가 위치 추가 시
3.3 types.ts: QuoteLocation에 finishingType 추가
3.4 actions.ts: BOM 산출 요청에 finishingType 포함
3.5 QuoteRegistration.tsx: mock 데이터 업데이트
3.6 QuoteSummaryPanel/PreviewContent: 마감재질 표시

2.4 Phase 4: 검증

# 작업 항목 상태 비고
4.1 통합 전후 BOM 계산 결과 비교 테스트 동일 입력 → 동일 결과
4.2 견적 등록 → 산출 → 저장 E2E 테스트

3. 작업 절차

3.1 단계별 절차

Step 1: DB 마이그레이션 스크립트 작성
├── 6개 모델별 대표 FG 품목 선정 (유지할 item_id 결정)
├── BOM parent_item_id를 대표 item_id로 재매핑
├── 대표 품목의 code를 통합 코드로 변경 (KWE01 등)
├── 대표 품목의 attributes에서 guiderail_type/finishing_type 제거
└── 나머지 12개 FG 품목 soft delete

Step 2: API 수정
├── FormRequest에 finishingType/FT validation 추가
├── FormulaEvaluatorService에 FT → finishing_type 매핑 추가
├── MapItemsToProcesses globalExcludes → item_type 기반 변경
└── KyungdongItemSeeder 코드 생성 로직 수정

Step 3: React 프론트엔드
├── types.ts에 finishingType 필드 추가
├── LocationDetailPanel에 마감재질 Select 추가
├── LocationListPanel에 마감재질 폼필드/컬럼 추가
├── actions.ts BOM 산출 요청에 finishingType 포함
└── Summary/Preview에 마감재질 표시

Step 4: 검증
├── 동일 입력(KWE01 + wall + SUS)으로 기존 결과와 비교
├── 모든 조합 테스트 (6모델 × 3설치 × 2마감)
└── 견적 등록 → 산출 → 저장 E2E

4. 상세 작업 내용 (코드 스니펫 포함)

4.1 현재 FG 품목 현황 (tenant_id=287)

모델 벽면형-SUS 벽면형-EGI 측면형-SUS 측면형-EGI 통합 코드 item_category
KWE01 FG-KWE01-벽면형-SUS FG-KWE01-벽면형-EGI FG-KWE01-측면형-SUS FG-KWE01-측면형-EGI KWE01 SCREEN
KWE02 (동일 패턴) KWE02 SCREEN
KWE03 KWE03 SCREEN
KWS01 KWS01 STEEL
KWS02 KWS02 STEEL
KWS03 KWS03 STEEL

KWE = 스크린(SCREEN), KWS = 철재(STEEL). item_category는 유지됨 (계산 분기에 사용)

FG 코드 생성 원본 (api/database/seeders/Kyungdong/KyungdongItemSeeder.php:305-307):

$finishingShort = self::FINISHING_MAP[$model->finishing_type] ?? 'STD';
$code = "FG-{$model->model_name}-{$model->guiderail_type}-{$finishingShort}";
$name = "{$model->model_name} {$model->major_category} {$model->finishing_type} {$model->guiderail_type}";

FINISHING_MAP (KyungdongItemSeeder.php:39-42):

private const FINISHING_MAP = [
    'SUS마감' => 'SUS',
    'EGI마감' => 'EGI',
];

items.attributes 구조:

{
  "model_name": "KWE01",
  "major_category": "스크린",
  "finishing_type": "SUS마감",
  "guiderail_type": "벽면형",
  "legacy_source": "models",
  "legacy_model_id": 123
}

4.2 BOM 재매핑 전략

BOM은 FG 품목(parent)의 items.bom JSON 컬럼에 저장:

[
  { "child_item_id": 123, "quantity": 1 },
  { "child_item_id": 456, "quantity": 2 }
]

마이그레이션 SQL 전략:

-- Step 1: 모델별 대표 FG 품목 선정 (벽면형-SUS를 대표로)
-- 대표 선정 기준: 같은 model_name 중 가장 작은 id

-- Step 2: 대표 품목의 code 변경
UPDATE items SET code = 'KWE01'
WHERE id = (대표_item_id) AND tenant_id = 287;

-- Step 3: 대표 품목의 attributes에서 guiderail_type/finishing_type 제거
-- (이 속성들은 더 이상 품목 고유 속성이 아님)

-- Step 4: 비대표 품목의 BOM을 대표 품목으로 이관
-- (동일 모델의 BOM은 동일하므로, BOM이 있는 품목의 bom을 대표로 복사)

-- Step 5: 비대표 12개 품목 soft delete
UPDATE items SET deleted_at = NOW(), deleted_by = 1
WHERE tenant_id = 287 AND item_type = 'FG'
  AND id NOT IN (대표_item_ids);

핵심 안전 요소:

  • BOM의 child_item_id는 PT/SM 품목 → FG 통합과 무관
  • FormulaEvaluatorService::getItemDetails() (line 1110-1112)에서 ->where('code', $itemCode) 조회
  • 통합 후 code가 'KWE01'이 되면 getItemDetails('KWE01')로 정상 조회

4.3 API 파라미터 흐름 (통합 후)

Frontend (LocationDetailPanel)
  ├── productCode: "KWE01" (통합 코드)
  ├── guideRailType: "wall" | "floor" | "mixed"
  ├── finishingType: "SUS" | "EGI"          ← 새로 추가
  └── motorPower: "single" | "three"
      ↓
actions.ts::calculateBomBulk() - POST /api/v1/quotes/calculate/bom/bulk
  body: { items: [{ finished_goods_code, openWidth, openHeight, guideRailType, motorPower, finishingType, ... }] }
      ↓
QuoteBomBulkCalculateRequest::normalizeInputVariables() (line 122-135)
  ├── 'W0' => openWidth, 'H0' => openHeight
  ├── 'GT' => guideRailType, 'MP' => motorPower
  └── 'FT' => finishingType                 ← 새로 추가
      ↓
FormulaEvaluatorService::calculateKyungdongBom() (line 1574~)
  ├── getItemDetails("KWE01", tenantId) → items.code = "KWE01" 조회 (line 1110-1112)
  ├── $finishingType: FT → SUS/EGI          ← 기존 line 1677 수정
  ├── $installationType: GT → 벽면형/측면형/혼합형 (line 1680-1684)
  └── $motorVoltage: MP → 220V/380V (line 1687-1690)
      ↓
$calculatedVariables = array_merge() (line 1692-1708)
  'finishing_type' => $finishingType  (line 1705)  ← 이미 포함됨
      ↓
KyungdongFormulaHandler (변경 없음)
  ├── calculateSteelItems() line 458: $rawFinish = $params['finishing_type'] ?? 'SUS'
  ├── calculateGuideRails() line 540: $finishingType 파라미터
  └── getBottomBarPrice() line 561: $finishingType 파라미터

4.4 핵심 파일별 변경 상세


4.4.1 api/app/Services/Quote/FormulaEvaluatorService.php

현재 코드 (line 1676-1677):

$productModel = $inputVariables['product_model'] ?? 'KSS01';
$finishingType = $inputVariables['finishing_type'] ?? 'SUS';

수정 후:

$productModel = $inputVariables['product_model'] ?? 'KSS01';

// 마감재질: 프론트 FT(SUS/EGI) → finishing_type 매핑
$finishingType = $inputVariables['finishing_type'] ?? match ($inputVariables['FT'] ?? 'SUS') {
    'EGI' => 'EGI',
    default => 'SUS',
};

$calculatedVariables array_merge (line 1705)에는 이미 'finishing_type' => $finishingType 포함됨


4.4.2 api/app/Http/Requests/Quote/QuoteBomCalculateRequest.php

현재 rules() (line 20-39)에 추가:

// 기존
'GT' => 'nullable|string|in:wall,ceiling,floor,mixed',
'MP' => 'nullable|string|in:single,three',
// 추가
'FT' => 'nullable|string|in:SUS,EGI',

현재 getInputVariables() (line 74-89)에 추가:

// 기존
'MP' => $validated['MP'] ?? 'single',
// 추가
'FT' => $validated['FT'] ?? 'SUS',

4.4.3 api/app/Http/Requests/Quote/QuoteBomBulkCalculateRequest.php

rules() (line 21-54)에 추가:

// React 필드명 (camelCase)
'items.*.finishingType' => 'nullable|string|in:SUS,EGI',
// API 변수명 (약어)
'items.*.FT' => 'nullable|string|in:SUS,EGI',

normalizeInputVariables() (line 122-135)에 추가:

// 기존
'MP' => $item['motorPower'] ?? $item['MP'] ?? 'single',
// 추가
'FT' => $item['finishingType'] ?? $item['FT'] ?? 'SUS',

4.4.4 api/app/Console/Commands/MapItemsToProcesses.php

현재 (line 48):

private array $globalExcludes = ['FG-%', 'RM-%', 'EST-INSPECTION'];

수정 후:

private array $globalExcludes = ['RM-%', 'EST-INSPECTION'];
// FG 제외는 item_type 기반으로 처리 (아래 쿼리에서 ->where('item_type', '!=', 'FG') 추가)

해당 명령어에서 items 조회 시 ->whereNotIn('item_type', ['FG']) 조건 추가


4.4.5 api/database/seeders/Kyungdong/KyungdongItemSeeder.php

현재 (line 305-307):

$finishingShort = self::FINISHING_MAP[$model->finishing_type] ?? 'STD';
$code = "FG-{$model->model_name}-{$model->guiderail_type}-{$finishingShort}";
$name = "{$model->model_name} {$model->major_category} {$model->finishing_type} {$model->guiderail_type}";

수정 후:

$code = $model->model_name;  // KWE01, KWS01 등
$name = "{$model->model_name} {$model->major_category}";

중복 방지: 같은 model_name은 하나만 생성 (기존: 설치유형×마감재질 조합별 생성 → 모델별 1개)


4.4.6 react/src/components/quotes/types.ts

LocationItem 인터페이스 (line 664-686)에 추가:

export interface LocationItem {
  // ... 기존 필드
  guideRailType: string;   // 가이드레일 설치 유형
  finishingType: string;   // 마감재질 (SUS/EGI)  ← 추가
  motorPower: string;      // 모터 전원
  // ...
}

4.4.7 react/src/components/quotes/actions.ts

BomCalculateItem 인터페이스 (line 343-354)에 추가:

export interface BomCalculateItem {
  finished_goods_code: string;
  openWidth: number;
  openHeight: number;
  quantity?: number;
  guideRailType?: string;
  finishingType?: string;   // ← 추가
  motorPower?: string;
  controller?: string;
  wingSize?: number;
  inspectionFee?: number;
}

4.4.8 react/src/components/quotes/LocationDetailPanel.tsx

상수 추가 (line 75 뒤):

// 마감재질
const FINISHING_TYPES = [
  { value: "SUS", label: "SUS (스테인리스)" },
  { value: "EGI", label: "EGI (아연도금)" },
];

2행 그리드 변경 (line 358-423): 현재 grid-cols-3 (가이드레일, 전원, 제어기) → grid-cols-4로 변경하고 마감재질 Select 추가:

{/* 2행: 가이드레일, 마감재질, 전원, 제어기 */}
<div className="grid grid-cols-4 gap-3">
  {/* 가이드레일 (기존) */}
  <div>...</div>
  {/* 마감재질 (새로 추가) */}
  <div>
    <label className="text-xs text-gray-600 flex items-center gap-1">
      🔩 마감재질
    </label>
    <Select
      value={location.finishingType}
      onValueChange={(value) => handleFieldChange("finishingType", value)}
      disabled={disabled}
    >
      <SelectTrigger className="h-8 text-sm">
        <SelectValue />
      </SelectTrigger>
      <SelectContent>
        {FINISHING_TYPES.map((type) => (
          <SelectItem key={type.value} value={type.value}>
            {type.label}
          </SelectItem>
        ))}
      </SelectContent>
    </Select>
  </div>
  {/* 전원 (기존) */}
  <div>...</div>
  {/* 제어기 (기존) */}
  <div>...</div>
</div>

4.4.9 react/src/components/quotes/LocationListPanel.tsx

formData 초기값 (line 110-120)에 추가:

const [formData, setFormData] = useState({
  // ... 기존
  guideRailType: "wall",
  finishingType: "SUS",    // ← 추가
  motorPower: "single",
  // ...
});

2행 폼 (line ~380 이후)에 마감재질 Select 추가 (가이드레일 Select 패턴과 동일)


4.4.10 react/src/components/quotes/QuoteRegistration.tsx

BOM 계산 페이로드 (line 459-469)에 finishingType 추가:

const bomItem = {
  finished_goods_code: newLocation.productCode,
  openWidth: newLocation.openWidth,
  openHeight: newLocation.openHeight,
  quantity: newLocation.quantity,
  guideRailType: newLocation.guideRailType,
  finishingType: newLocation.finishingType,    // ← 추가
  motorPower: newLocation.motorPower,
  controller: newLocation.controller,
  wingSize: newLocation.wingSize,
  inspectionFee: newLocation.inspectionFee,
};

다건 산출 (line 594-606)도 동일하게 finishingType 추가:

const bomItems = formData.locations.map((loc) => ({
  finished_goods_code: loc.productCode,
  // ...
  finishingType: loc.finishingType,    // ← 추가
  // ...
}));

기본값 (line 117):

// 기존
guideRailType: "wall",
// 추가
finishingType: "SUS",

mock 데이터 (line 248):

// 기존: productCode: randomProduct?.item_code || "FG-SCR-001"
// 수정: productCode: randomProduct?.item_code || "KWE01"

4.4.11 react/src/components/quotes/QuoteSummaryPanel.tsx & QuotePreviewContent.tsx

위치 정보 표시 영역에 마감재질 추가:

// QuoteSummaryPanel.tsx line 172 근처
{loc.productCode} ({loc.finishingType}) × {loc.quantity}

// QuotePreviewContent.tsx line 209 근처
<td>{loc.productCode}</td>
<td>{loc.finishingType}</td>  // 또는 기존 컬럼에 병합

4.4.12 react/src/app/[locale]/(protected)/sales/quote-management/[id]/page.tsx

기존 견적 조회 시 BOM 재계산 페이로드 (line 60-70):

const bomItems: BomCalculateItem[] = locationsNeedingRecalc.map(loc => ({
  finished_goods_code: loc.productCode,
  openWidth: loc.openWidth,
  openHeight: loc.openHeight,
  quantity: loc.quantity,
  guideRailType: loc.guideRailType,
  // finishingType: loc.finishingType,  ← 추가 필요
  motorPower: loc.motorPower,
  controller: loc.controller,
  wingSize: loc.wingSize,
  inspectionFee: loc.inspectionFee,
}));

4.5 DB 마이그레이션 사전 검증 쿼리

마이그레이션 실행 전 반드시 확인할 쿼리:

-- 1. 현재 FG 품목 전체 목록 확인
SELECT id, code, name, item_category,
       JSON_EXTRACT(attributes, '$.model_name') as model_name,
       JSON_EXTRACT(attributes, '$.guiderail_type') as guiderail_type,
       JSON_EXTRACT(attributes, '$.finishing_type') as finishing_type,
       bom IS NOT NULL AND bom != '[]' as has_bom
FROM items
WHERE tenant_id = 287 AND item_type = 'FG' AND deleted_at IS NULL
ORDER BY code;

-- 2. 모델별 BOM 동일성 검증 (같은 model_name의 bom이 동일한지)
SELECT JSON_EXTRACT(attributes, '$.model_name') as model_name,
       COUNT(DISTINCT bom) as distinct_bom_count,
       COUNT(*) as total_count
FROM items
WHERE tenant_id = 287 AND item_type = 'FG' AND deleted_at IS NULL
GROUP BY JSON_EXTRACT(attributes, '$.model_name');
-- distinct_bom_count = 1 이면 안전 (동일 모델의 BOM이 같음)

-- 3. 다른 테이블에서 FG item_id 참조 확인
SELECT 'quote_items' as tbl, COUNT(*) as cnt
FROM quote_items WHERE item_id IN (
  SELECT id FROM items WHERE tenant_id = 287 AND item_type = 'FG'
)
UNION ALL
SELECT 'work_order_items', COUNT(*)
FROM work_order_items WHERE item_id IN (
  SELECT id FROM items WHERE tenant_id = 287 AND item_type = 'FG'
);
-- 모두 0이면 안전하게 통합 가능

4.6 핵심 API 메서드 참조 (읽기 전용)

아래 메서드들은 변경하지 않지만 동작을 이해하기 위해 참조:

FormulaEvaluatorService::getItemDetails() (line 1102-1134):

public function getItemDetails(string $itemCode, ?int $tenantId = null): ?array
{
    $item = DB::table('items')
        ->where('tenant_id', $tenantId)
        ->where('code', $itemCode)     // ← 여기서 code로 조회
        ->whereNull('deleted_at')
        ->first();
    // ... id, code, name, item_type, item_category, bom 등 반환
}

→ 통합 후 getItemDetails('KWE01') 호출 시 code='KWE01' 품목 정상 조회

FormulaEvaluatorService::calculateKyungdongBom() 핵심 흐름 (line 1574~):

1. getItemDetails($finishedGoodsCode) → 완제품 조회
2. $productCategory = $finishedGoods['item_category'] → 'SCREEN' 또는 'STEEL'
3. $productModel, $finishingType, $installationType, $motorVoltage 결정
4. $calculatedVariables = array_merge($inputVariables, [...])
5. KyungdongFormulaHandler::calculateDynamicItems($calculatedVariables) 호출

item_category는 items 레코드에서 가져오므로 통합 후에도 정상 (KWE01 → SCREEN)

KyungdongFormulaHandler finishing_type 사용처:

  • calculateSteelItems() line 458: $rawFinish = $params['finishing_type'] ?? 'SUS'
  • calculateGuideRails() line 540: 파라미터로 수신
  • getBottomBarPrice() line 561: 가격 조회에 사용
  • getGuideRailPrice() line 696: 가격 조회에 사용 → 모두 $calculatedVariables['finishing_type']에서 값을 가져오므로 매핑만 추가하면 됨

React getFinishedGoods() (actions.ts line 302-317):

const result = await executeServerAction<FGApiResponse>({
  url: buildApiUrl('/api/v1/items', {
    item_type: 'FG',
    has_bom: '1',
    size: '5000',
  }),
});

item_type='FG'로 조회하므로 code 변경 영향 없음. 통합 후 6개만 반환됨.


5. 컨펌 대기 목록

# 항목 변경 내용 영향 범위 상태
1 FG 품목 통합 마이그레이션 18개 → 6개, BOM 재매핑 DB, 모든 FG 참조 대기
2 12개 FG 품목 soft delete 통합 후 불필요 품목 삭제 DB 대기

6. 변경 이력

날짜 항목 변경 내용 파일 승인
2026-02-19 - 문서 초안 작성 - -
2026-02-19 혼합형 지원 GT validation에 mixed 추가 QuoteBomCalculateRequest, QuoteBomBulkCalculateRequest
2026-02-19 모터 전압 MP → motor_voltage 매핑 추가 FormulaEvaluatorService
2026-02-19 가이드레일 GT → installation_type 매핑 추가 FormulaEvaluatorService
2026-02-19 혼합형 UI GUIDE_RAIL_TYPES에 mixed 옵션 추가 LocationDetailPanel

7. 참고 문서

  • 품목 정책: docs/rules/item-policy.md
  • 견적 시스템: docs/features/quotes/README.md
  • DB 스키마: docs/specs/database-schema.md
  • 품질 체크리스트: docs/standards/quality-checklist.md
  • 빠른 시작: docs/quickstart/quick-start.md
  • 견적 계산 계획: docs/plans/kd-quote-logic-plan.md
  • 경동 품목 시더: api/database/seeders/Kyungdong/KyungdongItemSeeder.php

8. 세션 및 메모리 관리 정책

8.1 세션 시작 시

read_memory("fg-consolidation-state")
read_memory("fg-consolidation-snapshot")
계획 문서 읽기 → docs/plans/fg-code-consolidation-plan.md

8.2 작업 중 관리

컨텍스트 잔량 Action 내용
30% 이하 Snapshot write_memory("fg-consolidation-snapshot", ...)
20% 이하 Symbol Tracking write_memory("fg-consolidation-active-symbols", ...)
10% 이하 Stop & Save 최종 상태 저장 후 세션 교체 권고

8.3 Serena 메모리 구조

  • fg-consolidation-state: { phase, progress, next_step, last_decision }
  • fg-consolidation-snapshot: 코드 변경점 + 논의 요약
  • fg-consolidation-rules: 불변 규칙 (코어 로직 변경 없음, BOM FK 안전 등)
  • fg-consolidation-active-symbols: 수정 중인 파일/심볼 리스트

9. 검증 결과

9.1 테스트 케이스

입력값 예상 결과 실제 결과 상태
KWE01 + wall + SUS + W0=2000 + H0=3000 FG-KWE01-벽면형-SUS 동일 결과 -
KWE01 + floor + EGI + W0=2000 + H0=3000 FG-KWE01-측면형-EGI 동일 결과 -
KWE01 + mixed + SUS + W0=2000 + H0=3000 혼합형 계산 정상 -
KWS01 + wall + SUS + W0=2000 + H0=3000 FG-KWS01-벽면형-SUS 동일 결과 -
KWE01 + three + SUS + W0=5000 + H0=5000 삼상 모터 + SUS 정상 -

9.2 성공 기준

기준 달성 비고
FG 품목 18개 → 6개 통합
BOM 계산 결과 통합 전후 동일 모든 조합
견적 등록 → 산출 → 저장 정상
마감재질 선택 UI 동작
기존 기능 회귀 없음

10. 자기완결성 점검 결과

10.1 체크리스트 검증

# 검증 항목 상태 비고
1 작업 목적이 명확한가? 1.1 배경, 1.2 목표
2 성공 기준이 정의되어 있는가? 9.2 성공 기준
3 작업 범위가 구체적인가? 2. 대상 범위 13개 항목
4 의존성이 명시되어 있는가? Phase 순서 = 의존성
5 참고 파일 경로가 정확한가? 4.4 핵심 파일 변경 목록
6 단계별 절차가 실행 가능한가? 3.1 + 4.x 상세
7 검증 방법이 명시되어 있는가? 9.1 테스트 케이스
8 모호한 표현이 없는가? 구체적 코드/파일명 명시

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

질문 답변 가능 참조 섹션
Q1. 이 작업의 목적은 무엇인가? 1.1, 1.2
Q2. 어디서부터 시작해야 하는가? 3.1 Step 1
Q3. 어떤 파일을 수정해야 하는가? 4.4 핵심 파일 변경 목록
Q4. 작업 완료 확인 방법은? 9.1, 9.2
Q5. 막혔을 때 참고 문서는? 7. 참고 문서

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


11. 리스크 및 롤백

11.1 리스크 평가

리스크 확률 영향 대응
BOM parent_item_id 누락 마이그레이션 전 BOM 전수 검증 쿼리 실행
견적 계산 결과 불일치 통합 전후 동일 입력 비교 테스트 5건 이상
기존 데이터 호환성 깨짐 현재 quote_items에 FG 코드 참조 데이터 없음
프론트 productCode 참조 오류 46개 참조 지점 전수 확인

11.2 롤백 전략

  • DB 마이그레이션은 Laravel down() 메서드로 롤백 가능하도록 작성
  • 마이그레이션 실행 전 items + BOM 데이터 백업 쿼리 준비
  • API/React 변경은 git revert로 원복 가능

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