Files
Aislo/docs/raw/plans/2026-07-19_plan_responsive_layout_drag.md
T
eomsangdonandClaude Opus 5 eb30b774f8 chore(docs): docs 폴더 git 추적 전환 · PLAN·OWNERS 최상위 이관
- `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>
2026-09-09 18:19:07 +09:00

9.5 KiB
Raw Blame History

PLAN: B05/B06 종·횡단도 반응형 레이아웃 및 마커 직접 드래그 (완료 이력)

🎯 활성 계획: B05/B06 종·횡단도 반응형 레이아웃 및 마커 직접 드래그 (2026-07-19)

배경 (사용자 요청 4건)

  1. B05 하단 종단도가 브라우저 폭 축소 시 높이(도면 크기)부터 줄어듦 → 하단 가로 스크롤바 방식으로 변경
  2. 배치된 포인트를 3D 뷰에서 직접 드래그로 위치 변경 (0_old I-401 기능 이식, 클릭/드래그 판별 필요)
  3. B06 종단도가 폭에 따라 높이가 계속 변함(가로세로비 고정) + sticky 상단 고정이 실제로 동작하지 않음
  4. 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__mainoverflow: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.tsLONG_WIDTH/CROSS_WIDTH/LONG_HEIGHT/CROSS_HEIGHT 고정 상수를 렌더 시점 파라미터로 전환한다.

  • createLongitudinalProfile / createCrossSectionCardwidthPx/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: createLongitudinalProfilewidthPx/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% 강제 제거, __bodyoverflow-x:auto + 좌우 padding 15px

작업 2: 배치된 포인트(BP/EP/CP/AP/FP) 직접 드래그 이동 — 0_old I-401 이식

참조 원본: 0_old/frontend/src/RouteDesignStage.tsx:868-1012, 2097-2111

이식할 판별 패턴 (클릭/드래그 공존의 핵심):

  1. pointerdown: 마커 히트 시 즉시 선택하지 않고 드래그 후보로만 등록 (dragStart 좌표 기록)
  2. pointermove: 이동 거리 3px 초과 시 드래그 확정 → OrbitControls.enabled = false, 지형 레이캐스트 지점으로 마커 메시 실시간 이동 (AP/FP는 반경 실린더 동반 이동)
  3. pointerup: 드래그였으면 최종 지형 좌표로 상태 확정·저장 + OrbitControls 복원. 이동 거리 3px 이하면 기존 클릭(선택/팝오버) 동작 수행
  4. 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__mainheight: calc(100vh - var(--spacing-64))(+dvh)를 지정해 __main을 실제 스크롤 컨테이너로 전환 → 기존 position:sticky; top:0이 정상 동작 (B05가 이미 쓰는 페이지 스코프 고정 패턴과 동일, 공통 템플릿은 수정하지 않음)

대상 파일 및 체크리스트

  • B06_wf3_ProfileCross_UI_Section_View.ts: createSectionViewdraw()에 ResizeObserver 기반 실측 폭 전달
  • B06_wf3_ProfileCross_UI_Page.ts: 레이아웃 루트에 B06 스코프 클래스 부여 (예: b06-profile-layout)
  • B06_wf3_ProfileCross_UI_Style.css: B06 스코프의 __main 높이 고정 CSS 추가, .b06-section__chartmin-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: createCrossSectionCardwidthPx/heightPx(250) 적용 (grid 열 폭 실측 1회 → 전체 카드 공통 적용, 카드별 관찰 금지)
  • B06_wf3_ProfileCross_UI_Style.css: grid auto-fill 전환, 카드 SVG 높이 250px 고정
  • 검증: 사이드바 열림/닫힘 및 브라우저 폭 축소 시 오버플로우·가로 스크롤 미발생, 카드 높이 불변

공통 마무리

  • prettier(TS/CSS) 포맷 실행, 700줄 제한 확인 (Section_View 현재 533줄 → 파라미터화 후 여유 확인)
  • 검증자 인계: 4건 통합 검증보고서 작성 대상

사용자 합의 사항 (2026-07-19 문답 확정)

  1. 스크롤 정책: 종단도(B05·B06 공통)는 높이 고정 + 폭이 일정 수준 이하로 줄면 가로 스크롤. 횡단도는 가로 스크롤 금지, 컨테이너 폭에 맞춰 내용 축소(높이 고정).
  2. 종단도 최소 폭: X축 측점 라벨이 서로 교차하지 않는 기준으로 AI가 산정 (라벨 실폭 + 여유 기반).
  3. 고정 높이: B05 종단 = 하단 패널 컨테이너에 맞춤, B06 종단 = 220px, B06 횡단 = 250px.