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

44 KiB
Raw Blame History

완료 항목 이관 (3) — 2026-09-07 밤

무엇 — 상시계획서 docs/raw/PLAN.md 에서 끝난 것을 옮긴 세 번째 묶음. 같은 날 낮·저녁에 두 번 옮겼고(…_completed_items.md · …_completed_items_2.md), 그 뒤 하루가 더 쌓여 2,425줄이 되어 한 번 더 갈랐음.

B08(8-x)·B09(9-x)는 손대지 않았음 — 그 명세는 지금 바로 착수하는 작업의 설계도라 계획서에 그대로 있음(2026-09-07 사용자 지시).

원문 그대로 — 본문은 한 글자도 고치지 않았음. 자리만 옮겼음. 절 번호도 계획서 것 그대로 — 남은 절들이 이 번호로 서로를 가리킴.

어느 절에서 왔나 — 0-11(절째) · 0-8 · 0-2 · 0-7 · 3-9. 그 절들에는 이 파일을 가리키는 줄이 붙어 있음(0-11 은 절 자체가 없어졌음).


### 0-11 (절째 — 종료)

0-11. 노선 [확인] 속도 — 293초 → 약 90초로 줄이고 종료 (2026-09-07 사용자 판단)

0-10 에서 재확정 체인이 끝까지 돌게 고쳤더니 208~221초가 나옴. 기능은 맞지만 속도개선(0-8)으로 얻은 인상을 이 한 조작이 통째로 깎아먹음. 먼저 어디서 쓰는지부터 잼.

단계 비중
준비·사용자입력 회수 0.3 0%
노선 재계산(as_planned) 1.9 1%
B05 확정 0.4 0%
배수·관·측점 재생성 286.8 98%
배수유역 분석 285.0 99%(그 안에서)
├ 관 지점 확정 0.3 0%
└ 종횡단 측점 재생성 1.5 1%
설계 이월 0.7 0%
B06 확정(서버 재계산 포함) 2.8 1%
합계 292.9

속도개선(0-8) 코드는 무죄 — 노선 재계산 1.9초, B06 확정 2.8초로 전부 초 단위임. 배수유역 하나만 285초. 그 안쪽 엔진 로그는 단계 분석 104.3s (확장 9회, 최종 셀 1,352,325개) 이라 분석 104초 + 나머지 181초(페이로드 조립·파일 쓰기) 로 또 갈림.

단계 비중
준비(도엽 읽기·좌표변환) 5.8 1%
preview_stages 467.4 99%
├ 그중 resolve_primary_region(1차 영역) ≈360 76%
└ 그 뒤 확장·흐름·강도 107.6 23%
페이로드 조립 + 파일 저장 전부 1.0 0%
합계 474.4

저장·페이로드는 무죄(1초) — 「파일 쓰기가 느리다」던 첫 추정은 틀렸음. 같은 계산을 보조 창 회차(285s 총·104.3s 분석)에 대입해도 1차 영역이 약 175초로 최대 조각임. ⚠ 계측 마크는 보조 창 커밋 4da04929 것을 씀(내가 넣던 것은 버리고 그쪽으로 합침).

단계
노선 재계산(as_planned) 1.9 2.1
B05 확정 0.4 0.3
배수유역 분석 285.0 111.1
_prepare(도엽 읽기) 4.9
├ 1차 영역 ≈256 2.6
격자 확장·흐름 판정 ≈104 102.0
└ 응답·저장 1.0 1.2
측점 재생성 1.5 1.7
설계 이월 0.7 0.8
B06 확정 2.8 2.5
합계 292.9 119.5

결과 동일 확인 — 같은 노선으로 새 코드 두 회차가 완전히 일치: 관 11개(기본 1 + 보충 10) · 측점 63개 · 배수관 측점 8개의 누가거리가 소수 둘째 자리까지 일치 (82.03 · 246.73 · 421.88 · 514.19 · 691.82 · 770.14 · 868.45 · 945.76) · 세류 조각 1,263 → 상류망 1 / 하류망 109 / 미연결 1,153 · 확장 9회 · 최종 셀 1,352,325. (옛 회차와의 직접 대조는 입력 노선이 달라 못 씀 — 그래서 같은 노선 두 회차로 확인함.)

  • 여기서 종료 — 2026-09-07 사용자 판단(「충분히 빨라. 필요시 나중에 다시 하겠음」). [확인] 이 293초 → 약 90초가 된 상태로 닫음. 더 줄이려던 갈래 셋(① 확장 재사용 · ② classify_flow 뿌리 찾기 · ③ 유역을 배경으로 미루기)은 설계·실측이 끝나 있어 지우지 않고 보관docs/raw/plans/2026-09-07_speed_watershed_shelved.md. 다시 꺼낼 조건 = 사용자가 [확인] 이 느리다고 말할 때.

이 절의 완료분은 아카이브로 옮겼음 — docs/raw/plans/2026-09-07_plan_completed_items_2.md.


### 0-7 노선 변경 — 당시 현황·지적된 구멍과 답

노선을 바꾸는 길은 지금 넷 (코드 확인, 2026-09-06)

지금 동작 초기값
파일 다시 올리기(B03) 옛 계산 결과 전부 지우고 체인 다시 돌림 버리고 다시 뜸
지표면 바꿔 재확정 사용자 입력 유지한 채 다시 계산 버리고 안 뜸 — [초기화]가 409 로 거절
최적 경로 계산 새 노선 행을 더함(옛 노선 남음) 그대로 — 첫 노선 기준
0-2 계획노선 편집 아직 없음

지적된 구멍과 그 답 (실제 확인 → 사용자 확정)

  • 구조물·관 정본이 프로젝트에 한 벌structures.json·pipe_points.json 은 노선별이 아니라 프로젝트별이라, 노선이 바뀌면 옛 누가거리에 찍힌 구조물이 엉뚱한 자리에 섰음. → 새 노선으로 자리만 옮겨 붙이고 맞추는 것은 사용자 (확정 6).
  • 프로젝트 범위 세션 초안이 안 갈림structures·uphill·std-cross 는 프로젝트 범위라 노선이 바뀌어도 살아남아 새 노선에 얹혔음(노선 범위 초안 11개는 저절로 갈림). → 노선 변경 때 비움.
  • [초기화]의 뜻 — 지금은 노선 행을 전부 지우고 첫 스냅샷을 되돌림. → 예상노선(원본)을 계획노선(수정본)에 복사하는 것이 노선 초기화임 (확정 4).
  • 초기값을 다시 뜰 자리가 없음 — 다시 뜰 필요가 없음. 원본은 예상노선이고 안 바뀜; 계산은 계획노선 기준으로 다시 돌 뿐임 (확정 2·5).

