Files
ParaWiki/Output/reports/ADR-004-sprint-25-39-decisions.md
minsung e32c09df2d 품질 강화 — ADR-004 + IFC snapshot 테스트 + helper 유닛 + clippy 경고 정리
## ADR-004 (Output/reports/ADR-004-sprint-25-39-decisions.md)
Sprint 25~39 기간의 **15개 아키텍처 결정** 정리:
- D1~D9: 거더교 MVP 확장 (단면 분기·다경간·Skew 관례·방호벽·격벽·Camber·헌치·UI)
- D10~D13: IFC4X3 Add2 익스포터 4 결정 (크레이트 분리·형상 전략 3단계·GUID·Camber 근사)
- D14: proc-macro 스캐폴딩 (전면 #[param] 는 Feature 10+ 안정 후)
- D15: 변단면 거더 알고리즘 (소핏 lift + Y 선형보간)
- 미결 6항목 (Pset 확장·LinearPlacement·ElementAssembly·IfcPile·#[param] 전면·변단면 IFC)
- 테스트 커버리지 101개 현황표

## IFC 스냅샷 테스트 (crates/ifc/tests/snapshot_tests.rs)
insta 기반 회귀 방지, 8개 baseline:
- mask_guids(): 22자 IFC GUID 를 'GUID' 로 정규화 (결정적 비교 가능)
- 시나리오: 기본 단경간 PSC-I / 2경간 π형 / skew 15° / camber 50mm /
  Rectangle 단면 / parapets off
- mask_guids 자체 유닛 테스트 2개

## Mesh helper 유닛 테스트 (crates/viewer/src/bridge_scene.rs helper_tests)
순수 함수 9개 검증:
- apply_camber_mesh: zero 항등·midspan 도달값·경간 밖 미영향
- rotate_y_around_z: 0 회전 항등·90° 피봇 회전·정점 개수 보존
- apply_variable_depth: zero 항등·소핏 lift · 지점 0 lift

## clippy lib 경고 15+ → 0
- map_identity (kernel/expansion_joint.rs)
- unnecessary_lazy_evaluations ×4 (dsl/abutment·pier·csv_template — auto-fix)
- too_many_arguments (usd save_scene — allow with justification)
- clamp-like 패턴 ×7 (viewer bridge_scene/incremental_scene 의 .max(1).min(N) → .clamp(1, N))
- redundant_closure ×2 (project_file 의 `|e| Error::other(e)` → `Error::other`)
- redundant_guard ×1 (viewer KeyboardInput match guard → 패턴 내 직접 매치)

cargo clippy --workspace --lib: 0 경고.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 08:37:11 +09:00

240 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: ADR-004
title: cimery Sprint 25~39 아키텍처 결정 — 거더교 MVP 확장·IFC4X3 Add2·proc-macro 스캐폴딩
status: accepted
date: 2026-04-15
related-wiki:
- "[[교각 형식 분류]]"
- "[[교각 파라미터 카탈로그]]"
- "[[선형 GIS 기반 좌표계]]"
- "[[특징 형상 기반 모델링 FBM]]"
related-adr:
- "[[ADR-001-tech-stack]]"
- "[[ADR-002-feature-dsl]]"
- "[[ADR-003-architecture-followups]]"
principles: [비패밀리, 증분, 선형-GIS]
---
# ADR-004 — Sprint 25~39 아키텍처 결정
Sprint 1~24 기반 위에서 거더교 MVP 확장과 IFC4X3 Add2 익스포터 Phase 1~3b 구축 중 내린 **15개 결정**을 정리. 이후 세대 에이전트가 설계 의도를 빠르게 소환하도록 한다.
## 결정 요약표
| # | 주제 | 결정 | Sprint |
|---|---|---|---|
| D1 | 단면 타입 분기 | SceneParams `section_type` 로 런타임 분기. `build_selectable_scene` 하드코딩 금지 | 25 |
| D2 | `span_m` 의미 | **경간 당** 길이(총 길이 아님). 다경간 총 길이 = span_m × span_count | 26 |
| D3 | 피어 MVP 스펙 | wiki 112개 변수 중 **Phase 1 = 30% 입력만**. 기본 사각기둥·CSB 2m | 26 |
| D4 | 거더·데크 Skew | **미적용** (precast 거더 관례). 교대·교각·받침·신축이음만 회전 | 27 |
| D5 | 방호벽 표현 | 뷰어: 독립 RC 박스 / IFC: `IfcRailing .GUARDRAIL.` | 28 |
| D6 | 격벽 표현 | 뷰어 전용 (IFC 미구현, Phase 4 ElementAssembly 로드맵) | 29 |
| D7 | Camber 알고리즘 | 포물선 `4·mid·u·(span-u)/span²`, 경간별 독립 적용 | 30 |
| D8 | 헌치 Y 기준 | 데크 soffit Y = `bearing_h + girder_h + haunch_depth` | 31 |
| D9 | UI 카테고리 | 6 섹션 CollapsingHeader 분리 (상부/바닥판/기하/하부/추가/표시) | 32 |
| D10 | IFC 크레이트 분리 | `cimery-ifc` 독립 크레이트. viewer/app 에서 선택적 의존 | 33 |
| D11 | IFC 형상 전략 | Phase 1 Rectangle → Phase 2 PSC-I Arbitrary → Phase 3 Alignment | 33-36 |
| D12 | IFC GUID | UUIDv4 → base64-22 (buildingSMART charset `0-9A-Za-z_$`) | 33 |
| D13 | IFC Camber 근사 | `camber > 0` 시 거더를 N=10 세그먼트 `IfcExtrudedAreaSolid` 로 분할 | 37 |
| D14 | proc-macro 스캐폴딩 | `cimery-macros` 최소 선행 — `#[derive(ParamSummary)]` 만, 전면 `#[param]` 은 Phase 2 | 38 |
| D15 | 변단면 거더 알고리즘 | 포물선 soffit lift + Y 선형보간 `y_new = y + lift·(1 - y/h)` | 39 |
---
## D1. 단면 타입 런타임 분기
**맥락**: `build_selectable_scene``SectionType::PscI` 를 하드코딩. 사용자가 SteelBox 를 선택해도 PscI 로 렌더되는 버그.
**결정**: `SceneParams.section_type` 로 런타임 분기. `build_bridge_scene` 과 동일한 match 패턴 적용.
**근거**:
- 하드코딩은 FBM(비패밀리 원칙) 위반.
- `build_bridge_scene` 에 이미 올바른 패턴 존재 → 한 쪽만 틀린 비대칭 버그.
**영향**: SteelBox·PscU·SteelPlateI 추가 시 `match p.section_type` 에 한 줄만 추가. 카탈로그 확장 비용 최소화.
---
## D2. `span_m` 의미 재정의
**맥락**: 단경간 가정 하에 `span_m = 전체 교량 길이` 로 사용. 다경간 추가 시 의미 충돌.
**결정**: `span_m` = **경간 당** 길이. `span_count` 신규 도입. `total_mm = span_m × span_count × 1000`.
**근거**:
- 실무 교량 설계는 "경간 × N" 으로 사고 (예: "40m × 3 연속교").
- ProjectFile 호환성: v1 저장 파일(`span_count` 없음)은 `default = 1` 로 기존 단경간 동작 유지.
**대안 기각**:
- `total_length_m + span_count` (계산 필요, 사고 무겁)
- `span_lengths: Vec<f64>` (MVP 과도, Phase 2+)
---
## D3. 피어 MVP 스펙 — wiki Phase 1
**맥락**: ParaWiki `[[교각 파라미터 카탈로그]]`**112개 변수** vs 현재 `PierIR` ~8개 변수 격차.
**결정**: Phase 1 MVP = 사용자 입력 **30% ≈ 30개 변수 중 핵심 5개만**:
- `pier_type` (T/π), `column_count` (1/2), `column_diameter/depth` (CSB 매트릭스 기본값 2000mm), `column_height`, `cap_beam` 치수.
나머지 82개 자동계산·상세 변수는 Phase 2~4 로드맵 (wiki 안내 순서).
**근거**:
- MVP 원칙: 기능 축소 OK, 아키텍처 타협 금지.
- 112개 전면 구현은 IR·DSL·UI·IFC 4 레이어 동시 확장 필요 → Sprint 1개에 불가.
- wiki 의 "Phase 1" 권장 순서가 엔지니어링 검증된 우선순위.
---
## D4. 거더·데크 Skew 미적용
**맥락**: 경사교(skew bridge) 표현 방식 선택.
**결정**: `skew_deg` 파라미터는 **교대·교각·받침·신축이음에만** Y축 회전 적용. **거더·데크는 직선 유지**.
**근거**:
- Precast 거더 스큐 교량의 **일반 관례** — 거더는 직교 제작, 교대 seat 만 사각으로 절단.
- 사각 데크 슬래브는 현장 타설 평행사변형으로 맞춤 (본 MVP 스코프 밖).
- 거더 회전 시 mesh 경계 교대와 맞물리는 정합성 문제 발생.
**영향**: `rotate_y_around_z()` 헬퍼로 지점부 요소만 후처리 회전. 기하 복잡도 최소화.
---
## D5~D6. 방호벽·격벽 표현 경로
**D5 방호벽 (Parapet)**:
- 뷰어: 독립 RC 박스(500mm × 1200mm × 전체 길이) 양쪽 엣지
- IFC: `IfcRailing .GUARDRAIL.`
**D6 격벽 (Diaphragm)**:
- 뷰어 전용. IFC 미구현.
- 향후 `IfcElementAssembly` 로 Pier 그룹 + Diaphragm 포함 예정 (Phase 4).
**근거**:
- IFC 표준에서 방호벽 → `IfcRailing` 이 가장 근접.
- 격벽은 IFC4X3 에 전용 엔티티 없음 — `IfcBeam` 또는 `IfcElementAssembly` 내부 원소로 표현.
- MVP 에선 격벽을 IFC 스킵하고 뷰어만 지원 → 범위 축소.
---
## D7. Camber 알고리즘
**결정**: 포물선 수식 `y_off(u) = 4 · mid · u · (span - u) / span²`, 경간별 독립 적용.
**근거**:
- 실무 거더 precamber 근사식과 정합 (중앙에서 최대, 양단 0).
- 경간별 독립 → 다경간 연속교에서도 각 경간이 올바른 솟음.
- 정점 단위 Y 오프셋만 추가 → mesh 생성 파이프라인 변경 최소.
**구현 위치**: `apply_camber_mesh(mesh, z0, z1, mid_mm)` — 순수 함수, 단위 테스트 가능.
---
## D8. 헌치 Y 기준
**결정**: 데크 soffit Y = `bearing_h + girder_h + haunch_depth` (헌치 0 이면 거더 상면 = 데크 소핏).
**근거**:
- 현실 bridge 단면: 거더 top flange → 헌치 블록 → 데크 soffit 순서.
- 단일 변수 `haunch_depth` 로 데크 높이·신축이음·방호벽 기준선 전부 이동 (6군데 일괄 수식 변경).
---
## D9. UI 카테고리 6 섹션
**맥락**: Sprint 14 ~ 31 누적 슬라이더·체크박스 11개가 한 덩어리 섹션에 쌓임.
**결정**: 6개 `CollapsingHeader` 로 분리:
1. 상부구조 (경간·거더 5개)
2. 바닥판 (슬래브 두께·헌치)
3. 선형·기하 (경사각·솟음·변단면)
4. 하부구조 (교각 형식)
5. 추가 부재 (가로보·신축이음·격벽)
6. 표시 (선형·투영)
**근거**: Revit UX 패턴과 정합 (카테고리별 속성 분류). 슬라이더 10+ 개 평면 배치는 스캐닝 비용 높음.
**매크로 hygiene 부산물**: `ps!` 매크로가 외부 `ui` 를 캡처하는 문제를 `ps!($ui, ...)` 명시 매개변수화로 해결.
---
## D10~D13. IFC 익스포터 4 결정
**D10 크레이트 분리**: `cimery-ifc` 독립. viewer/app 에서 선택적 의존 (deps: `cimery-ifc = { workspace = true }`). 크로스 컴파일·WASM 호환성 격리.
**D11 형상 전략 3단계**:
- **Phase 1** (Sprint 33): `IfcRectangleProfileDef` 일률 — 엔티티 계층·워크플로 검증용
- **Phase 2** (Sprint 34): `IfcArbitraryClosedProfileDef` + `IfcPolyline` 로 PSC-I 14점 실제 단면
- **Phase 3b** (Sprint 36): `IfcAlignment` 독립 엔티티 — LinearPlacement 는 Phase 3c+
**D12 GUID**: UUIDv4 → buildingSMART base64-22 (`0-9A-Za-z_$` charset).
- 대안 기각: IFC1 규격 Base85 — 길이 20자지만 charset 가독성 낮음.
**D13 Camber 근사**: `camber > 0` 시 거더를 `CAMBER_SEGMENTS = 10``IfcExtrudedAreaSolid` 로 분할.
- 대안 기각 1: `IfcSurfaceCurveSweptAreaSolid` — directrix curve 정의 복잡, 뷰어 지원도 낮음.
- 대안 기각 2: 단일 extrude + 속성에 camber 값만 기록 — 기하가 평평함, 시각 검증 불가.
- 선택: 다중 세그먼트 포물선 근사. 10 세그먼트 해상도면 ~1mm 오차, BIM 뷰어에서 곡선처럼 보임.
---
## D14. proc-macro 스캐폴딩
**맥락**: ADR-002 D `#[param(unit, range, default)]` 전면 구현 제안 대비 현재 Feature 카탈로그 규모.
**결정**: Sprint 38 에서 `cimery-macros` 크레이트와 `#[derive(ParamSummary)]` **최소 선행만**. 전면 `#[param]` attribute 는 카탈로그 10+ feature 안정 후.
**근거**:
- 현재 Feature = 거더·데크·받침·피어·교대·가로보·신축이음 7종. 수동 builder 로 관리 가능.
- Proc-macro 전면 구현은 큰 투자 — 실제 사용처(중복 보일러플레이트)가 누적돼야 ROI.
- 크레이트 인프라(syn/quote) 만 먼저 세팅 → 향후 `#[param]` 추가 시 진입 비용 최소.
---
## D15. 변단면 거더 알고리즘
**결정**: 포물선 soffit lift + Y 선형보간.
- `lift(u) = 4 · max · u · (span - u) / span²`
- `y_new = y + lift(u) · (1 - y/h)`
**근거**:
- 연속교 중앙부 단면 축소의 물리적 모델: **상면 유지(도로면 평탄) + 소핏 상승** = web 축소.
- 선형 보간 계수 `(1 - y/h)` 로 정점 클래스 구분 없이(top/web/bottom 구분 불필요) 균일 처리.
- 단일 함수 `apply_variable_depth(mesh, z0, z1, max, girder_h)` — camber 와 독립 조합 가능.
**영향**: 상수 단면 mesh 생성 후 post-process 만 추가. 커널(psc_i.rs·steel_box.rs) 수정 불필요.
---
## 미결·재검토 항목
- **C1 Pset_BearingCommon / Pset_SlabCommon**: Sprint 35 에서 Pset_BeamCommon 만 구현. 후속 스프린트에서 확장.
- **C2 IfcLinearPlacement**: Sprint 36 에서 IfcAlignment 만 추가, 요소 배치는 아직 `IfcLocalPlacement`. Phase 3c.
- **C3 IfcElementAssembly**: Pier 의 column + cap beam + footing 을 그룹화. Phase 4.
- **C4 IfcPile**: IFC4X3 신규 엔티티. 현재 피어 기초는 IfcFooting 으로 통합 표현. Phase 4.
- **C5 `#[param]` 전면 구현**: D14 언급, 카탈로그 확장 시점에 재검토.
- **C6 변단면 거더 IFC 반영**: Sprint 37 Camber 와 동일하게 세그먼트 분할 기법 적용 가능.
---
## 테스트 커버리지
Sprint 25~39 기간 추가된 테스트:
- `cimery-ifc`: 20 lib + 8 snapshot = 28
- `cimery-viewer` mesh helpers: 9 (camber/rotate/variable_depth)
- `cimery-macros` derive: 3
- 기존 kernel 4층 테스트: 61 유지
**61 + 40 = 101 테스트 통과** 현황.
---
## 관련
- [[ADR-001-tech-stack]]
- [[ADR-002-feature-dsl]]
- [[ADR-003-architecture-followups]]
- `cimery/crates/ifc/` 구현
- `cimery/crates/macros/` 구현
- PROGRESS.md Sprint 25~39 타임라인