Files
Aislo/docs/raw/plans/2026-07-24_plan_B05_irregular_stations.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

13 KiB
Raw Blame History

완료 계획서: B05 비정규 측점 테스트 반영 및 후속 작업 (2026-07-24 이관)

1. B05 비정규 측점 테스트 반영 (2026-07-24)

측점 1+0에 곡선, 1+10에 경사 변경 테스트 중 발견. 정적 검사 통과(tsc/prettier).

  • 구배 블록 값 안 보임 → 좁으면 90° 회전 표기: 변화점 추가로 구간이 좁아지면 구배(L/H/S) 값이 가로로 안 들어가 비었음. 이제 값을 항상 표기하되, 가로로 안 맞으면 90도 회전해 블록 폭·행 높이에 맞춰 글자를 줄여 넣음(작아도 표기 우선). → _UI_Profile_Table.ts buildSegmentRows(값 span+is-rotated, rowHeight 전달), _UI_Style.css.
  • 비정규 측점 값 열에 구배/곡선 표기: 선택 시 값 열 오버레이의 구배(L/H/S)·곡선(L/R) 칸을 채움 — 구간 이면 그 구간 값을 그대로(중복 허용), 변화점이라 좌우로 갈리면 좌 / 우로 절반 나눠 가로 표기. 곡선도 동일(해당 chainage 곡선의 L/R). → _UI_Profile_Table.ts buildIrregularColumn, _UI_Style.css (셀 overflow:hidden).

2. B05 후속 — 편집값 세션 유지 · 복원 · Y축 sticky · 비정규 측점 편집

2026-07-24 기획→승인→구현. 정적 검사 통과(tsc --noEmit 기존 B07 무관 오류 제외, prettier). 실제 앱 E2E 미실시. 사용자 결정: 작업1(+작업2 복원) 포함 · 작업3(Y축 sticky, 저위험) 포함 · 작업4·5·6 추가. 아래 체크리스트 모두 구현됨.

구현 요약(변경 파일):

  • 작업1(편집 이월): _UI_Profile_Panel.ts render() — 경로 변경 시 store.edited()면 현재 편집을 새 스토어로 이월(store.replace(carried))해 새 base에 재적용. best-effort(미매칭·범위 밖 드롭).
  • 작업2a(복귀 복원): _UI_Page.ts restoreSections — 클라이언트 목록이 빈 경우만 detail.longitudinal.stationskind==="irregular"(+structure)로 사이드바 복원. _UI_IrregularStations.ts setStations() 신설. B06_..._Api_Fetch.ts SectionStation.structure? 추가.
  • 작업3(Y축 sticky): B06_..._UI_Longitudinal.ts onYAxis 콜백으로 눈금 공유. _UI_Profile_Panel.ts buildStickyYAxis(0크기 sticky 앵커+절대배치 축, 불투명 배경으로 값 누출 차단). _UI_Style.css.
  • 작업4(z-index): _UI_Style.css .b05-profile-table__irregular-col z-index 6→4(이름표 5 아래).
  • 작업5(비정규 편집 버튼): _UI_Profile_Edit.ts createEditOverlayirregularStations 추가 → ▲/▼(+원복) 렌더(규칙 측점과 동일 adjustStation 파이프라인). _UI_Profile_Panel.ts에서 전달.
  • 작업6(값 열 입력): _UI_Profile_Table.ts buildIrregularColumn 계획고 셀을 no-spin 입력으로 → onAdjustStation(chainage, targetplan). _UI_Style.css 입력 셀 pointer-events 복구.

상세 작업 체크리스트