### 0-7 노선 변경 — 「지금 있는 것 / 없는 것」(당시 기준, 지금은 다 있음)

지금 있는 것 / 없는 것 (코드 확인)

  • 있음 — 원본 자리 initial_snapshot/planned_route.csv(자동 체인이 씀) + 수정본 자리 B05_Profile/route/route_main.geojson + DB routes·route_points. 두 벌 구조는 이미 섬.
  • 없음 — ① 계획노선을 고칠 길(0-2 모달) ② 노선이 바뀐 뒤 배수유역부터 다시 도는 경로 ③ 구조물·배수관을 새 노선으로 옮기는 처리 ④ 두 노선을 함께 그리는 화면 ⑤ 프로젝트 범위 세션 초안(structures·uphill·std-cross)을 노선 변경 때 비우는 자리.

### 0-8 속도 개선 — 목표·결과·고친 것·안 하기로 한 것·재발 방지

이 절 하나가 속도 작업의 유일한 기록임. 옛 0-8(응답 크기·서버 낭비) / 0-9(조작 반응 속도)로 나눠 적던 것을 합쳤음 — 두 절이 같은 목표를 좇으며 서로 다른 회차의 숫자를 들고 있어 나중에 읽으면 어느 것이 지금 코드인지 알 수 없었음.

목표 (사용자 확정)

「반응 시작 0.1초」와 「결과 도착」을 나눔. 끌면서 보는 값은 0.1초 안에 결과까지, 놓고 나서 보는 값은 반응만 즉시면 되고 결과는 1초. [저장]·[확정]처럼 기다리는 조작은 진행 표시가 있으면 수 초도 허용.

결과 — 무엇이 얼마나 빨라졌나

화면 조작 잰 곳
계획고 ▲ → 누가토량 라벨 130ms 85.5ms 공용 브라우저(헤드풀)
B05 화면 진입 14,708ms 726~1,269ms 공용 브라우저
B06 화면 진입 1,028ms 885ms (편차 294→49ms) 보조 창(격리 프로젝트, 3회 중앙)
[저장] 버튼 전체 대기 4,137ms 1,826~2,979ms 공용 브라우저
route/confirm 470~834ms 324~356ms
관 옮긴 뒤 유역 다시 나누기 907ms 187ms 보조 창
B05 첫 진입 수신량 118MB 11.9MB (dev) 보조 창
등고선 도엽 전송 65.9MB 11.3MB
종횡단 상세 전송 1.56MB 0.32MB
모든 요청에 붙던 세션 확인 34.4ms 13.6ms

배포 빌드 전체는 1.90MB · gzip 0.71MB(dev 의 three.js 5.9MB 는 개발 서버 특성이라 고객이 받는 양이 아님).

고친 것 — 원인별

ⓐ 같은 일을 두 번 하던 것

  • 지표면을 화면에 들어올 때마다 다시 파싱 (B05_Profile_UI_Viewer.loadSurface) — B05 는 해시가 바뀔 때마다 renderB05Route 로 통째로 다시 조립돼 뷰어 상태가 비워짐. 8MB 지표면을 매번 읽고 파싱했음. 파싱본을 모듈 단위 cachedTerrain 에 한 벌 쥠 (다른 모델을 부르면 옛것을 버림, 장면에서 뗄 때 disposeObject 안 함). 커밋 44ff1dea.
  • 카드 기하를 두 번 계산 (B06_Section_UI_Section_View 1차·2차) — 행 높이를 재는 1차와 실제 그리는 2차가 같은 측점의 crossPlotMetrics 를 각각 돌렸음(3.2ms/장). crossPlotBase(강제 높이 전) + crossPlotFromBase(높이만 얹기)로 갈라 1차 결과를 2차에 넘김. 1차 반폭도 카드가 실제로 쓰는 값(effectiveCardHalfWidth)으로 맞춰 카드·행 높이 기준이 어긋나 있던 것도 함께 고침. 커밋 4df5d0a7.
  • 유토곡선을 편집당 두 번 적분reportSummarydraw 가 각각 돌았음. 펼침일 때는 한 번만 내고 그 결과에서 총괄값을 꺼내며, 접힘이면 횡단 기준만 냄 (computeMassHaulSeries 에 기준 인자 추가). 커밋 1bb36705.

ⓑ 안 써도 될 것을 만들거나 받던 것

  • 3D 저장본 18.6MB 를 받아놓고 버림 — 열쇠(해시)에 종횡단 내용이 섞여 있어 계획고를 한 번이라도 만졌으면 다 받은 뒤 버리고 브라우저가 다시 만들었음. 서버 GET .../corridor?hash= 를 받아 저장본 머리 4KB 에서 열쇠만 떼어 맞춰 보고, 어긋나면 파일 대신 {"status":"stale"} 만 냄. 화면 진입은 loadCorridorIfFresh맞을 때만 올리고 낡았으면 [3D 업데이트] 대기 표시만 켬(버튼 경로는 종전대로 재생성). 커밋 59df765e.
  • 지형 표고를 점마다 레이캐스팅 (terrainElevation) — 한 번 쏠 때마다 삼각형 전수 훑기. 마커·측점선이 점마다 부르는 자리라 곱해져 CDP 프로파일에서 13,537ms 중 11.6초였음. 이미 있던 TerrainHeightIndex(격자 색인)로 돌리고 색인도 지형과 한 벌로 보관. 커밋 0b30f5b0.
  • 접힌 유토곡선의 종단 기준 곡선 — 버려질 값을 냈음(위 ⓐ 마지막 항목).

