[작업내용] : - 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>
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.js 의 shapeMenu(L164173) 와 486) — handleSave(L184saveRidge / 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 5
8 의516)이else분기가 top/bottom(또는 left/right)을 미러링한다.initLineSetting(L488outerLines[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에 그대로 둔다. 카탈로그는 매칭 룩업 테이블 역할만 하며 역할 배정 로직을 중복 보유하지 않는다(중복 = 유지보수 리스크). - 자동 클릭은 다음 단계에서
shapeNum→setShapeNum(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가 담당한다.