qcast-front/docs/superpowers/specs/2026-06-24-roof-shape-pattern-catalog-design.md
sangwook yoo 105c8470ad docs(roof-shape): 지붕형상 패턴 카탈로그 설계 스펙 추가
[작업내용] :
- PDF 분석→지붕형상 자동 매칭의 선행 단계로 8패턴 카탈로그 설계 정의
- 매칭 키: type+ridge+flows(화면축) 시그니처, flows 집합만으로 8패턴 유일 식별
- 5~8 화면축↔버튼 매핑을 useRoofShapeSetting case 4~8 에서 1:1 전사
- 매칭 함수·PDF 분석 확장은 다음 작업으로 분리(계약만 명시)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 10:43:18 +09:00

9.0 KiB

지붕형상 패턴 카탈로그 — 설계 (Design Spec)

  • 일자: 2026-06-24
  • 브랜치: feature/pv-btob
  • 상태: 설계 확정 (구현 대기)

1. 목적 / 배경

PDF 도면 업로드 → 외벽선 미확정 분석 → 외벽선 자동 확정 팝업(AutoOuterLineFixSetting)까지는 구현 완료. 다음 목표는 분석된 도면으로부터 지붕 형상을 유추해, 지붕형상 설정 팝업(RoofShapeSetting)의 8개 패턴 버튼 중 매칭되는 것을 자동 클릭하는 기능이다.

그 자동 매칭을 구현하려면, 먼저 8개 패턴 각각이 "무엇인지"를 매칭 가능한 시그니처로 정의·저장해 두어야 한다. 본 스펙은 그 카탈로그 데이터 모듈만을 다룬다.

범위 경계 (중요)

  • 이번 작업(본 스펙): 8패턴 카탈로그 상수 모듈 1개 + 경량 접근자 + distinct 자기검증.
  • 다음 작업(별도): ① PDF/Gemini 분석을 확장해 지붕형상 descriptor 산출(屋根伏図의 棟/軒先/ケラバ → 용마루·흐름), ② matchRoofShapePattern(descriptor) → shapeNum 매칭 로직, ③ 매칭된 버튼 자동 클릭/적용.
  • 본 작업에서 매칭 함수는 구현하지 않는다. descriptor 계약(스키마)만 명시해 카탈로그가 다음 단계와 짝이 맞도록 한다. (CLAUDE.md Simplicity First)

2. 8패턴의 실제 구조 (코드 검증)

근거: src/hooks/roofcover/useRoofShapeSetting.jsshapeMenu(L164173) 와 handleSave(L184486) — saveRidge / saveAPattern / saveBPattern / case 4~8.

shapeNum 버튼 nameKey 지붕유형 용마루 변 역할 배정 (코드 근거) 흐름방향(화면축)
1 modal.roof.shape.setting.ridge (용마루) 寄棟 hip 모서리 hip 전 변 = 처마(saveRidge) top·bottom·left·right
2 modal.roof.shape.setting.patten.a (패턴A) 切妻 gable 가로 상하=처마 / 좌우=케라바(saveAPattern) top·bottom
3 modal.roof.shape.setting.patten.b (패턴B) 切妻 gable 세로 좌우=처마 / 상하=케라바(saveBPattern) left·right
4 modal.roof.shape.setting.side (변별로 설정) 수동 없음 변마다 수동(처마/케라바/벽/팔작/반절처/한쪽흐름)
5 commons.west (서) 片流れ shed shed 처마=bottom, shed=top, 케라바=좌우 (case 5) bottom
6 commons.east (동) 片流れ shed shed 처마=top, shed=bottom, 케라바=좌우 (case 6) top
7 commons.south (남) 片流れ shed shed 처마=right, shed=left, 케라바=상하 (case 7) right
8 commons.north (북) 片流れ shed shed 처마=left, shed=right, 케라바=상하 (case 8) left

5~8 화면축 ↔ 버튼 정확 매핑 (전사 근거)

정규 winding(outerLines[0].direction === 'bottom') 기준. case 4 의 SHED direction 분기(bottom→east, top→west, left→south, right→north)와 교차검증 일치:

  • 처마(flow) BOTTOM → 버튼 5 (서/west)
  • 처마 TOP → 버튼 6 (동/east)
  • 처마 RIGHT → 버튼 7 (남/south)
  • 처마 LEFT → 버튼 8 (북/north)

winding 이 다르면 case 58 의 else 분기가 top/bottom(또는 left/right)을 미러링한다. initLineSetting(L488516)이 outerLines[0].direction==='right' 일 때 top↔bottom 을 스왑해 정규화하며, PDF 임포트 좌표는 usePdfImport.ensureCounterClockwise 로 CCW 정규화된다. 카탈로그는 정규 winding 기준 canonical 값을 기록하고, 다음 단계 매칭기는 동일 정규 프레임의 flow 를 산출해야 한다.

3. 시그니처 어휘 (방위적용 前 → 화면축 기준)

지붕형상 설정 시점에는 실제 방위가 없다 — 방위적용(MENU.MODULE_CIRCUIT_SETTING.PLAN_ORIENTATION)은 모듈/회로 단계다. 따라서:

  • 흐름방향은 화면축 4방향(top/bottom/left/right)으로 인코딩한다.
  • 5~8 의 서/동/남/북은 코드가 쓰는 앱 고유 라벨(direction: 'west'|'east'|'south'|'north')로 그대로 보존한다(버튼 정체성).

4. 데이터 모듈 설계

신규 파일: src/common/roofShapePattern.js

// 화면축 흐름방향 (방위적용 前)
export const ROOF_FLOW = { TOP: 'top', BOTTOM: 'bottom', LEFT: 'left', RIGHT: 'right' }