ⓒ 원격 DB 왕복이 행 수만큼 나던 것 (DB 가 dsm.chemifactory.com — 왕복 하나 약 12ms)

  • 측점 설계 쓰기 — 측점마다 SELECT+UPDATE 두 왕복(22행이면 670ms). 새 파일 B06_Section_Repository_Bulk.merge_cross_section_designs 로 묶음 — 노선 측점을 한 번 읽고 UPDATE … SET data = CASE id … 한 문장. 행 수와 무관하게 왕복 두 번. 부르는 자리 셋: 편집 반영·기본설계·정본 재계산. 커밋 d650d705.
  • 측구 방향 역반영 (route/confirm) — 측점마다 종단 정본을 다시 열고(13.8ms) 행마다 썼음(24.5ms). asyncio.to_thread 한 번에 몰아 종단·포장표·세월교표를 한 번만 읽고 쓰기도 묶음(read_cross_design_inputspreloaded 인자 추가). 커밋 5967d64b.
  • 순차 읽기 묶기get_section_context·get_section_detail·배수유역 context 를 asyncio.gather 로. 공용 헬퍼는 config.config_db.run_with_connection(읽기 전용).
  • 요청마다 세션 활동시각 UPDATE+commitlast_activity_at 은 4시간 단위 유휴 판정에만 쓰이므로 1분에 한 번만 쓰게 함. 진입 한 번에 API 가 180번 나가는 자리라 곱해졌음.

ⓓ 화면이 레이아웃을 강제로 다시 재던 것

  • 카드 버튼줄 넘침 정리(reflow) — 한 칸 옮길 때마다 scrollWidth 를 다시 읽었고 (쓰기→읽기→쓰기), 카드마다 requestAnimationFrame 을 걸어 한 프레임에 67번 돌았음. 한 번의 강제 레이아웃이 그때까지 들어간 카드 전부를 다시 재 뒤로 갈수록 비쌌음. reset(쓰기)/measure(읽기)/apply(쓰기) 세 토막으로 갈라 모듈 단위 scheduleReflow 가 한 프레임에 몰아 돌림 — 강제 레이아웃 67회 → 1회. 커밋 1abb456e.

ⓔ 오가는 양

  • 응답 압축main.py 미들웨어(1KB 이상, /corridor 예외). ⚠ 강도는 1. 기본값 9 로 두었더니 등고선 36.8MB 를 매 요청 최대 강도로 눌러 한 요청이 9,410ms 였음(1 로 낮추니 739ms, 줄어드는 양은 거의 같음). 커밋 d0ba8ed8.
  • 좌표 자릿수 정리 — 저장 파일은 그대로 두고 내려보낼 때만. common_util_json.round_floats(value, digits) + LONLAT_DIGITS(7, 1.1cm) · METRE_DIGITS(6). 도엽 GeoJSON 은 자릿수만 줄인 표시용 사본(.display.geojson)을 한 번 만들어 서빙.
  • 디바운스 60 → 16ms (CROSS_PREVIEW_DEBOUNCE_MS) — 서버 왕복이 있던 시절의 값. 계산이 6ms 라 한 프레임이면 충분하고 연속 입력은 여전히 마지막 값만 계산함.

ⓕ 계산 자리 — 보안 때문에 옮긴 것 (속도와 반대 방향이지만 함께 결정됨)

  • [저장]·[확정] 정본을 서버가 다시 계산 — 편집을 커밋한 recompute_server_side 를 부름. run_node 갈래를 없애 부르는 자리 둘(파일입력 체인·저장)이 모두 Node 를 돎. 커밋 3cd717d8.
  • 유토 배분을 서버로POST /projects/{id}/sections/{route_id}/haul-plan (B06_Section_Router_HaulPlan.py). 브라우저가 낸 누가토량 결과를 받아 배분만 돌려줌. 화면이 쓰는 꼴 그대로 내보내 그리기 코드를 안 건드림. 화면 쪽은 B05_Profile_Api_HaulPlan.createHaulPlanPrefetch 가 편집이 멈추면(400ms) 조용히 받아 둠. 선행 걸림돌이던 massHaulPayloadhaulPlanPayload 값 import 를 끊음(extra 인자). 커밋 c0bfcba3 · 버그 수정 c8468e78.

검토 끝에 안 하기로 한 것 (다시 꺼내지 말 것)

  • 계산을 전부 서버로 — 실제로 돌려 보니 종단 높이 변경 313ms(중앙 413), 표준단면 330ms. 응답은 gzip 3KB 까지 줄었는데도 파이썬 계산 자체가 벽(브라우저 6ms 의 약 50배, 그중 헛일은 31ms 뿐이라 캐시로 못 줄임). 조작 경로는 브라우저에 둠(0-5 규칙과 같음).
  • 배수관 계산 2건(세부유역 자르기·관경 제안) TS 짝 — 0.19초면 「놓고 나서 보는 값」 합격이고, 격자 npz 3.1MB 를 브라우저로 보낼 값이 그만큼 크지 않음.
  • 종횡단 상세에 자릿수 걸기 — 지반선이 브라우저 계산 입력이라 반올림하면 파이썬 짝과 면적이 갈림. 압축만으로 1.56 → 0.32MB 이고 자릿수까지 걸어야 0.27MB 라 위험이 큼. round_floatsB04 계통에서만 씀.
  • B06 횡단 카드 가상 스크롤 — 카드 한 장 조립이 2.9ms(판단선 20ms 의 1/7). 보조 창(GPU 없음)에서 27초로 보였던 것은 소프트웨어 렌더링 특성이었음.
  • DocumentFragment 로 묶기grid 가 이미 떼어낸 노드이고 마지막에 replaceChildren 한 번이라 그 이득을 이미 얻고 있음.
  • Node 상주 프로세스 — Node 단독 8회 폭 16ms 로 매우 안정적. 기동값이 문제가 아님.
  • 난독화·소스맵·도메인 상수 서버 배달 — 배포 산출물에 소스맵 0개, 식별자 뭉개짐, 원본 함수명 검색 0건. 남은 상수는 조도계수·관경 규격표 같은 공개 표준값.
  • 지형 도엽 2건 중복 제거 — 중복이 아니었음. DRAINAGE_SHEET_LAYERS 는 등고선·하천중심선 두 레이어이고 IndexedDB 캐시(ETag)가 두 번째부터 안 받게 하고 있음.
  • 3D·계획노선 변경을 배경에서 조용히 — 도는 동안 브라우저가 통째로 바빠 「조용히」가 성립 안 함. 수동 유지([3D 업데이트] 버튼·모달 [확인]).