작업 1 — 재탐색 시 "사용자 조작값" 유지 (프론트 한정, 백엔드는 확정 때만)

  • 문제: 브라우저 새로고침은 결과가 남지만, 경로 변경·재탐색(재계산) 시 사용자가 조작한 계획선 편집(측점 계획고 델타·종단곡선 반경)이 리셋됨. 사용자는 결과가 아니라 조작값이 이어져, 경로가 미세하게 바뀌어도 대부분 재계산되며 편집이 유지되길 원함. 백엔드로는 안 보냄 (확정 시에만 전송).
  • 원인(조사): 프로필 편집 스토어가 routeId로 키됨. B05_wf2_Route_UI_Profile_Panel.ts render()if (nextRouteId !== routeId || !store.dirty()) 분기에서, 재탐색으로 새 routeId가 오면 스토어를 stored?.edits(신규 경로엔 빈 값)로 재생성 → 편집 소실. (비정규 측점은 Page 클라이언트 상태라 재탐색 후에도 유지되므로 이번 대상 아님.)
  • 핵심 근거: 편집은 chainage 키(station_offsets[chainage], curve_radii[chainage])라, buildAlignment( base, edits)새 경로의 base에 그대로 재적용 가능. 즉 스토어만 안 비우면 이어짐.
  • 구현 체크리스트:
    • render()에서 경로 변경 시 현재 편집을 새 스토어로 이월: 재탐색으로 routeId가 바뀔 때 store.edited()store.edits()(현재 in-memory 편집)를 새 스토어의 초기값으로 넘기고, 없을 때만 stored?.edits(백엔드 저장분/신규 빈 값) 사용.
    • 이월된 편집을 새 routeId의 세션 초안(sessionStorage)에도 기록해 재탐색 후 새로고침에도 유지.
    • 새 alignment에 없는 chainage의 편집 처리 확인: buildAlignment가 미매칭 키를 무시하는지 점검, 필요 시 매칭 chainage로만 프루닝(경로 대폭 변경 시 오적용 방지).
    • 비정규 측점은 현행 유지 확인(재탐색 후 setIrregularStations 재적용) — 회귀만 점검.
  • 문제(한계) — 사용자 질문 답변: 편집을 이어붙이는 방식은 chainage 키 기준 best-effort라 완벽하진 않다.
    1. 측점 계획고 델타(station_offsets): 규칙 격자(20·40·60…)에 걸리므로 노선이 조금 바뀌어도 격자 chainage는 대부분 그대로 → 잘 유지됨. 단 노선이 짧아지면 범위 밖(끝단 너머) 편집은 드롭 (resolvePvi가 노선 범위 밖 offset을 버림).
    2. 종단곡선 반경(curve_radii): 키가 자동 변화점(PVI) chainage인데, 재탐색 시 지반 형상이 바뀌면 자동 변화점 위치가 달라질 수 있음 → 옛 변화점 chainage에 맞는 변화점이 새 선형에 없으면 그 반경 편집은 적용 안 됨(무시). 즉 계획고 편집보다 반경 편집이 더 취약.
    3. 의미 드리프트: 노선이 크게 달라지면 같은 offset이 새 지반에선 어색한 결과를 낼 수 있음(그래도 값은 유지). → 결론: “미세 변경 시 이어짐”은 확실히 충족. 대폭 변경 시엔 일부(특히 반경) 편집이 드롭될 수 있고, 이는 chainage 키 방식의 근본 한계라 완전 해결하려면 편집의 의미(어느 변화점/구간인지) 추적이 필요(과대). 이번엔 best-effort로 진행하고, 드롭된 편집은 조용히 버린다(사용자 의도에 부합).

작업 2 — [검토] 확정 시 저장 위치 정리 + 다음 페이지 이동/복귀 흐름

  • 성격: 코드 변경 전 현황 검토·결정. 조사 결과:
    • 확정 시 저장(현행): ① profilePanel.save()→PUT /route/profile-alignment→편집을 longitudinal.json에 반영 + grade_summary를 DB(longitudinal_sections.data). ② confirmRoute(...)→비정규 측점 횡단 파일 생성 + longitudinal.json stations 병합(파일), 경로 상태 CONFIRMED(DB). ③ 편집 세션 초안은 markSaved()로 삭제.
    • 편집 중 프론트 상태: 계획선 편집 = sessionStorage 초안(routeId) + in-memory. 비정규 측점 = in-memory(Page+패널)뿐(확정 전 미영속).
    • 복귀 시(현행): fetchLatestRoute+restoreSections로 편집(=longitudinal.json)·백엔드 비정규 측점은 그래프에 복원됨. 그러나 사이드바 비정규 목록은 미복원(클라이언트 상태 소실 → 편집/삭제 불가).
  • 검토 결과 / 계획:
    • (a) 포함(사용자 승인) — 복귀 시 사이드바 비정규 목록을 백엔드에서 복원: detail.longitudinal. stationskind==="irregular"(+structure)를 읽어 사이드바 리스트·Page irregularStations 재구성. 구현 체크리스트:
      • restoreSections(또는 render 후)에서 detail의 irregular 측점을 추출 → 사이드바 목록 API로 주입 (신규 panel.irregularStations.setStations(...) 또는 유사) → applyIrregularStations로 그래프·3D 반영.
      • 측점번호/잔여거리 역산: chainage → (측점번호, 잔여거리)는 측점간격으로 환산.
    • (b) [백로그 유지] 확정 시 비정규 측점의 DB cross_sections 행 미기입(B07/B08용) — 표시(B06)는 파일 기반이라 정상. 이번 범위 제외, 후속 판단.
    • (c) 문서화: 확정 데이터 흐름표(무엇이 DB/파일/프론트 어디에)를 완료 후 검증보고서로 남김.