// 지붕 유형
export const ROOF_SHAPE_TYPE = { HIP: 'hip', GABLE: 'gable', SHED: 'shed', MANUAL: 'manual' }

// 용마루 방향
export const ROOF_RIDGE = { HIP: 'hip', HORIZONTAL: 'horizontal', VERTICAL: 'vertical', SHED: 'shed', NONE: 'none' }

/**
 * 지붕형상 설정 팝업(RoofShapeSetting)의 8개 버튼 카탈로그.
 * shapeNum 은 useRoofShapeSetting.shapeMenu 의 id 이자 자동 클릭 대상(= setShapeNum 값).
 * 매칭 키: type + ridge + flows(집합). 8개가 상호 배타적이라 flows 집합만으로도 유일 식별 가능.
 */
export const ROOF_SHAPE_PATTERNS = [
  { shapeNum: 1, key: 'ridge',    nameKey: 'modal.roof.shape.setting.ridge',
    type: 'hip',    ridge: 'hip',        flows: ['top', 'bottom', 'left', 'right'], autoMatchable: true,
    note: '寄棟 — 전 변 처마, 사방 흐름' },
  { shapeNum: 2, key: 'patternA', nameKey: 'modal.roof.shape.setting.patten.a',
    type: 'gable',  ridge: 'horizontal', flows: ['top', 'bottom'],                  autoMatchable: true,
    note: '切妻 — 상하 처마/좌우 케라바, 가로 용마루' },
  { shapeNum: 3, key: 'patternB', nameKey: 'modal.roof.shape.setting.patten.b',
    type: 'gable',  ridge: 'vertical',   flows: ['left', 'right'],                  autoMatchable: true,
    note: '切妻 — 좌우 처마/상하 케라바, 세로 용마루' },
  { shapeNum: 5, key: 'west',     nameKey: 'commons.west',
    type: 'shed',   ridge: 'shed',       flows: ['bottom'], direction: 'west',      autoMatchable: true },
  { shapeNum: 6, key: 'east',     nameKey: 'commons.east',
    type: 'shed',   ridge: 'shed',       flows: ['top'],    direction: 'east',      autoMatchable: true },
  { shapeNum: 7, key: 'south',    nameKey: 'commons.south',
    type: 'shed',   ridge: 'shed',       flows: ['right'],  direction: 'south',     autoMatchable: true },
  { shapeNum: 8, key: 'north',    nameKey: 'commons.north',
    type: 'shed',   ridge: 'shed',       flows: ['left'],   direction: 'north',     autoMatchable: true },
  // 변별로 설정 — 자동매칭 비대상(폴백). 깨끗한 매칭 실패 시 후보로만 표시.
  { shapeNum: 4, key: 'side',     nameKey: 'modal.roof.shape.setting.side',
    type: 'manual', ridge: 'none',       flows: [],                                 autoMatchable: false },
]

// 경량 접근자
export const getRoofShapePatternByShapeNum = (shapeNum) =>
  ROOF_SHAPE_PATTERNS.find((p) => p.shapeNum === shapeNum) || null

설계 원칙:

  • 변 역할 배정 로직(처마/케라바 실제 적용)은 useRoofShapeSetting 에 그대로 둔다. 카탈로그는 매칭 룩업 테이블 역할만 하며 역할 배정 로직을 중복 보유하지 않는다(중복 = 유지보수 리스크).
  • 자동 클릭은 다음 단계에서 shapeNumsetShapeNum(shapeNum) → 기존 handleSave 경로로 처리한다(별도 신규 적용 로직 불필요).

5. 다음 단계 계약 (본 작업 미구현, 스펙에만 명시)

// PDF 분석 확장이 산출할 지붕형상 서술자
RoofShapeDescriptor = {
  type:  'hip' | 'gable' | 'shed' | 'manual',
  ridge: 'hip' | 'horizontal' | 'vertical' | 'shed' | 'none',
  flows: Array<'top'|'bottom'|'left'|'right'>,   // 정규 winding 화면축
  direction?: 'west'|'east'|'south'|'north',     // shed 일 때
  confidence?: number,                            // 0~1
}

// 다음 작업에서 구현:
matchRoofShapePattern(descriptor) -> shapeNum | null   // flows 집합 + type/ridge 대조, 실패 시 4(수동) 폴백 or null

6. 검증

테스트 러너 미연동(CLAUDE.md). 따라서:

  • distinct 불변식: 8개 항목의 flows 집합(+type,ridge)이 상호 유일함을 보장. 모듈 내 순수 헬퍼 또는 주석으로 문서화(불변식 명시). 별도 테스트 프레임워크는 도입하지 않는다.
  • yarn lint 통과(에러 0, 경고 최소화) + 빌드 통과.
  • Prettier: 싱글쿼트, 세미콜론 없음, tabWidth 2, printWidth 150.

7. 영향 범위 / 변경 파일

  • 신규: src/common/roofShapePattern.js
  • 무변경: useRoofShapeSetting.js, RoofShapeSetting.jsx, Gemini route, usePdfImport.js — 본 작업은 데이터 추가만, 기존 플로우 미변경.
  • (선택) 문서: 본 스펙으로 충분. CLAUDE.md 갱신은 다음 매칭 단계에서 함께.

8. 리스크 / 미해결

  • 5~8 winding 의존성: canonical 값으로 기록하되, 다음 매칭기가 동일 정규 프레임의 flow 를 생성해야 정확. (분석 확장 작업의 책임으로 이관.)
  • L자·凹凸 복합 footprint: 1·2·3·5~8 의 깔끔한 매칭은 직사각형 계열을 가정. 복합형은 패턴 4(수동) 폴백 — 폴백 판정 정책은 다음 단계.
  • 본 카탈로그는 버튼 식별/매칭 전용이며, 실제 지붕 생성은 기존 handleSave 가 담당한다.