다음에 같은 일을 할 때 지킬 것 (재발 방지)

  1. 측정은 127.0.0.1 로. 파이썬 urlliblocalhost 를 부르면 IPv6(::1) 로 먼저 붙었다 떨어지며 약 2초가 붙음. 브라우저는 해당 없음.
  2. 배포 빌드에서 코드가 빠졌는지는 함수 이름으로 못 봄 — 식별자가 뭉개져 코드가 있어도 0건이 나옴. 속성 이름(payload 키)으로 볼 것.
  3. 창이 다르면 절대값을 비교하지 말 것. 보조 창은 GPU 가 없어 SVG·3D 시간이 부풀어 보임. 비율·차이로 보고, 같은 창 안에서 전후를 잼.
  4. 검증 프로젝트를 환경마다 나눌 것. storage 가 정션으로 한 벌이고 DB 도 한 벌이라, 두 창이 같은 프로젝트를 만지면 같은 파일·같은 행에서 부딪혀 전체 시간만 튐(부분은 전부 안정적인데 합계만 튀면 이것을 의심할 것).
  5. 한 번 잰 뒤 되돌릴 것. 검증으로 만진 계획고는 세션 초안에서 걷어내 원래 값으로 되돌림 (feedback_no_undo_during_verification 규칙 — undo 스택 대신 초안을 고칠 것).
  6. 코리도 캐시를 잴 때는 저장본이 최신인지 먼저 볼 것. 낡았으면 서버가 stale 만 주고 브라우저가 직접 만들어(__corridorSource.source === "built") 담을 것이 없다. 되살리려면 서버에서 prebuild_corridor(project_id, route_id, project_root) 를 부르면 된다(4.3초).
  7. 해시만 바꾸는 이동은 새로고침이 아니다 — 같은 문서라 프래그먼트 이동으로 끝난다. page.reload() 를 쓸 것. __corridorSource.at 이 그대로면 안 도는 것이다.
  8. B02 프로젝트 생성 API 는 /api/b02/project/api/projects/project 가 아니다.
  9. B03 업로드는 「칸별 배정」이다 — 화면이 파일칸을 유형별로 갈라 두고(csv·shx·dbf· cpg·route_prj·las_laz·prj·tfw·tif) 각 칸에 맞는 파일을 넣어야 [파일 업로드] 가 열린다. API 로 9개를 한 요청에 뭉쳐 보내면 화면이 하는 일과 달라 검사에서 막힌다. 자동화 선택자 — 칸은 input.b03-file__slot-input, 유형은 card.dataset.slotId, 한글 라벨은 .b03-file__card-labeltextContent(card.textContent 는 빈 문자열).
  10. 조정창은 합성 이벤트로 안 열린다dispatchEvent 로는 안 되고 Playwright click() (신뢰 이벤트)이어야 한다. 그리고 location.hashpage.evaluate 안에서 바꾸면 그 뒤 같은 스크립트의 locator 가 옛 화면을 본다 — 해시 변경과 조작을 다른 명령 파일로 나눌 것.
  11. CPU 프로파일은 CDP Profiler 로. PerformanceObserver 의 longtask 는 「몇 ms 짜리 블록이 있다」까지만 알려 주고 누가 범인인지는 못 알려 줌.

### 0-2 계획노선 편집 — 0-2-A 개선·뒷손질·자체검증

0-2-A. 편집 조작 개선 — 사용자 지적 6건 (2026-09-07 사용자 지시)

받은 지적 그대로 — ① 노드를 끌면 R 이 사라짐 ② R·직선을 고르기만 해도 전역 R 이 사라짐 ③ R 입력을 하단 행 말고 고른 요소 옆 라벨로 ④ 기본적으로 사용법을 모르겠음 ⑤ 줌·팬을 배수유역도와 동일하게 ⑥ 등고선을 노선 기준 ±300m 까지만, 창 크기 변화 주의.

①②의 뿌리는 하나 — 모달이 그리는 곡선은 서버가 만든 것뿐임. 그래서 손을 대는 순간 markEdited()plannedLine·curveInfo·nodeInfo 를 통째로 비우고, 화면은 곡선이 하나도 없는 꺾임점 직선으로 바뀜. 상태줄의 「곡선 22곳(하한 R 12m)」도 같이 사라짐. ⚠ 값이 지워지는 것은 아님curveRadius 는 그대로 남아 [확인] 때 반영됨. 화면만 「지워졌다」고 말하는 것이라 더 나쁨(사용자가 되돌릴 길을 찾게 됨). 사용자 추측(「한쪽만 옮겨 접선 거리가 달라져서?」)은 원인이 아님 — 다만 접선 자리가 줄면 서버가 R 을 눌러 줄이는 것은 사실(tangent > available → radius = available / half_tan). 그 눌림도 지금은 [확인] 뒤에야 보임.

  • A. 곡선을 브라우저가 즉시 그리게 — 서버 build_planned_polyline편집 갈래 (simplify=False + curve_flags/radii)만 TS 짝으로 냄. 필요한 것은 내각·원호 기하· 원호 점·접선 클램프뿐임(단순화·IP 추출은 초기 변환 전용이라 안 옮김). CLAUDE.md 「계산 자리」 ① 파이썬·TS 짝 — 두 파일 머리에 짝임을 적고 거울 시험을 남길 것. 기존 B05_Profile_UI_RouteEdit_Curve.ts(순수 기하)를 그 자리로 키움. → 노드를 끌면 그 곡선만 다시 그려지고 나머지 곡선은 그대로 남음. R 눌림도 즉시 숫자로 보임.

  • B. R 입력을 고른 요소 옆으로 — 하단 .b05-routeedit__curve 행을 걷고, 고른 꺾임점 화면 좌표 옆에 뜨는 작은 라벨 상자(R 입력 + 곡선 지우기/넣기 + 반지름 자동)로 바꿈. 캔버스 밖으로 나가지 않게 가장자리에서 접어 넣을 것.

  • C. 사용법이 보이게 — ⚠ 손잡이가 노드를 가리는 것이 큰 원인임: handleAt 이 11px 안에서 노드보다 먼저 잡혀, 헤어핀처럼 곡선이 몰린 데서는 노드를 아예 못 집음 (실화면: 격자 훑기로 처음 잡힌 것이 전부 손잡이였음). 노드를 우선으로 바꾸고 손잡이는 고른 곡선에서만 보이게 할 것. 조작 안내는 늘 보이게 두고, 오른쪽 클릭 메뉴는 공용 ui_template_context_menu 를 재사용(새로 만들지 말 것).

  • D. 줌·팬을 배수유역도와 동일하게B05_Profile_UI_Drainage_Interact.ts 기준. 휠 방향 당기면 확대(지금은 반대) · 계수 1.15/0.87(지금 1.2) · 상한 computeMaxScale(…, 16)(지금 고정 2000) · 하한 0.5(지금 1) · 팬은 가운데 버튼 전용 (지금은 왼쪽 배경 끌기 — 노드를 집으려다 지도가 딸려 움직임). ⚠ 커서 고정 계산이 지금 틀림affineOfcenterX*(1-scale) 을 품고 있는데 모달은 화면 좌상단 기준으로만 셈해 확대할수록 지점이 밀림. 배수유역도 식 (cursor*(1-ratio) + offset*ratio, cursor 는 중심 기준)으로 고칠 것.

  • E. 등고선을 노선 ±300m 로 — 도엽을 그리기 전에 노선 경계 + 300m 사각형으로 캔버스를 클립. 창 크기와 무관한 고정 띠라 창을 늘리거나 줄여도 안 깨짐(사용자 우려 지점). 화면 밖 걸러내기는 이미 drawPreparedLayer 가 하고 있으므로 띠만 얹으면 됨.

  • F. 되돌리기·다시하기·초기화(2026-09-07 사용자 추가 지시) — [확인]이 무거워 되돌릴 길이 없으므로 창 안에서 물릴 수 있어야 함. 사진(스냅샷) 목록으로 두되 끌기 한 번이 한 걸음임. [초기화] = 이 창을 연 상태로 (≠ [예상노선으로], 그쪽은 서버 재계산). 단축키 Ctrl+Z / Ctrl+Y · Ctrl+Shift+Z.

