## 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>
11 KiB
id, title, status, date, related-wiki, related-adr, principles
| id | title | status | date | related-wiki | related-adr | principles | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ADR-004 | cimery Sprint 25~39 아키텍처 결정 — 거더교 MVP 확장·IFC4X3 Add2·proc-macro 스캐폴딩 | accepted | 2026-04-15 |
|
|
|
ADR-004 — Sprint 25~39 아키텍처 결정
Sprint 124 기반 위에서 거더교 MVP 확장과 IFC4X3 Add2 익스포터 Phase 13b 구축 중 내린 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 로 분리:
- 상부구조 (경간·거더 5개)
- 바닥판 (슬래브 두께·헌치)
- 선형·기하 (경사각·솟음·변단면)
- 하부구조 (교각 형식)
- 추가 부재 (가로보·신축이음·격벽)
- 표시 (선형·투영)
근거: 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 = 28cimery-viewermesh helpers: 9 (camber/rotate/variable_depth)cimery-macrosderive: 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 타임라인