--- 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` (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 타임라인