자체검증 (2026-09-07, 공용 브라우저 5174 · 용화 프로젝트 · 저장 안 함)

  • A — 노드를 끈 뒤 상태줄이 「곡선 22곳(하한 R 12m) · 기준 미달 2곳」 유지. 종전에는 「곡선 기준 R 12m — [확인] 때 반영」으로 바뀌며 곡선이 다 사라졌음. 거울 시험 10건(test_route_polyline_browser_mirror.py), 공식을 일부러 틀어 시험이 잡는 것도 확인.
  • B — 노드를 누르니 라벨이 그 오른쪽 [+18, 38]px 에 뜸. 「22번째 꺾임점 · R 12m · 값 지정 · 내각 169° · 법정 하한 12m」.
  • C — 손잡이는 고른 곡선에만 나옴. 노드를 먼저 잡으므로 헤어핀에서도 노드가 잡힘.
  • D — 휠을 당기니 확대(화면 중심에서 373.1 → 428.1px). 왼쪽 끌기로는 지도가 안 움직임(false), 가운데 버튼 끌기는 [60, 40] 그대로 따라옴.
  • E — 등고선이 노선 둘레 사각형 안에서만 그려짐(스크린샷). 창 크기와 무관한 고정 띠.
  • F — 열자마자 세 단추 모두 꺼짐 · 클릭만 하면 걸음이 안 생김(고친 자리) · 끌기 뒤 [되돌리기]·[초기화] 켜짐 · 되돌리면 「기준 미달」이 사라지고 [초기화] 꺼짐 · 다시하기로 복귀 · [초기화] 뒤 [되돌리기]로 물릴 수 있음. Ctrl+Z 동작 확인.
  • 전체 554 passed · 18 skipped, tsc --noEmit 통과. [저장]·[확인] 안 눌러 정본 그대로.

0-2-A 뒷손질 (2026-09-07 사용자 지적 3건, 같은 날 반영)

  • 접선점(직선↔R 만나는 자리)이 사라졌다 — ④ 를 고치며 손잡이를 「고른 곡선만」으로 줄였는데 그 바람에 표기까지 같이 없앴음. 접선점은 손잡이이기 이전에 읽을 정보임. → 늘 그림(고른 곡선은 속을 채워 도드라지게). 노드를 못 집던 문제는 집기 우선순위 (노드가 손잡이보다 먼저)로 이미 풀려 있어 다 내놓아도 안 겹침.
  • 라벨을 R + 곡선 길이 두 칸으로, [자동] 단추 삭제 — 교각 Δ 는 앞뒤 직선이 정하므로 둘은 L = R·Δ 로 묶임. 길이를 받으면 R 로 바꿔 한 값만 들고 감(두 벌로 두면 어긋남). 칸을 비우는 것이 곧 「자동」임.
  • 라벨 자리를 곡선 중심의 반대쪽으로(상하좌우 넷) — 중심 쪽에 두면 라벨이 곡선을 가림. 중심 방향은 접선점 두 방향의 단위벡터 합(각 이등분선)으로 구함.
  • 라벨 글자가 안 읽혔음--color-surface-2 가 이 테마에 없어 밝은 기본값으로 떨어져 다크 화면에 밝은 글자가 얹혔음. 모달 본체와 같은 토큰(--color-surface-raised · --color-text-body)으로 바꿈. 실측 배경 rgb(37,31,56) · 글자 rgb(228,224,240).
  • 상자 높이가 한 박자 늦게 자라 자리가 11px 어긋나던 것도 다음 프레임에 다시 맞추게 고침.

검증 — 접선점이 고르기 전에도 보임(스크린샷) · 곡선 길이 40 을 넣으니 반지름 203.7m 로 따라옴(Δ=11.25°) · 라벨이 노드 위쪽(중심이 아래인 곡선)에 붙음 · [자동] 단추 없음. 554 passed · 18 skipped, tsc 통과.

자체검증 방법 — 공용 브라우저(5174)에서 ① 노드를 끌어 다른 곡선이 안 사라짐을 스크린샷과 상태줄로 ② 고른 자리 옆 라벨에 R 이 뜨는지 ③ 휠 방향·팬 버튼이 배수유역도와 같은지 ④ 창을 줄였다 늘려도 등고선 띠가 유지되는지. 거울 시험은 tmp/tests/ 에 남김.