작업 3 — [가능성 검토 포함] 하단 패널 그래프 Y축 스크롤 고정(sticky)

  • 목표: 가로 스크롤 시 그래프 좌측 Y축(표고 눈금·축선) 고정. 단, 예전 테이블 좌측 여백처럼 스크롤되는 값이 새어 보이면 안 됨(불투명 배경으로 가림).
  • 조사/설계: 그래프는 스크롤 컨테이너 안 단일 SVG라 SVG 자체를 sticky 불가. → .b05-profile__chart 안에 별도 sticky 오버레이 div(position: sticky; left: 0, 폭=LONG_PAD.left, 불투명 배경, 상위 z-index)를 두고 표고 눈금 라벨+축선을 그린다. 스크롤 SVG의 좌측 눈금은 이 오버레이가 덮어 가림.
  • 가능성 판정(사용자 조건: 버튼 중첩·성능 문제면 하지 말 것):
    • 편집 버튼은 상/하단(중앙 측점 위)에 있고 Y축은 좌측(0~62px), 0측점 버튼은 ≈90px라 중첩 거의 없음.
    • 성능: 정적 오버레이라 부담 없음.
    • 주요 난점: 오버레이 눈금이 SVG 격자선과 같은 Y-스케일이어야 함 → createLongitudinalProfile의 Y-스케일/눈금 계산을 오버레이가 공유하도록 노출/전달 필요(중복 렌더 소지).
    • 판정: 구현 가능·저위험 → 포함(사용자 승인). 다만 Y-스케일 공유(중복 렌더) 정리가 필요.
  • 구현 체크리스트:
    • createLongitudinalProfile가 Y-스케일(또는 눈금 배열)을 반환하거나, 패널이 동일 스케일로 눈금 계산.
    • .b05-profile__chart에 sticky Y축 오버레이(불투명 배경·축선·표고 라벨) 추가, z-index로 값 누출 차단.
    • 리사이즈·폭맞춤 시 눈금 위치 재동기화 확인.

작업 4 — [버그] 비정규 측점 값 열이 행 제목(sticky 이름표)을 가림 (사용자 3번)

  • 문제: 지난 세션 ⑤에서 만든 비정규 측점 값 열 오버레이의 z-index(6)가 sticky 행 이름표(z-index 5)보다 높아, 가로 스크롤로 값 열이 이름표 열 위로 오면 행 제목이 가려짐.
  • 계획:
    • .b05-profile-table__irregular-col z-index를 이름표(5) 아래(예: 4)로 낮춘다. 규칙 값 셀(0)·곡선(2) 보다는 위라 값 열은 정상 표시되고, 좌측 이름표만 항상 위에 남는다.
    • 스크롤로 값 열이 이름표 영역까지 왔을 때 이름표가 안 가려지는지 확인.

작업 5 — [기능] 비정규 측점도 상/하·시프트 상/하 편집 버튼으로 제어 (사용자 4번)

  • 근거(조사): adjustStation(base, edits, chainageM, delta) + resolvePvi노선 범위 내 임의 chainagestation_offset으로 받아 변화점(PVI)으로 승격시킨다 → 비정규 측점의 계획고도 규칙 측점과 똑같이 버튼으로 조정 가능.
  • 계획:
    • createEditOverlay비정규 측점 목록을 전달하고, 각 비정규 측점 x에 측점 ▲/▼(+원복 ↺) 버튼을 렌더 → adjustStation(base, edits, irregular.chainage_m, ±step) 호출(규칙 측점과 동일 파이프라인).
    • 시프트(구간 ⇧/⇩)는 현행처럼 구간 단위 유지(비정규 측점은 구간 내부에 놓임) — 별도 버튼 불필요, 다만 비정규 측점이 변화점이 되면 그 지점에서 구간이 나뉘는 것을 확인.
    • 편집 오버레이 버튼과 값 열(작업4)·측점 라벨이 겹치지 않게 배치 점검.

작업 6 — [제안] 테이블에서 비정규 측점 값 직접 입력 (사용자 5번, 작업5 연계)

  • 목표: 버튼(작업5)과 별개로, 테이블 레이아웃을 깨지 않는 범위에서 비정규 측점의 계획고를 직접 입력.
  • 제안(권장안): 작업4의 값 열 오버레이 안(테이블 실제 행이 아니라 오버레이라 레이아웃 영향 없음)의 계획고 셀을 숫자 입력으로 바꾼다. 입력 시 현재 계획고 → 목표 계획고 차이를 delta로 계산해 adjustStation(base, edits, chainage, delta) 호출 → 규칙 측점과 동일하게 station_offset으로 반영. 스피너는 제거(no-spin). (곡선이 생기는 변화점이면 곡선 R 셀도 입력 가능하게 확장 여지.)
    • 장점: 오버레이는 테이블 위에 떠 있어 실제 12행 셀 구조를 안 건드림 → 레이아웃 안전. 값 입력·버튼이 같은 station_offset 파이프라인이라 일관.
    • 선택된 비정규 측점의 값 열 계획고 셀 → <input type=number no-spin>, change 시 adjustStation.
    • 입력 폭/정렬이 오버레이 yr 폭 안에 들어오는지(레이아웃 불변) 확인.