- `docs/` 를 .gitignore 에서 빼 git 추적 대상으로 전환 (위키·완료 이력·검증 기록) - `docs/raw/PLAN.md` · `docs/raw/OWNERS.md` 를 저장소 최상위로 이동 후 .gitignore 에 등록 — 창끼리 공유하되 저장소에는 안 올리는 장부 - 살아 있는 경로 참조 7개 파일 정정 (아카이브 107건은 그때 사실이라 그대로 둠) - graphify 날짜별 산출물(`docs/wiki/graphify-out/20*/`) 제외 — `graphify update` 가 다시 만듦 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
9.5 KiB
9.5 KiB
PLAN: B05/B06 종·횡단도 반응형 레이아웃 및 마커 직접 드래그 (완료 이력)
🎯 활성 계획: B05/B06 종·횡단도 반응형 레이아웃 및 마커 직접 드래그 (2026-07-19)
배경 (사용자 요청 4건)
- B05 하단 종단도가 브라우저 폭 축소 시 높이(도면 크기)부터 줄어듦 → 하단 가로 스크롤바 방식으로 변경
- 배치된 포인트를 3D 뷰에서 직접 드래그로 위치 변경 (0_old I-401 기능 이식, 클릭/드래그 판별 필요)
- B06 종단도가 폭에 따라 높이가 계속 변함(가로세로비 고정) + sticky 상단 고정이 실제로 동작하지 않음
- B06 횡단도가 화면 크기 따라 계속 확대/축소되다가 일정 폭 이하에서 우측 오버플로우 발생
원인 분석 (코드 확인 완료)
| 증상 | 원인 위치 | 원인 |
|---|---|---|
| B05 종단도 축소 | B05_wf2_Route_UI_Style.css:68-73 |
SVG에 width/height:100% 강제. SVG는 고정 viewBox(1200×220)의 비율을 유지(preserveAspectRatio 기본값)하므로 폭 축소 시 도면 전체가 비례 축소 |
| B06 종단도 높이 변동 | B06_wf3_ProfileCross_UI_Style.css:163-168 |
.b06-section__chart { width:100%; height:auto } + 고정 viewBox → 폭에 비례해 높이 변동 |
| B06 sticky 미동작 | ui_template_workflow_layout.css:118-124 |
.ui-workflow-layout__main이 overflow:auto이지만 높이 제한 없이 내용만큼 늘어나 실제 스크롤은 document에서 발생. sticky의 기준 스크롤포트가 "스크롤되지 않는 __main"으로 잡혀 무효화 |
| B06 횡단도 오버플로우 | B06_wf3_ProfileCross_UI_Style.css:163-168, 179-183 |
min-width:520px + 2열 고정 grid(repeat(2, minmax(0,1fr))). 열 폭 < 520px이면 오버플로우. 기존 media query(900px)는 뷰포트 기준이라 사이드바 열림 상태의 실제 컨테이너 폭과 불일치 |
| 마커 직접 드래그 부재 | B05_wf2_Route_UI_Viewer.ts:176-196 |
pointerdown에서 마커 히트 시 즉시 selectObject 후 종료. 드래그 이동 로직 없음 |
공통 설계: SVG 동적 크기 렌더 (사용자 합의 완료)
B06_wf3_ProfileCross_UI_Section_View.ts의 LONG_WIDTH/CROSS_WIDTH/LONG_HEIGHT/CROSS_HEIGHT 고정 상수를 렌더 시점 파라미터로 전환한다.
createLongitudinalProfile/createCrossSectionCard에widthPx/heightPx파라미터 추가 (미지정 시 기존 상수 폴백 → 기존 호출부 무변경 호환)- 공통 원칙 (합의): 도면 높이는 항상 고정. 종단도는 폭이 최소 폭 미만이 되면 가로 스크롤 (B05·B06 공통), 횡단도는 한눈에 봐야 하는 정보이므로 가로 스크롤 금지 — 컨테이너 폭에 맞춰 내용 축소
- 고정 높이 (합의): B05 종단 = 하단 패널 컨테이너 실측 높이에 맞춤(패널 250px 내부 채움), B06 종단 = 220px, B06 횡단 = 250px
- 종단도 최소 폭 산정 (합의: X축 측점 라벨 비교차 기준):
minWidth = PAD.left + PAD.right + 측점 수 × 라벨 슬롯 폭. 라벨 슬롯 폭은 최장 라벨(예: "12+7.5", 10px 폰트) 실폭 + 여유로 산정 (구현 시 대략 48~56px 상수화, 검증 단계에서 실화면 확인) - 컨테이너 크기 실측은
ResizeObserver로 감지하고 디바운스(~150ms) 후 재도면 - 생성 시점 보장: 최초 데이터 바인딩·패널 펼침 직후는 레이아웃 미확정으로 폭이 0일 수 있음 → 폭이 0이면 도면 생략하고,
requestAnimationFrame+ResizeObserver 통지로 실측 폭 확정 후 도면 (B05 접힘 패널display:none상태 포함)
작업 1: B05 하단 종단도 — 높이 고정 + 하단 가로 스크롤
방향: 도면 폭 = max(컨테이너 폭 - 30px(좌우 15px), 최소 도면 폭). 컨테이너가 좁으면 최소 폭을 유지한 채 wrapper에 overflow-x:auto로 하단 스크롤바 생성. 넓으면 좌우 15px 여유만 남기고 전체 사용.
- 최소 도면 폭: 공통 설계의 라벨 비교차 기준 동적 산정
- 도면 높이: 하단 패널 컨테이너(
__body) 실측 높이에 맞춤 (스크롤바 발생 시 스크롤바 높이만큼 감산)
대상 파일 및 체크리스트
B06_wf3_ProfileCross_UI_Section_View.ts:createLongitudinalProfile에widthPx/heightPx파라미터 추가, 내부 좌표계(x 스케일·grid·축라벨)를 파라미터 기반으로 전환, 라벨 비교차 최소 폭 산정 유틸 추가B05_wf2_Route_UI_Profile_Panel.ts: ResizeObserver 부착, 실측 폭·높이 기반draw()재호출, 접힘→펼침 시 재도면, dispose 시 observer 해제B05_wf2_Route_UI_Style.css: 68-73행의width/height:100%강제 제거,__body에overflow-x:auto+ 좌우 padding 15px
작업 2: 배치된 포인트(BP/EP/CP/AP/FP) 직접 드래그 이동 — 0_old I-401 이식
참조 원본: 0_old/frontend/src/RouteDesignStage.tsx:868-1012, 2097-2111
이식할 판별 패턴 (클릭/드래그 공존의 핵심):
pointerdown: 마커 히트 시 즉시 선택하지 않고 드래그 후보로만 등록 (dragStart좌표 기록)pointermove: 이동 거리 3px 초과 시 드래그 확정 →OrbitControls.enabled = false, 지형 레이캐스트 지점으로 마커 메시 실시간 이동 (AP/FP는 반경 실린더 동반 이동)pointerup: 드래그였으면 최종 지형 좌표로 상태 확정·저장 + OrbitControls 복원. 이동 거리 3px 이하면 기존 클릭(선택/팝오버) 동작 수행pointerleave/pointercancel: 드래그 중 이탈 시 마지막 유효 좌표로 안전 종료 (원본 2097행 패턴)
대상 파일 및 체크리스트
B05_wf2_Route_UI_Viewer.ts: pointerdown 즉시 선택 로직(176-196행)을 3-ref(dragStart/dragCandidate/draggingMarker) 패턴으로 교체, pointermove/up/leave 핸들러 추가B05_wf2_Route_UI_Markers.ts: 히트된 마커 객체→(kind, index) 식별 및 지정 마커 좌표 갱신 API 노출 (AP/FP 실린더 동기 이동 포함)B05_wf2_Route_UI_Page.ts: 드래그 확정 시 포인트 상태 갱신 → 기존 "재탐색 필요(stale)" 배지·확정 버튼 비활성 게이트와 연동 확인 (Epsilon 오차 보정 로직 통과 확인)- 기존 팔레트 드래그&드롭(신규 배치) 및 클릭 선택·팝오버 동작 회귀 없음 확인
작업 3: B06 종단도 — 높이 고정 + sticky 상단 고정 복구
방향:
- 높이·폭: 작업 1의 동적 크기 렌더를 B06
createSectionView에도 적용. 높이 220px 고정, 폭은 컨테이너 전체 사용하되 최소 폭(라벨 비교차 기준) 미만이면 B05와 동일하게 가로 스크롤 (.b06-section__chart-wrap의 기존overflow-x:auto활용) - sticky: B06 페이지 스코프에서
.ui-workflow-layout__main에height: calc(100vh - var(--spacing-64))(+dvh)를 지정해 __main을 실제 스크롤 컨테이너로 전환 → 기존position:sticky; top:0이 정상 동작 (B05가 이미 쓰는 페이지 스코프 고정 패턴과 동일, 공통 템플릿은 수정하지 않음)
대상 파일 및 체크리스트
B06_wf3_ProfileCross_UI_Section_View.ts:createSectionView의draw()에 ResizeObserver 기반 실측 폭 전달B06_wf3_ProfileCross_UI_Page.ts: 레이아웃 루트에 B06 스코프 클래스 부여 (예:b06-profile-layout)B06_wf3_ProfileCross_UI_Style.css: B06 스코프의 __main 높이 고정 CSS 추가,.b06-section__chart의min-width:520px제거(횡단은 작업 4에서 별도 처리)- 검증: 횡단 리스트 스크롤 시 종단도가 상단에 고정 유지되는지, 브라우저 폭 변경 시 종단도 높이 220px 불변인지
작업 4: B06 횡단도 — 크기 안정화 및 오버플로우 해소 (좌우 스크롤 금지)
방향: 횡단 카드는 높이 250px 고정(합의: 기존 260 → 250) + 카드 실측 폭 기반 동적 폭 렌더. 한눈에 봐야 하는 정보이므로 가로 스크롤 없이 컨테이너 폭에 맞춰 내용을 축소. 열 수는 뷰포트가 아닌 컨테이너 폭 기준으로 전환해 오버플로우 자체를 제거.
- grid:
repeat(2, minmax(0,1fr))→repeat(auto-fill, minmax(480px, 1fr))(컨테이너 폭 < 480×2 + gap이면 자동 1열 전환, media query 의존 제거) - SVG
min-width제거로 카드 밖 오버플로우 원천 차단
대상 파일 및 체크리스트
B06_wf3_ProfileCross_UI_Section_View.ts:createCrossSectionCard에widthPx/heightPx(250)적용 (grid 열 폭 실측 1회 → 전체 카드 공통 적용, 카드별 관찰 금지)B06_wf3_ProfileCross_UI_Style.css: gridauto-fill전환, 카드 SVG 높이 250px 고정- 검증: 사이드바 열림/닫힘 및 브라우저 폭 축소 시 오버플로우·가로 스크롤 미발생, 카드 높이 불변
공통 마무리
prettier(TS/CSS) 포맷 실행, 700줄 제한 확인 (Section_View 현재 533줄 → 파라미터화 후 여유 확인)- 검증자 인계: 4건 통합 검증보고서 작성 대상
✅ 사용자 합의 사항 (2026-07-19 문답 확정)
- 스크롤 정책: 종단도(B05·B06 공통)는 높이 고정 + 폭이 일정 수준 이하로 줄면 가로 스크롤. 횡단도는 가로 스크롤 금지, 컨테이너 폭에 맞춰 내용 축소(높이 고정).
- 종단도 최소 폭: X축 측점 라벨이 서로 교차하지 않는 기준으로 AI가 산정 (라벨 실폭 + 여유 기반).
- 고정 높이: B05 종단 = 하단 패널 컨테이너에 맞춤, B06 종단 = 220px, B06 횡단 = 250px.