자체검증 (2026-09-06, 용화 확폭검증 프로젝트 · 공용 브라우저)

  • GET /route/plan — 예상노선 331 정점 = 계획노선 331 정점, edited=false(고친 적 없음).
  • POST /route/replan — 가운데 1/3 구간을 5m 옮긴 노선으로 요청 → 200, 203초, 노선 145 → 148, 연장 1,070 → 1,104.7m. 체인 로그에 배수유역 재분석 91초, 관 새로 11개 (기본 1 + 보충 10), 옛 측점 설계 67건 이월 확인.
  • POST /route/replan/reset — 계획노선 파일 삭제 후 같은 재계산 → 노선 148 → 149, 설계 68건 이월, edited=false 로 복귀. 예상노선 정본(expected_route.csv, 331 정점, 1,097m)이 이 경로에서 처음 세워짐.
  • 모달 실화면 — 좌측 「계획노선」 섹션의 [계획노선 편집] 로 열림, 등고선 + 노선 그림, 「노드 323개 · 예상노선과 같음」 표시. 배경 끌기로 화면 이동, 노선 위 노드를 60px 끌자 그 자리에 노선이 따라옴(놓은 자리 픽셀이 노선 색으로 바뀜).
  • 고친 점 — 재확정 체인이 initial_snapshot/ 을 통째로 지워 예상노선 원본이 사라지는 구멍을 발견해, 원본을 스냅샷 밖(B05_Profile/route/expected_route.csv)으로 옮김. 지표면 모델도 확정 선택값으로는 못 찾는 프로젝트가 있어 현재 노선이 쓰던 모델을 1순위로 바꿈(용화에서 500 오류로 드러남).

이 절의 완료분은 아카이브로 옮겼음 — docs/raw/plans/2026-09-07_plan_completed_items_2.md.


### 3-9 소단 — 착수 전 조사(기준 셋 비교·닿는 범위·크기)

조사 결과 (2026-09-07 보조 창) — 보고서 https://claude.ai/code/artifact/ef819d57-0d30-4d19-8df2-9b09c40645f7 막히는 것은 **「수치가 없다」가 아니라 「수치가 갈린다」**임. 이미 미결 6번에 올라가 있음.

출처 간격
별표2 (임도, 법정) 사면길이 2~3m 마다 50~100㎝
KDS 44 30 00 (도로) 깎기 높이 5m 마다 1m (리핑암 이상 매 20m·3m)
사방 세부기준 (훈령) 절·성토고 3~5m 0.5m 이상

별표2 안에서도 말이 둘임 — 경사 기준표 비고는 「토사지역은 절토면 높이에 따라」(수치 없음), 소단 조항은 「사면길이 2~3m 마다」. 재는 축부터 갈림(높이 vs 사면길이). 얼마나 다른가 — 절토고 5m·경사 1:1(사면길이 7.07m)에 셋을 대면 소단이 3개 / 0개 / 1개. 소단은 폭만큼 더 파므로 절토량·사면보호 수량이 그만큼 달라짐. 고른 뒤 바꾸면 전 측점 재계산임.

닿는 범위 — 뜻밖에 좁음. 절토 비탈면이 이미 「꼭짓점 목록 + 거리→높이 함수」 두 조각뿐이고, 암 경계 무릎(2단계 경사)이 바로 그 방식으로 들어가 있음. 소단도 같은 자리에 같은 방식으로 들어감.

  • 고칠 곳 — B06_Section_Engine_Design.py(breakpoints·_cut_slope_z) + 짝 common_util_cross_design_geometry.ts(breakpoints·cutSlopeZ) 두 파일.
  • 따라오는 곳 — 절·성토 면적은 설계선에서 바로 재므로 저절로 맞음(측구 굴착이 이미 그 방식). 도면·3D·유토곡선·구조물 트림은 design_line 을 읽는 24개 파일이 자동으로 따라옴.
  • 깨질 수 있는 전제 셋(규모를 정하는 것) — ① 무릎이 절토선에 하나뿐이라는 전제(소단과 겹치면 순서 결정) ② 토사/암 면적 나누기가 무릎 하나 기준(소단이 경계를 여러 번 넘으면 규칙 손봄) ③ 소단은 평평해서 지반과 나란할 때 「첫 교차」 판정이 흔들릴 수 있음.

크기 — 치수만 정해지면 1~2일. 첫 반나절이면 횡단 카드 절토선에 계단이 보임(눈 확인 지점).

사용자 결정 넷 (2026-09-07) — 「기준을 고르는 것」이 아니라 「사용자가 놓는 것」으로 바뀜

  • 법정 기준 중 하나를 자동 적용하지 않음. 사용자가 넣고 뺄 수 있게 하고 폭·간격 등으로 제어함(사용자 원문). ⇒ 셋 중 고르는 문제가 사라짐 — 다만 폼 기본값은 근거가 있어야 함 (「근거 없는 기본값 금지」 시험). 별표2(임도 법정, 사면길이 23m 마다 · 폭 50100㎝)를 기본값 후보로 삼되, 한 값으로 좁힌 근거를 적을 것.
  • 좌측 패널에서 사용자가 입력. 위 ①이 기본 옵션이고, **종단 위치는 「길이 + 측점 기준 전·후값」**으로 정함(사용자 원문). ⇒ C군 구간형 구조물과 같은 꼴 — 기준측점·전·후를 받는 기존 폼 방식을 그대로 씀. 「붕괴 우려 지역」을 프로그램이 판정하려 들지 않음.
  • 절토·성토 둘 다. 성토면에는 기슭막이·다단 벽이 이미 서므로 겹칠 때 규칙을 정해야 함.
  • 소단 기울기 = 안쪽으로 2도 기본값0도로 정정(2026-09-07 실무자 확인, ⑦ 항목 참조). ⚠ 지식DB 에는 적지 말 것 (사용자 지시) — 법령·교본 근거가 없는 값이므로 지식DB(근거 문서)에 넣지 않고 코드 기본값 + 이 계획서에만 둠. 근거 표기는 「2026-09-07 사용자 확정」.

### 3-9 소단 — 착수 순서(끝남)

착수 순서 — ① 무릎 하나 전제 풀기(소단 여러 개 + 무릎 공존) → ② 파이썬·TS 짝 + 거울 시험 (여기서 횡단 카드에 계단이 보임) → ③ 좌측 폼(C군 구간형과 같은 꼴: 길이·기준측점 전·후 + 폭·간격·기울기) → ④ USER_TOUCHED_KEYS 등록 + 7-3 회귀 한 줄 → ⑤ 3-6 ② 소단측구 연장 빈 칸.


### 3-9 안에 있던 「절토 경사를 사용자가 직접 넣게 함」 — 끝남

  • 🔴 절토 경사를 사용자가 직접 넣게 함끝냄(2026-09-07, ①~⑤ 아래 참조).
    • [저장] 뒤 정본 유지까지 확인(2026-09-07 사용자 허락받고 실화면, 용화 5601e828 · route 169 · 측점 1+0.0). 50° 를 넣고 [저장]세션 cutslope 를 지우고 새로고침50.0° · 암 절토 3.04 · 계 5.76㎡ 그대로. 세션이 비었으므로 값은 정본에서만 올 수 있고, 면적이 함께 살아 있으므로 다시 계산까지 그 경사로 된 것임(라벨만 남은 것이 아님). 되돌리기(↺) 후 [저장]·재확인으로 68.2° · 1.91 · 3.90㎡ 원상 복귀 — 사용자 프로젝트에는 시험값을 남기지 않았음.
    • [확정] 뒤에도 남는 것 확인(2026-09-07 사용자 지시, 실화면 · 용화 5601e828 · route 169 · 측점 1+0.0). 55° 를 넣고 [확정] → B07 로 넘어감(10초, 전 측점 재계산 포함) → B06 으로 돌아와 세션 cutslope 를 지우고 새로고침55.0° · 암 절토 2.50 · 계 4.94㎡ 그대로. 세션이 비었으니 값은 정본에서 온 것이고, 면적이 함께 살아 있으니 재계산도 그 경사로 된 것임. 되돌리기(↺)+[저장]으로 68.2° · 1.91 · 3.90㎡ 원상 복귀 — 사용자 자료에 시험값 안 남김.
      • 곁가지 — 확정이 3분대가 아니라 10초였음. 3분대는 노선 확인이고 종횡단 확정은 원래 초 단위임(0-11 표의 「B06 확정 2.8초」와 같은 자리). 되묻은 답으로 범위가 넘어졌음 — 「암 측점은 경사가 둘인데 어느 쪽이냐」를 묻자 사용자가 지반유형 버튼 자체를 정리하라고 확정했음(원문):

리핑암과 발파암 버튼을 삭제하면서 구분의 의미가 없어졌어. … 토사버튼만 존재하고 이값은 활성화/비활성화로 반영(기본값은 비활성화) / 비활성화 상태에서는 암경계선이 나오고 암경계선 아래는 당연히 사용자가 지정한 암 절토각 반영. 이후는 토사 절토 각도를 반영(현재 구현되어 있음) / 활성화 상태에서는 암반이 없으니 당연히 절토는 토사 절토 각도로만 구현 / 개별 횡단도에는 암 절토 각도의 개별 수정 가능해야함 / 전체 변경을 위해서는 좌측 패널의 표준 횡단면 설정을 이용. (표준 횡단면 설정으로 변경시 사용자가 기본값을 사용하지 않는 값들은 변경되면 안됨.)

  • ① 계산에 경사를 넣는 자리 — 파이썬 compute_cross_design · TS computeCrossDesign그룹을 만든 바로 뒤에서 cut_slope_ratio 만 갈아 끼움(각 한 곳). 부르는 쪽 9곳에서 표준값을 측점마다 복제하는 방식은 안 씀 — 한 곳만 빠져도 값이 조용히 사라지는 실패군임. 25 확인 끝(2026-09-07) — 그 자리를 갈아 끼우면 소단 위치·개수도 새 경사를 그대로 따라옴.
  • ② 측점 하나 — 카드에 암 절토각 칸(이미 서 있는 계통 그대로: 암 경계선 rockb · 표시 반폭 crossw 꼴). 세션 키 cutslope → 카드 입력 → [저장]·[확정]에서 cross_patches 로 정본. 각도(°)로 받고 속으로 1:n (소단 폼의 「안쪽 기울기 (°)」와 같은 말법. 1:0.4 = 68.2° · 1:1.0 = 45° · 1:1.5 = 33.7°).
    • 끝남(2026-09-07 d9f1a6계열, 실화면 8000/5173 · 용화 5601e828 · route 169). 카드 63장 아래에 「암 절토 68.2° ↺」 칸이 섰고, 측점 1+0.0 에서 수치로 확인 — 표준 68.2°(1:0.4) 암 1.91㎡ · 토사 1.99 · 계 3.90㎡ → 35° 로 눕히니 암 18.58 · 토사 9.74 · 계 28.32㎡ → ↺ 로 정확히 원복. 세션은 aislo:draft:cutslope:…{"0.00":1}, 되돌리면 {"0.00":0}(0 = 표준값을 씀).
    • 곁가지 — 토사 절토도 함께 늘어남(1.99 → 9.74). 암 사면이 누우면 무릎이 바깥으로 밀려 그 위 토사 구간도 길어짐. 암 경계선을 내릴 때와 같은 성질임(7-3 ⑨).
  • ③ 지반유형 버튼 셋 → 「토사」 토글 하나 (기본 꺼짐 = 암). 끈 상태에서만 암 경계선과 암 절토각 칸이 보임. 켜면 토사 경사 하나로만 그림.
    • 유토곡선·수량에 파급함 — 지금은 리핑암·발파암을 부피 환산계수까지 갈라 잡고 있음 (흐트러진 상태 1.35 vs 1.60 · 다져진 상태 1.15 vs 1.30 · config_system_design.py). 버튼을 없애면 암이 한 종류로만 잡힘 — 갈라 넣는 것은 8-1(설계내역에서 설계자 비율)로 넘기는 것이 맞음. 그때까지는 리핑암으로 잡음(지금 기본값이 그것임). 사용자에게 알리고 감.
  • ④ 전체 변경은 표준 횡단면 설정 — 이미 있음(공통 절토 경사 · 암 절토 경사). 단위를 카드와 맞출 것.
  • ⑤ ⚠ 표준을 바꿔도 개별로 고친 측점은 그대로 (사용자 원문 끝줄). 사용자 값이므로 USER_TOUCHED_KEYS 계통 + 7-3 회귀 한 줄.
    • ③④⑤ 끝남(2026-09-07 실화면 8000/5173 · 용화 5601e828 · route 169). · ③ 측점 1+0.0 — (끄기) 토사 1.99+암 1.91=3.90㎡ ↔ 토사(켜기) 전량 토사 7.20㎡, 토사로 켜면 암 경계선·암 절토각 칸이 함께 사라짐. 되돌리면 3.90㎡ 원복. · ④ 표준 패널 절토·성토 경사 칸 툴팁에 각도 병기(1:0.4 = 68.2°). · ⑤ 1번 카드에 40° 를 넣고 표준 암 절토경사를 0.4→0.8 [전체 측점 반영] — 1번 40.0° · 암 절토 5.66㎡ 그대로, 2번(값 없음)만 68.2°→51.3° · 2.61→4.01㎡. · ⚠ 남은 것 — [저장]·[확정] 뒤 정본 유지는 아직 실화면 확인 전(사용자 프로젝트 정본에 쓰는 것이라 누르지 않았음). 시험·코드 경로는 서 있음.
    • 경사는 「나르는 값」이 아니라 「기하 입력」 — 계산 뒤에 키만 베껴 붙이면 설계선은 옛 경사로 나오고 값만 새것이 됨. 소단과 같은 자리 — 계산 전에 읽어 넣는 순서를 지킬 것.

이 절의 완료분은 아카이브로 옮겼음 — docs/raw/plans/2026-09-07_plan_completed_items_2.md.



「7. 개발 환경」 구획 — 계획서에서 삭제(2026-09-07 사용자 지시)

사용자가 **「이 항목은 그냥 삭제」**로 지시해 계획서에서 뺐음. 내용은 여기 보존함. 요지 — 백엔드 자동 반영(감시)이 죽는 원인은 절반까지만 밝혀졌고, 고치지 않기로 사용자가 판단했음(「둘다 안할거야. 난 별로 불편함 없음」). 배제된 원인 셋과 파다가 알아 둔 사실 둘이 아래에 있음.

7. 개발 환경

7-2. 백엔드 자동 반영(감시)이 조용히 죽는 것 (2026-09-07 — 오늘 세 번 속았음)

오늘 두 창이 「옛 코드로 도는 서버」에 세 번 속았음 — 0-11 속도 측정(488초로 잘못 나옴) · 0-12 응답 필드 확인(새 필드가 아예 없는데 화면은 정상처럼 보임) · 표준단면 고침(감시 폴더에 들어 있는 .py 를 고쳤는데 응답이 옛 코드). 매번 틀린 결론을 한 번씩 냈다가 되물렀음.

아는 것B06_Section 은 감시 폴더 12개 안에 있는데도 .py 변경이 안 물렸음. 재시작하니 바로 반영됨. 재시작 전 프로세스는 그날 09:06 기동분이라 그 사이 감시가 죽어 있었을 수 있음. watchfiles 로 바꾼 뒤(55895c3e)의 일임.

  • 원인 — 절반까지. 「폴더 좁히기」가 의심 (2026-09-07)
    • 한글 경로·정션 아님watchfiles 를 그 폴더(…\B06_Section)에 직접 걸면 변경을 정상 감지함({(Change.added, '…\B06_Section\zz_wf_probe.py')}). 라이브러리·경로 문제 아님.
    • uvicorn 은 기동 때 감시 폴더 12개를 정확히 선언함(Will watch for changes in these directories: [… B06_Section …]). 그런데 그 폴더의 .py 를 고쳐도 리로드 줄이 안 뜸. 목록 밖(tmp/)도 안 뜸 ⇒ 이 프로세스는 아무 것도 리로드하지 않음.
    • 시간 순서 — 옛 프로세스는 tmp/ 변경에 리로드가 떴음(감시 폴더를 안 좁혔던 판). 폴더를 좁힌 뒤부터 리로드가 통째로 안 도는 것으로 보임(좁히기는 오늘 CPU 70% 를 잡느라 넣음).
    • 남은 갈래 — ⓐ uvicorn reload_dirs + WatchFiles 조합의 FileFilter 에서 걸러짐 ⓑ 부모 리로더가 이 기동 방식에서 감시 루프를 못 돎.
    • ⓐ 격리 시험 — 필터가 원인 아님(2026-09-07, 서버 안 건드림). main.py같은 인자uvicorn.config.Config(reload=True, reload_dirs=_reload_dirs()) 를 만들고 그 Config 로 uvicorn.supervisors.watchfilesreload.FileFilter 를 세워 실제 경로를 물음 — Config.reload_dirs 12개 정상 선언, reload_includes·reload_excludes 둘 다 비어 있고, B06_Section_Schema.py · common_util_route_polyline.py · tmp/tests/zz.py 전부 통과(True). ⇒ 감시 폴더 안의 .py 가 걸러져서 안 도는 것이 아님.
      • 곁가지 — 필터는 폴더를 안 봄(tmp 것도 True). 폴더 제한은 watch() 에 넘기는 reload_dirs 몫이고 그 목록은 정상임.
    • 지금까지 배제된 것 (다음 사람이 같은 자리를 다시 파지 않게) — ① 한글 경로·정션 (watchfiles 직접 시험에서 정상 감지) ② uvicorn 파일 필터(위) ③ 설정 누락 (reload=True · 폴더 12개 선언 확인).
    • 남은 것은 ⓑ 뿐안 하기로 함(2026-09-07 사용자 판단: 「둘다 안할거야. 난 별로 불편함 없음」). 자동 반영 고치기(ⓑ)도, 아래 곁가지(화면 서버 떼기)도 꺼내지 말 것. 급한 불은 stale 경고등이 이미 껐고, 재시작은 Claude 쪽 수고일 뿐 사용자 불편이 아님. 꺼낼 조건 = 사용자가 재시작 때문에 불편을 말할 때.
    • 파다가 알아 둔 것 둘(다음 사람이 다시 재지 않게)
      • 화면 서버(vite)는 main.py 의 자식(main.py → npm → vite, .claude/dev_up.py:40). 그래서 백엔드를 트리째 끄면 화면 서버도 같이 죽고 열려 있던 탭이 옛 모듈을 붙들어 백지가 됨(3-7 환경 규칙과 같은 자리). 재시작이 비싼 진짜 까닭은 서버가 아니라 이쪽임.
      • 로그인 세션은 DB 표(sessions, common_util/common_util_auth.py:115)라 재시작으로 지워지지 않음. ⚠ 다만 「재시작하면 다시 로그인해야 했다」는 기록이 있으므로 다른 까닭이 있을 수 있음 — 실측은 안 해 봤음.

이 절의 완료분은 아카이브로 옮겼음 — 2026-09-07_plan_completed_items_2.md · …_3.md(docs/raw/plans/).