사용자 확정(2026-09-07): 「연암과 경암 / 발파암과 리핑암 선택은 횡단도에서 선택 안함.
사유는 향후 설계내역에서 설계자가 직접 비율로 지정하기로 함. 암반 지정과 범위 애매모호한
경우가 있어. 실무자는 그렇게 하기로 판단함. 대신 경고부분은 삭제해주고 대신 각도를
사용자가 넣을수 있게 반영.」
**왜 폐기인가** — 판정이 서려면 측점마다 암질(연암·경암)을 못 박아야 하는데, 실무에서
암반 지정·범위가 애매해 그 못 박음 자체가 틀린 전제였음. 암 비율은 **설계내역 단계에서
설계자가 비율로** 넣음. 기준이 없으니 경고도 없음.
되돌린 것 — 1e596846(암질 선택) + 973128f7(별표2 경고). 카드 경고 배지 · 판정 모듈 ·
config 범위표 · context 필드 · 표준 패널 암질 칸 전부 사라짐.
⚠ **남긴 것** — `cut_slope_segments`(구간별 경사)는 지우지 않음. 소단 기하가 그 위에
서 있고 구간 경사를 아는 값이라 뒤에 쓸 자리가 있음. 4e7a4bf7(사면 구간 재료를 그린
경사비로 가름 — 무릎 위 라벨 13구간 오류 수정)도 남김. 검사와 무관하게 옳은 고침임.
시험 4건 — 되살아나지 않게 막는 쪽으로 다시 씀(판정 모듈 없음 · 카드 경고 없음 ·
암질 고르는 자리 없음 · **구간별 경사값은 남아 있음**).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
사용자 확정(2026-09-07): 「연암과 경암도 별도의 버튼으로 선택하게 하고 경암을 기본값으로
선택. 리핑을 할지 발파를 할지는 설계자가 선택 필요함.」
**축이 둘이었음 — 섞지 말 것.**
· **암질**(연암·경암) = 별표2 경사 판정의 기준. 설계자가 고름. 기본 **경암**.
· **굴착 공법**(리핑암·발파암, `cut_rock_kind`) = 어떻게 파는가. 수량·단가 몫.
처음에는 공법에서 암질을 유추하려 했는데 **그것이 잘못 세운 문제**였음. 매핑을 걷어내고
암질 선택 하나로 바꿈 — 표준 횡단면 설정 안, 자리는 그대로.
- config 매핑 상수 → `FOREST_ROAD_CUT_SLOPE_ROCK_QUALITY_DEFAULT = "hard_rock"`.
- 판정에서 `cut_rock_kind` 를 뗌. 수량 쪽 쓰임은 그대로 둠.
- 실측(용화 63측점·구간 124개) — 기본값 경암에서 **위반 0건**. config 기본 절토비 1:0.4 가
경암 범위(0.3~0.8) 안임. 토사 63구간도 0건. **기존 설계·수량 안 바뀜.**
(연암으로 고르면 61건 — 1:0.4 가 연암 하한 0.5 밖이라 그때는 설계 검토가 필요함.)
시험 8건 — 별표2 값 · 작업임도 제외 · **암질은 설계자가 고르고 기본은 경암** ·
**공법으로 암질을 추론하지 않음** · 구간별 판정 · 소단이 있어도 위반이 안 사라짐 ·
경계값 통과 · 카드에 같은 방식으로 붙는지.
7-3 회귀에 소단 한 줄 더함 — 「소단은 값이 아니라 **기하 입력**이라 계산 뒤에 베껴 붙이면
`berm` 값만 남고 계단이 안 그려진다」. 저장분 소단을 **계산 전에** 읽어 넣는 순서를 지킴.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
한 묶음으로 처리했음 — 화면에 5m 판정이 틀린 채 서 있는 시간이 없게.
성토 사면 꼭짓점 짝 신설(`fill_profile_points` · `fillProfilePoints`). 절토와 달리
경사가 하나뿐이라 무릎이 없고, 소단 규칙은 같음. 설계선·지반 교차·꼭짓점 목록이 모두
그 선을 봄.
⚠ **옹벽 의무 판정을 함께 고쳤음** — 「성토사면 길이 5m 이내, 넘으면 옹벽·석축 의무」
(`성토_비탈면.md` §2)를 재는 `fillSlopeLengths` 가 **「성토선은 1:n 직선」을 전제로
수평거리 × 기울기**로 재고 있었음(주석에도 그 전제가 적혀 있었음). 소단이 들어가면 그
전제가 깨져 값이 틀림.
고침 — 소단이 있으면 설계선을 걸어가며 **소단으로 끊긴 한 도막**의 최대 길이를 잼.
· 전체를 한 줄로 재면 소단을 넣어도 5m 를 넘어 **의무가 사라지지 않음**
· 실효 경사로 재면 완만해져 **의무가 사라진 것처럼** 보임
둘 다 틀리므로 「끊긴 한 도막」이 맞는 기준임.
**소단이 없는 측점은 종전 식을 그대로 탐** — `design.berm` 이 없으면 예전 계산 그대로라
값이 한 톨도 안 바뀜(구조로 보장). 화면 전후 대조는 다음 단계에서 냄.
자체검증 — 새 시험 2건(성토면에 계단이 서고 같은 거리에서 덜 내려감 · 소단이 없으면
성토선이 곧은 한 줄). 전체 542 passed · 18 skipped. TS 타입 검사·ruff 통과.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`B06_Section_UI_Page_Persist.ts` 의 import 한 줄만 갈렸음. 양쪽 다 쓰는 이름이라 합침 —
`readByKey`/`writeByKey`(저장소 선택을 한 창구로 모은 것)와 `writeState`(소단 배선)가
같은 파일에서 각각 한 번씩 쓰임. 다른 충돌 없음.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
다른 창이 전 측점을 돌려 찾은 것(용화 5601e828 · route 169, 63측점 124구간).
`material: "rock"` 인데 경사비가 1.0(토사)인 구간이 **13개** 나왔음.
까닭 — 재료를 **암반 경계선을 다시 재서** 붙였음. 그런데 무릎을 지난 뒤에도 경계선은
지반을 따라 계속 오르므로, 토사 경사로 그린 구간이 경계 아래로 **되돌아가 있는** 일이
흔함. 그것을 경계로 재면 그린 것과 라벨이 어긋남.
고침 — 재료는 **그 구간을 실제로 그린 경사비**를 따라 붙임(암 경사비에 가까우면 암,
토사 경사비에 가까우면 토사). 두 경사비가 같으면(2단 절토 아님) None.
영향 — 별표2 경사 판정이 갈리던 자리임. 13개가 「암인데 너무 완만」 위반으로 잘못 뜰 수
있었음. 토사 구간 50개는 종전대로 위반 0.
자체검증 — 새 시험 1건: 지반이 가파른 암 지반에서 모든 구간의 재료가 그 경사비와 맞는지
확인(어긋나면 실패). 거울 시험도 통과. 전체 542 passed · 18 skipped.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
법정 위반 표시가 없던 유일한 자리였음(평면 곡선반경·성토사면 길이는 이미 있음).
절토 경사비는 표준 횡단면 설정에서 오는 입력값일 뿐이라 범위를 벗어나도 아무 표시가 없었음.
- 기준 — 별표2(지식DB `01_임도/02_상세설계/절토_비탈면.md` §1): 경암 1:0.3~0.8 ·
연암 1:0.5~1.2 · 토사 1:0.8~1.5. **작업임도는 규정 없음 → 검사 제외.**
- 판정은 **구간별 경사**(`cut_slope_segments`)로 함. 소단이 서면 실효 경사가 완만해져
**위반이 사라진 것처럼** 보임(폭 1.0·간격 2 이면 1:1 이 1:1.71). 그 함정을 시험으로 못박음.
- 표시는 **기존 방식 그대로** — 성토사면·미폐합 경고와 같은 자리·같은 클래스. 새 방식 안 만듦.
- 지반유형(리핑암·발파암) → 별표2 줄 매핑은 법령 근거가 아니라 **프로그램 설정**이라
표준 횡단면 설정에서 **사용자가 고르게** 함(2026-09-07 사용자 확정). 기본값 리핑암 → 연암 ·
발파암 → 경암. 표준단면과 함께 저장돼 이미 사용자 값 계통임.
- 기준값은 서버가 컨텍스트로 내려보냄 — 화면에 상수를 복제하지 않음.
시험 7건 — 별표2 값 · 작업임도 제외 · 매핑이 기본값일 뿐 · 구간별 판정 ·
**소단이 있어도 위반이 안 사라짐** · 경계값 통과 · 카드에 같은 방식으로 붙는지.
⚠ 실화면 — 사용자 선택 칸은 섰음(리핑암 연암 · 발파암 경암). 다만 이 프로젝트 저장분
63측점에 `cut_slope_segments` 가 아직 없어(오늘 새로 생긴 키) 경고가 뜨는 것은 못 봄.
재계산이 한 번 돌면 채워짐 — 지금 경고 0건은 맞는 동작임.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
사용자가 소단을 **직접 놓는** 화면을 만들었음. 프로그램이 「붕괴 우려 지역」을 판정하지
않고, 법정 기준 셋 중 무엇도 자동 적용하지 않음(2026-09-07 사용자 확정).
폼은 C군 구간형 구조물과 **같은 꼴** — 기준 측점 + 전·후 거리로 종단 범위를 잡고
폭·간격·기울기를 함께 받음. 기본값 폭 0.5m · 간격(사면길이) 3.0m · 안쪽 2°.
새 모듈 `B06_Section_UI_Berm_Panel.ts` 로 뺐음 — 좌측 페이지가 이미 700줄을 넘어 있어
거기 더 넣지 않고 두 줄만 꽂음(그 파일 분리는 별건).
세션에는 **구간 목록**으로 둠. 측점별로 펴서 저장하면 「어디부터 어디까지 놓았나」를
되짚을 수 없음. 읽는 자리에서 펴고, 서버에는 측점키 dict 로 실어 보냄.
**확정 뒤에도 계단이 남게** — 소단 제원을 설계 결과에 되싣고(`berm`),
`USER_TOUCHED_KEYS` 에 넣어 재계산이 지우지 않게 함. 서버 재계산은 세션값이 없으면
저장분을 씀(`stored_berm`).
⚠ 소단은 사용자 조작값이면서 **기하 입력**이라 다른 사용자 키와 다름 — 계산 뒤에 키만
베껴 붙이면 설계선·면적은 계단 없이 나오고 `berm` 값만 남아 서로 어긋남. 그래서 단측점
설계 경로는 저장분을 **계산 전에** 읽도록 순서를 바꿨고, 포장 강제·세월교 노면 하강
재계산 경로에도 저장분 소단을 실었음. `extra_spans` 가 그렇게 빠져 있던 자리와 같음.
자체검증 — 새 시험 2건(저장분만으로 같은 설계가 나오는지 · 값이 없거나 손상되면 None).
전체 541 passed · 18 skipped. TS 타입 검사·ruff 통과.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
계단이 값에만 있던 것을 화면까지 연결함.
배선 — 세션 열쇠 `berm`(측점키 → 폭·간격·기울기)을 등록표에 두고,
`readBermSession` 으로 읽어 ① 브라우저 재계산(`refreshCrossDesigns`)과
② 서버 미리보기(`cross-design/preview` 의 `berms`) 양쪽에 실음.
암 경계선 오프셋과 같은 길이라 「계획선을 고치면 계단이 사라지는」 일이 없음.
실화면 확인(8001·5174, `/api/health` `stale:false`) — 측점 4120.0m 에 소단을 놓고
계획고를 한 칸 올렸다 내려 전 구간 재계산을 태움.
· 폭 0.5m · 간격 3.0m → 절토 6.84 → 8.10㎡ (토사 2.42→3.24 · 암 4.42→4.86)
· 폭 1.0m · 간격 2.0m → 절토 6.84 → 12.96㎡, 횡단도에 **계단이 눈으로 보임**
· 소단을 안 놓은 옆 측점(4100.0m)은 3.77㎡ 그대로 — 놓은 곳만 달라짐
· 되돌린 뒤 6.84㎡ 로 복귀. [저장]·[확정] 안 눌렀으므로 정본은 그대로.
`cut_slope_segments` 신설 — 절토 사면을 경사 구간별로 쪼갠 목록(파이썬·TS 짝).
법정 경사 검사가 읽을 값임(다른 창 요청). 소단이 서면 사면 전체를 하나로 재는
「실효 경사」가 완만해져 **위반이 사라진 것처럼** 보이므로(폭 1.0·간격 2 이면 설계 1:1 이
실효 1:1.71), 검사는 소단을 뺀 구간 자체를 봐야 함. 실측 — 소단을 놓아도 구간별 경사비는
1.0 그대로 나옴. 평탄부(소단)와 지반 만난 뒤 구간은 싣지 않음.
자체검증 — 거울 시험에 사면 구간 대조를 더해 파이썬·TS 일치 확인.
전체 539 passed · 18 skipped. TS 타입 검사 통과.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
B군(측구·맹암거 등)을 넣어도 **어디에 놓였는지 안 보였음**. 알약은 기준점 한 곳에만 찍히고,
시~종점 띠는 **고른 동안만** 폈기 때문. 측구처럼 길게 이어지는 시설은 「어디부터 어디까지」가
곧 그 시설의 내용이라 늘 보여야 함(2026-09-07 사용자 지시).
- 구간형이면 **늘** 띠를 그림. 고른 것은 두껍고 진하게(4px·0.55), 나머지는 얇고 옅게(3px·0.45).
- **레인 높이는 42px 그대로** — 2-5b 에서 줄여 둔 것을 안 부풀림.
- B05 가 레지스트리를 **한 번만 받아 쓰는 자리에 따라 나눔** — 추가 메뉴는 켜진 종류만
(종전대로 여섯 군), **알약 레인·군 판정은 전부**. B06 에서 넣은 B군은 레지스트리에서
꺼져 있어, 켜진 것만 주면 종단에 이름·색 없이 「?」 회색 알약으로 떴을 자리였음.
실측(용화, 구간형 7건) — 띠 **0 → 7개**, 높이 3px, 레인 42px 그대로, 알약 18개 그대로.
B군 6종은 전부 `interval` 이라 확인한 C군과 같은 경로를 탐. 타입 응답 36종에 B군 6종 포함 확인.
평면(지도)은 손대지 않음 — 노선 위에 구간을 얹는 방법이 지금 없어 **별건**으로 둠.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
배분 창 지적으로 확인한 자리 — 미룰 수 없는 쪽이었음.
무엇이 문제였나 — 소단은 평탄한데 암 경계선은 지반을 따라 올라감. 지반이 1:1 이면
폭 0.5m 소단 하나를 지나는 동안 경계는 0.5m 오르고 소단은 2°(0.017m)만 오름. 그래서
설계선이 경계 아래로 **되돌아 들어가는 일이 흔함**. 무릎을 한 번만 꺾는 종전 규칙으로는
그 구간을 **암인데 토사 경사로** 그려 절토가 조용히 커짐 — 경고도 안 뜸.
고침 — `multi_knee` 를 둬서 경계를 오갈 때마다 꺾게 함. **소단이 있을 때만** 켬.
소단이 없을 때 켜면 지금 측점들의 설계가 같이 바뀌기 때문임(실측: 물결 경계 0.0016m).
기본값은 거짓이라 소단을 안 쓰는 프로젝트는 그대로임.
자체검증
- 새 시험 1건 — 경계를 뚫고 올라갔다가 소단마다 뒤처져 되돌아 들어가는 지형에서
한 번만 꺾는 결과와 갈리고, 되돌아 들어간 구간이 암 경사(가파름)라 같은 거리에서
더 높이 올라가는 것을 확인.
- 거울 시험에 되돌아 들어가는 경우를 한 줄 더해 파이썬·TS 일치 확인(12건).
- 전체 539 passed · 18 skipped.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
사용자 확정(2026-09-07)에 따라 소단을 **자동 적용하지 않고 사용자가 놓는 것**으로 만듦.
이 커밋은 그 기하 한 벌이고, 화면 폼은 다음 단계임.
새 짝 모듈 `common_util_cross_berm.py` · `.ts` — 절토 사면 꼭짓점을 만듦.
암 경계 무릎과 소단이 한 목록에 함께 들어가고, `breakpoints` 가 그 꼭짓점을 설계선에
실어 도면·면적·유토곡선·3D 가 계단을 그대로 봄.
기본값 폭 0.5m · 간격(사면길이) 3.0m · 안쪽 기울기 2°.
· 폭·간격은 별표2 범위(사면길이 2~3m마다 · 폭 50~100㎝) 안에서 가장 적게 파는 조합임.
기본값은 되돌리기 쉬운 쪽이어야 함 — 더 넣는 것은 폼에서 한 번이지만 이미 판 것을
되돌리면 전 측점을 다시 계산해야 함. 실효 경사로도 그러함(경사 1:1 기준)
폭 0.5·간격 3 → 1:1.24 / 폭 1.0·간격 3 → 1:1.47 / 폭 1.0·간격 2 → 1:1.71.
· 기울기 2°는 사용자 확정값이고 법령·교본 근거가 없어 지식DB 에 적지 않음(사용자 지시).
무릎은 종전대로 **한 번만** 꺾음. 여러 번 꺾게 풀면 소단이 없는 지금 측점 설계도 같이
바뀌므로 별건으로 미룸(실측: 물결 경계에서 0.0016m 차이, 경계를 다시 만나면 구조가 달라짐).
자체검증
- 거울 시험 10건 `tmp/tests/test_cross_berm_mirror.py` — 소단 켠 5경우 포함해 파이썬·TS
꼭짓점이 1e-9 안에서 일치. 간격이 수평이 아니라 사면길이 기준인 것, 평탄부가 2° 기운 것,
소단이 없으면 옛 무릎 방식과 완전히 같은 것까지 확인.
- 면적 시험 6건 `tmp/tests/test_cross_berm_area.py` — 절토 55.115 → 78.785㎡(기본값),
폭·간격이 물량에 단조로 반영, 소단 모서리가 설계선에 실림, 소단을 안 주면 설계선까지 동일.
- ⚠ 어림식 두 번을 시험이 잡아냄. 「늘어난 면적 = 폭 × 그 위 높이」는 틀림(11.797 vs
7.770㎡) — 설계선이 밀리면 지반과 만나는 점도 함께 밀려 절토가 더 길어짐. 「폭만큼 밀림」도
틀림 — 참값은 「그 아래 소단 개수 × 폭」임. 시험은 그 참값으로 잼.
- 기존 거울 시험(`test_b06_cross_design_mirror.py`) 10건 그대로 통과 — 리팩터가 값을
안 바꿨다는 증거임. 전체 537 passed · 18 skipped.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
사용자 승인(2026-09-07). 앞서 취향을 localStorage 로 옮겨 **탭·재시작** 문제는 풀었고,
이 변경은 그 위에 **다른 PC 에서도 같은 배치**를 얹음. 사용자가 노트북·데스크톱 두 대를 오감.
- `db_management/018_user_ui_prefs.sql` — 사용자당 한 줄, `prefs` JSON 한 칸.
칸을 나누면 취향이 늘 때마다 마이그레이션이 또 필요해 한 칸에 담음.
- `GET/PUT /api/dashboard/me/ui-prefs` — 배치 값만. **설계값은 안 담음**(문자열만 받음).
- 로그인이 확인된 첫 순간에 한 번 받아 로컬 위에 얹고, 취향이 바뀌면 1.5초 모아 올림.
서버가 없거나 못 읽으면 **로컬 값으로 그대로 돔**(계획서 원문).
⚠ 같은 함정을 또 밟을 뻔했음 — 키만 아는 자리가 저장소를 직접 고르면 **올려보내기도 안 걸림**.
그래서 `storageOf` 를 없애고 **읽기·쓰기 창구 하나**(`readByKey`/`writeByKey`)로 모음.
저장소 선택과 서버 올려보내기가 그 한 곳에만 있음. 그물도 그 이름으로 갱신.
**DB 는 아직 적용하지 않았음** — 표가 없으면 API 가 실패하고 화면은 로컬 값으로 도는 것이
정상 동작임. 적용 시점은 다른 창들과 맞춘 뒤.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
오늘 같은 계통이 두 번 났음 — 키는 등록표에서 받는데 **저장소는 제 맘대로** 고르는 자리.
· `rockb` — 키를 날문자열로 만들어 읽는 쪽이 늘 빈 값(`f8fafd23`)
· 화면 취향 — 키는 맞았는데 저장소를 직접 골라 **절반만** 옮겨짐(`9db84d9d`)
남은 두 자리(암 경계선 저장·표시 반폭)도 `storageOf` 를 거치게 함. 지금은 둘 다 초안이라
세션이 맞지만, 통이 바뀌면 조용히 갈릴 자리였음.
그물도 넓힘(`test_session_keys_registered.py`) — 등록표 키를 쓰는 파일이 저장소를 이름으로
직접 고르면 깨짐. 고르는 곳은 `b_page_state` 하나. 앱 전역 값(`CURRENT_PROJECT_ID_KEY`)은
등록표 밖이라 제외하고 이유를 적어 둠. **훑어 보니 남은 위반 0곳.**
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
화면 취향까지 sessionStorage 에 있어 **탭을 새로 열거나 브라우저를 껐다 켜면** 패널 높이·
접힘·유토곡선 열림이 매번 초기화됐음. 계획서에는 「PC 를 바꾸면」으로 적혀 있었으나 실제
불편은 훨씬 잦았음(사용자 승인 뒤 조사에서 드러남).
- 저장소를 통(bucket)으로 가름 — **`pref` 만 localStorage**, 초안·결과는 세션 그대로.
설계 데이터가 아니므로 CLAUDE.md 5장 「캐시=세션」에 걸리지 않음.
- 키만 들고 저장소를 직접 만지던 화면 공용 부품 셋(`ui_template_resizer`·`_overlay`·
`_overlay_drag`)이 같은 판단(`storageOf`)을 쓰게 함 — 안 그러면 높이 값만 세션에 남아
절반만 옮겨졌음(실측: 접힘 4개는 옮겨지고 높이 3개는 안 옮겨짐).
- 첫 로드 때 세션에 남은 옛 취향을 한 번 쓸어 옮김 — 이 변경으로 쓰던 크기가 초기화되지 않게.
실측(공용 브라우저) — 옮긴 뒤 취향 8개 전부 localStorage, 세션 0개, 높이 보존
(종단 438px · 표 204px). **새 탭에서도 8개 그대로**(같은 값). 검증 탭은 닫음.
시험 `tmp/tests/test_prefs_persist_across_tabs.py` 4건 — 취향만 갈리는지 · 초안·결과는
세션인지 · 공용 부품이 같은 판단을 쓰는지 · 옛 값을 한 번 옮기는지.
이 조각은 DB 없이 되는 몫임. 계정 저장(`018_user_ui_prefs`)은 다음 단계.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
사용자 결정 두 가지를 반영함(2026-09-07).
① 측구(옆도랑) — 목록에 두되 표시만.
레지스트리에 `design_owner` 를 새로 두고 측구에 「횡단 설계」를 적음. 구조물 배치 폼에서
그 종류를 고르면 「횡단 설계에서 관리 — 여기서 넣어도 제원·수량은 그쪽 값을 씁니다.」가
뜸. `managed_by` 와 달리 저장은 그대로 되므로 배치는 계속 가능함.
까닭 — 횡단 설계가 측구 켬/끔·형식·터파기 단면적(`ditch_area_m2`)을 파이썬·TS 짝으로
이미 셈함. 구조물로 또 세면 같은 것을 두 번 계상함.
목록 항목에 안 붙이고 폼에 붙인 것은 「한 항목 = [측점][이름]만」이 2026-08-18 사용자
지시이기 때문임.
② B군 수량 1단계 — 구조물 정본에서 시설별 연장(m)을 냄.
`common_util_structure_lengths.py` 하나. B군 수량 단위가 전부 m 이라 단면 기하가 필요
없음(맹암거 12-10 · L형 측구 12-9-1 · 산마루측구 12-9-2). 수량서 양식에 안 묶이므로
B08 을 실무 xlsx 양식으로 다시 짜도 그대로 씀.
빼는 것 셋 — 관 정본 소관(`managed_by`), 횡단 소관(`design_owner`), 그리고 소단측구
(놓일 소단이 아직 없음 — 계획서 3-9 뒤에 채움).
겹친 구간은 합쳐서 셈함. 같은 시설을 겹치게 두 번 넣으면 단순 합이 그 구간을 두 번
세기 때문임. 원래 합(`raw_length_m`)도 함께 내보내 겹침이 숨지 않게 함.
자체검증
- 새 시험 7건 `tmp/tests/test_structure_lengths.py` — 종류별 합산 / 겹침 제거(80m 인데
단순 합 110m) / 측구 제외 / 소단측구 제외 / 배관 제외 / 구간 없는 항목은 스키마가 막음
/ 빈 프로젝트.
- 화면 확인(8001·5174, `/api/health` `stale:false`) — 구조물군 B → 종류 측구 선택 시
안내가 254×44px 로 뜨고 문구가 맞음. 리셋으로 폼 되돌림.
- 전체 521 passed · 18 skipped.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
자동 리로드가 조용히 죽어 **옛 코드로 도는 서버**에 오늘 두 창이 세 번 속았음
(속도 488초 오측 · 응답에 새 필드가 없는데 화면은 정상으로 보임 · 표준단면 응답이 옛 스키마).
매번 틀린 결론을 한 번씩 냈다가 되물렀음. 규칙("재기 전에 재시작")으로는 세 번 다 안 지켜져
**한 번 보면 아는 표시**로 바꿈.
- `/api/health` 가 개발 모드에서 `started_at` · `code_mtime`(기동 때 본 소스 최신 수정) ·
`source_mtime`(지금 소스 최신 수정) · `stale` 을 함께 냄. `stale: true` 면 재시작 전
어떤 측정도 믿으면 안 됨.
- 기동 로그에 감시 폴더 수와 소스 최신 수정 시각을 한 줄 남김.
- 실측 — 감시 폴더의 `.py` 를 고치니 리로드는 **안 뜨고** `stale` 이 즉시 true 로 바뀜.
원인 추적은 계속함(PLAN 7-2) — 한글 경로·정션은 **아님**(`watchfiles` 를 그 폴더에
직접 걸어 보니 변경을 정상 감지). uvicorn 이 감시 폴더 12개를 선언하고도 리로드를
안 내는 자리라 더 파야 함.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
같은 노선인데 지문이 늘 어긋나 관 지점이 매번 투영 이월을 탔음.
까닭 — 같은 노선을 두 파일이 다른 자릿수로 담고 있음.
`planned_route.csv` 는 소수 4자리(`208403.2001`), `route_main.geojson` 은 소수 3자리
(`208403.2`)로 csv 를 mm 반올림한 사본임. 좌표 차는 최대 0.5mm 뿐인데, 지문이
`f"{x:.2f}"` 로 0.01m 자리에서 끊는 탓에 그 0.5mm 가 `.xx5` 경계를 넘는 정점마다
글자가 바뀜 — 169개 중 16개가 그랬음.
경계에서 자르는 방식은 저장 자릿수가 또 바뀌면 다시 흔들리므로, 글자 일치 대신
**허용오차**로 가름. 저장된 관을 지금 노선에 투영해 **재 보기만** 하고
(`max_projection_shift`, 값은 안 고침), 최대 어긋남이 0.05m 이하면 같은 노선으로 보고
저장분을 그대로 돌려줌.
허용오차 0.05m 근거 — 실측 어긋남이 최대 0.0053m 이라 다섯 배 이상 여유이고,
사람이 노선을 실제로 고치면 관은 m 단위로 밀리므로 「같다」로 볼 위험이 없음.
투영 이월 가지는 그대로 둠 — 대신 그 가지가 실제로 돌면 WARNING 을 찍게 함.
한동안 0 인 것을 확인한 뒤에야 지울 수 있음(먼저 지우면 관이 통째로 사라짐).
자체검증
- 새 시험 3건 `tmp/tests/test_pipe_route_tolerance.py` — mm 반올림 사본은 같은 노선으로
판정되고 누가거리가 한 값도 안 움직임 / 중간을 3m 민 노선은 안 걸림 / 허용오차 범위.
- 기존 `test_pipe_point_projection.py` 2건 그대로 통과(±0.7m 잔물결 노선은 여전히 투영).
- 저장된 실제 3개 프로젝트 전후 대조 — 셋 다 「다름 → 투영」이 「같음 → 저장분 그대로」로
바뀜. 화면 값 변화는 최대 0.0053m · 0.0009m · 0.0005m.
- 전체 514 passed · 18 skipped.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
표준단면 편집값은 sessionStorage 에만 살아 **탭을 새로 열면** 사라졌음. 그때 브라우저는
config 기본값으로, 서버는 저장분으로 계산해 **같은 측점이 갈렸음**. 표준단면은 모든 측점의
횡단 모양을 정하므로 면적·유토곡선·수량까지 그대로 흐름.
- `sections/context` 가 저장분을 **한 칸 더** 실어 보냄(`stored_standard_cross_section`).
기존 `standard_cross_section`(config 기본값)은 그대로 둠 — 옛 화면 안 깨짐, 마이그레이션 없음.
- 브라우저는 두 화면이 함께 지나는 한 자리(`fetchSectionContext`)에서 **세션이 비었을 때만**
그 값으로 세움. 그 탭에서 고친 값이 있으면 안 건드림(초안 우선).
실측(용화 5601e828 · route 169):
- 고치기 전 — 저장분 암반 횡단경사 **5%** · 측구 상단폭 **0.9m** 인데
화면이 받는 값은 **3%** · **0.69m**(config 기본값)였음. 응답에 저장분 칸 자체가 없었음.
- 고친 뒤 — 응답에 0.9·5 가 실리고, **빈 새 탭**에서 B06 을 열면 세션이
`rock.ditch.top_width_m=0.9` · `rock.cross_slope_pct.max=5` 로 섬(검증 탭은 닫음).
시험 `test_b06_stored_standard_reaches_browser.py` 3건 — 칸이 따로 있고 기본은 None ·
라우터가 저장분을 실음 · 브라우저가 세션이 빈 경우에만 세움(두 갈래 모두).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
노선을 다시 계산하면 세션의 옛 노선치가 그대로 쌓였음. 용화(5601e828)에 145·161·169
세 노선치가 남아 있는 것을 실측함.
원인은 「안 지웠다」가 아니라 **못 지웠다** — 노선 변경 자리는 새 번호를 아직 모르니
`clearDrafts(projectId)` 로 부르는데, `stateKey` 가 노선 번호 없이는 `null` 을 내어
route 범위 키가 한 개도 안 나갔음. 부르는 쪽 주석은 「남기지 않는다」였고 실제로는 다 남았음.
`clearState` 한 곳에서, 노선 범위인데 노선 번호가 없으면
`aislo:<통>:<이름>:<프로젝트>:` 로 시작하는 키를 모두 쓸어 내게 함.
`clearDrafts` 와 `clearResults` 가 함께 나음. 노선 번호를 주면 종전대로 그 한 벌만 나감.
안전 확인 — 옛 노선치를 읽는 자리 없음(`readState` 호출 8곳 전부 전역·프로젝트 범위).
노선 복원도 새 route id 를 발급하므로 옛 키는 다시 안 불림.
자체검증 — `tmp/tests/test_page_state_route_sweep.py` 3건 신설.
옛 코드로 같은 시험을 돌리면 세 벌이 그대로 남아 깨지는 것까지 확인(헛도는 시험 아님).
전체 511 passed · 18 skipped.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`std-cross` 가 [저장] 뒤에도 안 비는 것이 실수인지 의도인지 갈렸음 — **의도로 확정.**
다른 초안과 달리 이 값은 브라우저가 가진 **유일한** 사용자 표준단면임:
`sections/context` 가 주는 `standard_cross_section` 은 저장분이 아니라 config 기본값이고
(`B06_Section_Router.py:132`), 저장분은 서버 안에서만 쓰임
(`stored_standard_cross_section`). 비우면 브라우저 횡단 계산이 그 순간 config 기본값으로
되돌아감. [초기화]에서만 버림.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
쓰는 쪽은 `stateKey("rockb", …)` = `aislo:draft:rockb:{p}:{r}` 에 쓰는데, 읽는 쪽
`readRockBoundarySession` 이 옛 키 `b06:rockb:{p}:{r}` 를 날문자열로 읽고 있었음.
`writeStateRaw` 는 옛 키를 안 쓰므로 **읽는 쪽이 늘 빈 값**을 받았음.
그래서 계획선을 고쳐 횡단을 다시 계산할 때(`B06_Section_Cross_Refresh`) 사용자가 옮긴
암 경계선이 안 실려 나가 **토사/암 나눔이 기본값으로 되돌아갔음** — 수량이 갈리는 자리.
[저장] payload 는 메모리 Map 을 써서 저장 자체는 무사했음.
- 읽는 쪽을 등록표(`readState("rockb", …)`)로 바꿈. 옛 키 만드는 함수는 지움.
- 실화면 실측(용화 5601e828 · route 169) — 경계선 -0.5 → -0.8m 로 옮기니
새 키에 `{"0.00":-0.8}` 가 쌓이고 **옛 키는 아예 없었음**(고치기 전 근거).
고친 뒤 새로고침해도 값·표기 그대로.
시험 `tmp/tests/test_session_keys_registered.py` 3건 — 등록표 밖 날문자열 세션 키를
쓰면 깨지는 그물 + 이 자리 못박기. **훑어 보니 다른 날문자열 키는 0개**로,
`rockb` 가 유일한 구멍이었음.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
엔진 둘이 결과 딕셔너리에 **엔진이 실제 적용했는지**를 담아 저장했고, 그 값이 다음
재계산에 인자로 되먹여졌음. 그래서 토사 측점처럼 2단을 못 쓰는 자리에서 사용자가 켜 두면
저장값이 False 로 바뀌고, 그 False 가 다시 인자가 되어 **켬이 영구히 사라졌음.**
- `two_stage_slope` 를 **받은 요청값 그대로** 내보내게 함(파이썬·TS 짝 두 줄).
- 「실제로 적용됐나」를 읽는 곳은 코드 전체에 없음(전수 확인) — 읽는 쪽 여섯은 모두
사용자 설정으로 씀. 2단 사면이 그려지는 근거는 이 값이 아니라 설계선 기하임.
- 저장 형식 안 바뀜. 옛 저장분도 그대로 읽힘.
시험 `test_b06_two_stage_slope_kept.py` 4건 — 토사에서 켬 유지 · 되먹임 왕복 ·
끔도 유지 · 「2단은 설계선이 그린다」(암은 기하가 갈리고 토사는 같음) 확인.
거울 시험은 기존 `test_b06_cross_design_mirror.py` 가 그대로 잡음.
같은 모양인 `ditch_enabled` 은 손대지 않음 — 그쪽은 저장값을 「측구 없음」 판정으로
읽는 곳이 4군데(횡단 맞춤·횡단 그리기·B07 도면·3D 카브)라 처방이 다름.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
계획서 7-1. 화면의 **모든 `/api` 요청이 「Failed to fetch」** 가 되는데 서버는 멀쩡한
증상이 하루 종일 있었음. 원인은 프록시 대상이 `http://localhost:<포트>` 인 것 —
Node 18+ 는 `localhost` 를 **IPv6(::1) 먼저** 물고, 백엔드는 `SERVER_HOST=0.0.0.0`
(IPv4 전용)으로 떠서 서로 못 만남.
실측 — `127.0.0.1:8001` 200 · `[::1]:8001` 연결 실패.
고친 뒤(vite 재기동 포함) 화면에서 `/api/health` **200**, 로그인 POST **200**.
오늘 겪은 「저장 Failed to fetch」 두 번과 「화면 백지」 세 번이 이것으로 설명됨 —
부팅 때 API 가 죽으면 앱이 아무것도 안 그리고 콘솔 오류도 안 남았음.
공용 파일이라 배분 창(claude-b1) 승인을 받고 고침. 5173·5174 양쪽에 같이 적용됨.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
새 사용자 값을 더할 때 **네 곳을 다 고쳐야** 했고, 하나만 빠지면 그 값이 저장에서
조용히 사라졌음(`extra_spans` 실사고 `b6941bd2`). 나열을 걷어 목록 한 벌로 모음.
- 확정·저장 payload 조립(`_Router_Confirm.py`) — `if patch_item.X is not None:` 11개를
스키마 통째 덤프 + 최상위 None 걷기로 바꿈. 새 필드가 저절로 실림.
중첩 None 은 남김(기슭막이 4축 `d: null` 은 「자동」이라는 뜻).
- 브라우저 payload(`_Section_Store.ts`) — `put("X", design.X)` 10줄을 목록 순회로.
- `_Cross_Refresh.ts` 에 `USER_TOUCHED_KEYS` 를 내보내고 `PRESERVED_KEYS` 는
거기에 상태 둘을 더해 만들게 함. 서버 목록과 짝임을 머리에 적음.
전수 조사 결과 `design` 키는 세 갈래였음 — ① 재계산 인자로 되먹여 사는 것 8개
② 목록으로 베껴야 사는 것 12개 ③ 순수 계산값 24개. 위험한 칸은 ② 하나뿐임.
시험 3건 추가:
- `test_b06_design_key_split.py` — 사용자 값이 ①·② 어디에도 안 걸리면 깨짐(불변식).
지금 코드는 통과 = 알려지지 않은 구멍 없음.
- `test_b06_recompute_keeps_user_values.py` — 포장 재계산 뒤 ② 값 보존·① 인자 전달.
- `test_b06_user_touched_keys.py` — 손나열이 되살아나면 깨지는 검사 둘 추가.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
3-7 (2026-09-07). 안내는 이미 있었으나 `activeRevet` 이 그 벽일 때만 떠서, 좌측 「구조물
배치」 폼처럼 **조정창을 안 거친 경로**로 단 수를 바꾸면 개수만 조용히 잘렸음. 사용자는
[+]를 눌러도 아무 일이 없는 것으로 보임.
- 조건을 없애고 **자리(측점·측·세워진 단 수)마다 한 번만** 내도록 바꿈. 다시 그릴 때마다
뜨면 시끄럽고, 결과가 달라지면(지형·조작) 키가 달라져 다시 알림.
- 요청대로 다 선 자리는 기록을 지워, 나중에 또 모자라면 다시 알림.
- 문구는 기존 `B06_Cross_Extra_Limit` 그대로 — 새 문구를 만들지 않음(중복 토스트 방지).
앞서 `Station_Controls.syncCount` 에 같은 안내를 붙였다가 **기존 것과 겹쳐 두 줄로 뜨는 것**을
실화면에서 보고 되돌렸음. 안내는 이 한 자리에만 둠.
tsc 통과 · 시험 508 통과.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- 독립 기슭막이(D군) 툴팁은 **따로 만들어져** 있어 「지형에 막힘」조차 안 떴음.
배관 벽과 같은 두 줄(지형 막힘 · 각도 하한)을 같은 문구로 붙임.
- 기준벽 경로(`computeRevetmentLayout`)에도 `shiftFloorM` 을 남김 —
하한이 `1.2 × 상하 내림 − 자동 자리`라 d 2.6m 자리에서 1.0m 요청이 삼켜졌음.
- 하한 알림은 **사용자가 실제로 값을 넣었을 때만** 뜨게 좁힘. 요청이 0이면
하한은 그냥 기본 자리라 모든 벽에 뜨면 소리만 됨.
- 문구를 두 경로에 맞게 고침 — 「상하 내림의 1.2배」가 아니라
「성토선 각도(1:1.2)를 지키려면 그만큼은 나가 있어야 함」.
- 시험 3건(`test_b06_extra_shift_floor.py`) — 하한 적용·알림·요청 없을 때 침묵.
미확인 — 실화면에서 알림이 뜨는 것까지는 못 봄. 지금 프로젝트(용화)의 카드가
전부 링크 카드라 좌우 이동 조작 자체가 먹지 않아 조건을 세우지 못했음.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
「좌우 1.0m 를 넣어도 안 움직인다」의 원인은 지형이 아니라 **선반 하한**이었음 —
좌우 이동은 선반 길이가 음수가 되지 않게 `1.2 × 상하 내림`까지 따라 나가므로,
저장값이 d 2.6m 이면 하한이 3.12m 라 1.0m 요청은 아무 변화도 못 냄. 그런데도
`shiftBlockedM`(지형에 막힌 양)은 0 이라 까닭을 알 길이 없었음.
- `shiftFloorM` 추가 — 요청이 하한에 눌려 통째로 무시된 경우 그 하한을 남김.
- 벽 툴팁에 한 줄 — 하한 값과 「그 아래로 넣으면 안 움직임」을 적음.
지형 탓(`shiftBlockedM`)과 자리를 나눠 두 까닭이 안 섞이게 함.
- 시험 `tmp/tests/test_b06_extra_shift_floor.py` — 화면 코드를 그대로 컴파일해
Node 로 돌림. 좌우 0.5m 요청이 d 2.0m 아래에서 2.4m 로 밀리고 그 값이 알려지는 것 확인.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- `B05_Profile_UI_RouteEdit_Curve.ts` 신설(68줄) — 손잡이 끌기 역셈
(`dragHandleTo`)·직선 교점(`intersect`)·내각(`innerAngleDeg`) 순수 기하만.
- 모달은 740 → 692줄. 화면 배선·동작 불변, 셈 식 그대로 옮김.
- 서버 정셈과 짝임을 새 파일 머리에 명시. 거울 시험은 새 자리를 보게 갱신.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
사용자 승인(2026-09-07). 없으면 우버콘이 StatReload 로 떨어져 0.25초마다
감시 폴더를 통째로 훑고, 그러면 요청이 없어도 CPU 를 먹음.
실측 (놀고 있는 서버) — 감시 폴더를 좁히기 전 **70%** → 좁힌 뒤 **18%** →
이 패키지 설치 뒤 **0%**. 기동 로그가
`Started reloader process [...] using WatchFiles` 로 바뀜.
보조 창(8001)도 같은 값 확인 — 리로더 0% · 서버 0%.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
④(2026-09-07)로 붙인 레인이 `chartWrap` 안에 서는데, 그래프 높이를 정할 때 잰 값에서
레인 몫을 빼지 않아 되먹임이 생겼음 — 잰 값이 커지면 그래프를 키우고, 그러면 다음 회차에
더 커짐. 실측 패널이 **16,664px** 까지 부풀어 카드·알약을 누를 수 없었음.
레인 높이(`STRUCTURE_LANE_HEIGHT_PX`)를 빼서 한 번에 수렴하게 함.
자체검증 — 패널 242px · 그래프 228px · 레인 42px · 알약 10개. tsc 통과.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
실화면에서 잡음 — 곡선 하나만 지워도 나머지 반지름이 **R 12~199m → 전부 12m**
로 되돌아갔음.
원인은 편집 모델과 저장 모델이 다른 것이었음. 서버는 처음 만들 때 이어진
꺾임을 한 곡선으로 묶는데, 그 곡선의 교각점은 앞뒤 직선을 늘려 만나는 자리라
**원본 꺾임점 중 어느 것도 아님**. 편집은 「꺾임점 하나 = 곡선 하나」로
표현되므로, 묶인 채로 두면 한 번만 손대도 묶음이 낱개로 흩어지고 각 자리가
하한으로 떨어졌음.
모달이 자료를 읽을 때 **묶인 구간을 그 교각점 하나로 갈아 끼우게** 함 —
안쪽 꺾임점은 그 곡선이 대신하므로 뺌. 앞뒤 직선과 반지름이 그대로라
그려지는 선은 똑같고, 이제 손대도 안 흩어짐. 서버가 고른 반지름도 함께
들고 가 [확인] 때 되돌려 보냄.
시험 tmp/tests/test_route_polyline_apex_roundtrip.py 2건 — 갈아 끼워도 곡선
수·반지름이 같고 선이 **1mm 안**에서 일치, 그리고 안 갈아 끼우면 실제로
하한으로 눌리는 것(왜 필요한지)도 못박음.
실화면 확인(용화) — 되돌린 뒤 곡선 26곳 R 최대 198.9m → 곡선 하나 지우고
[확인] → 곡선 25곳, **R 42.5·51·198.9m 그대로**.
tsc --noEmit 통과 · pytest 432 passed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
사용자 확정(2026-09-07) — 「직선과 곡선 교차점 이동 시에는 직선의 각도와
반지름 값 변경이 되」. 되물어 확정: 끄는 점은 **곡선 시작·끝점**.
셈은 서버의 반대 방향임 — 서버는 교각점·R 에서 접선점을 내고, 화면은 끈
접선점에서 교각점·R 을 구함. 끈 쪽 직선만 돌리고 반대쪽은 옛 교각점을 지나는
그대로 두어, 두 직선이 만나는 자리가 새 교각점이 되고
R = |새 교각점 − 끈 자리| / tan(교각/2) 로 나옴.
**두 벌이 아니라 짝임** — 왕복이 제자리인지를
tmp/tests/test_route_polyline_handle_drag.py 3건이 지킴. 화면이 낸 (교각점, R)
을 서버 셈에 넣으면 끈 자리가 **1µm 안**으로 되돌아옴. 접선점을 직선 위에서만
밀면 교각점이 안 움직이고 R 만 커지는 것도 확인.
손잡이를 **속 빈 네모(5px)** 로 키움 — 3.5px 채운 네모는 선·노드와 색이 같아
눈에도 안 띄고 집기도 어려웠음(실화면). 곡선을 지운 자리에는 손잡이도 없음.
실화면 확인(용화) — 손잡이를 끌자 「14번째 꺾임점 · R 16.6 · 값 지정」으로
바뀜. 서버가 곡선 성분 22개(예: R 44.3m, 노드 1~3을 한 곡선으로)를 내려줌.
⚠ 그 확인 도중 **서버가 또 옛 코드로 돌고 있었음**(응답에 `curves` 키 자체가
없었음). 오늘 두 번째임 — 화면 검증 전에 응답에 새 필드가 있는지부터 볼 것.
tsc --noEmit 통과 · pytest 430 passed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
PLAN 2-5 (2026-09-07 사용자 지시 4 「구조물 표시 통일」). B06 종단에는 구조물 마크가
아예 없었고 배수관·세월교만 다른 계통으로 그려졌음.
- B05 가 쓰는 **같은 부품**(`buildStructureLane`)을 B06 종단 그래프 아래에 붙임.
이름·중복 번호·툴팁·약호가 전부 B05 규칙 그대로 따라옴.
- 종단 차트가 `toX`(누가거리 → px)를 내보내 레인이 그래프와 같은 자리를 씀.
- 좌측 「구조물 배치」가 목록을 받을 때마다 `setStructureMarks` 로 넘김(구조물 정본 +
관 정본). 관을 가상 구조물로 바꾸는 `pipesToStructureMarks` 는 B05 안에 있던 것을
공용 자리(`B05_Profile_Api_Structures`)로 올려 **한 벌만** 둠.
- 알약을 누르면 그 자리에 가장 가까운 측점 카드를 고름(좌측 목록 클릭과 같은 규칙).
끌기는 B05 몫이라 여기서는 자리만 보임(배수유역 재분할이 걸림).
- 검증 훅 `window.__b06Marks` 추가(`__corridorBuild` 등과 같은 용도).
자체검증 — 실화면 B06 종단에 레인 1줄·알약 10개(「배수관 1~8 · 세월교 1~2」)가 B05 와
같은 표기로 뜸. 훅 값 `{structures:10, types:36}`. tsc 통과.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
실화면에서 [곡선 지우기] 클릭이 캔버스에 가로채였음 —
「<canvas …> subtree intercepts pointer events」.
원인은 세로 배치임. 상자가 세로 flex 이고 캔버스 자리가 `flex: 1 1 auto` 라
공간을 다 먹는데, 새로 넣은 편집줄에 `flex: none` 이 없어 눌려 없어지고
캔버스가 그 위를 덮었음. 바닥 단추줄과 같이 `flex: none` 을 주고
`z-index: 1` 로 올림.
실화면 재확인(용화, 20번째 꺾임점) — 네 조작 모두 동작:
고름 → 「자동 · 내각 155° · 법정 하한 12m」
곡선끔 → 「곡선 없음 — 직선이 그대로 꺾입니다」 · 상태줄 「곡선 지움 1곳」
곡선켬 → 「자동」으로 복귀
R 40 → 「값 지정」 · 상태줄 「R 지정 1곳」
자동 → 값 비고 「자동」으로 복귀
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
사용자 지시(2026-09-07) — 「r과 직선 삭제나 추가가 있어야 하지 않을까?」
서버 쪽(f59932de)에 이어 화면을 붙임.
- **곡선 시작·끝점을 네모로 표시** — 어디부터 어디까지가 한 곡선인지 눈으로 봄.
- 꺾임점을 누르면 **고른 자리**가 되고 아래 편집줄이 그 곡선을 만짐.
- **R 입력칸** — 값을 넣으면 그 반지름으로 못박고, [반지름 자동]으로 되돌리면
서버가 예정노선에 맞춰 다시 고름.
- **[곡선 지우기] / [곡선 넣기]** — 지운 자리는 가운데가 빈 동그라미로 보이고
직선이 그대로 꺾임.
- 직선 삭제·추가는 원래 있던 조작 그대로(오른쪽 클릭 삭제 · 두 번 클릭 삽입).
다만 그때 **편집값 목록도 같은 자리에서 밀어 준다** — 안 그러면 뒤 노드의
R·켬끔이 한 칸씩 어긋남.
- [확인]이 정점마다 `curve`·`radius_m` 을 함께 보냄.
상태줄에 「곡선 N곳(하한 R m) · 곡선 지움 N곳 · R 지정 N곳」을 보임.
tsc --noEmit 통과 · pytest 427 passed.
남은 것 — **곡선 시작·끝점을 끌어 직선 각도와 R 을 함께 바꾸는 조작**은 다음 단계.
지금은 표시까지이고, 자리 옮기기는 꺾임점 끌기로 함.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
사용자 지시(2026-09-07) — 「r과 직선 삭제나 추가가 있어야 하지 않을까?」
편집을 **꺾임점 목록 + 자리마다 곡선 켬끔 + 반지름** 셋으로 표현함.
· 직선 삭제·추가 = 목록에서 점을 빼거나 더하기(빼면 앞뒤 직선이 하나로
합쳐지고, 직선 위에 더하면 둘로 갈리며 그 자리에 곡선이 생김)
· 곡선 삭제·추가 = `curve` 를 끄고 켜기(끄면 직선이 그대로 꺾임)
· 반지름 변경 = `radius_m` 못박기(없으면 서버가 고름)
`POST /route/replan` 의 정점마다 `curve`·`radius_m` 을 받게 함.
편집값이 오면 **묶지 않음** — 사용자가 곡선 하나로 본 것을 임의로 합치면
손잡이가 사라지기 때문. 편집값은 점과 짝이라 중복 제거도 함께 함.
시험 tmp/tests/test_route_polyline_edit.py 7건 — 곡선 삭제·추가, 반지름
못박기, **반지름을 바꿔도 교각점은 안 움직임**(「주변 직선 각도 구속」),
직선 삭제·추가. pytest 427 passed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
PLAN 3-6 「설치 측을 사용자가 고르게 할지(폼에 칸 추가) 뒤에 정할 것」 — 2026-09-07
사용자 확정 「폼 선택은 가능하게 반영하고 나중에 선택 비활성화로 하자」.
- 레지스트리 C군 5종(옹벽·돌쌓기(찰)·돌쌓기(메)·흙막이·큰돌쌓기)에 `side` 선택 칸.
선택지 「자동(성토 쪽)·좌·우」, 기본은 자동 — 종전 동작(성토 나는 쪽 자동)과 같음.
- 기하는 이미 그 값을 봄 — `B06_Section_UI_Cross_Revetment.ts:168` 이 "좌"/"우" 면 그쪽,
아니면 `fillSide()`. 배관(`common_util_structure_walls.ts:80`)·파이썬 짝도 `options.side`
를 이미 나름. 즉 배선은 있고 **칸만 없었음**.
- 옵션 칸에 `enabled` 를 둠(기본 참). 거짓이면 폼에 **회색으로 남고** 못 고르며 필수 검사
에서도 빠짐 — 칸을 지우지 않고 잠그는 자리(사용자 「나중에 비활성화」).
자체검증 — 레지스트리 5종 모두 `(['자동(성토 쪽)','좌','우'], 기본 '자동(성토 쪽)', enabled True)`
로 읽힘. 실화면 B06 C군 폼에 「설치 측」 칸이 그 세 선택지로 뜸(값 자동). tsc 통과.
시험 499 통과 — 실패 1·오류 8 은 모두 `require is not defined in ES module scope` 인
JS 도우미 건으로 내 변경과 무관(도우미 파일 날짜 8/27·9/2, 다른 환경에서 만든 것).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
사용자 지시(2026-09-07) — 「내각 155 이상이면 곡선 생략은 반영하지 말자.
사용자가 계획 평면 노선을 수정할 때 문제가 될 것 같아.」
곡선이 있다 없다 하면 잡는 손잡이도 있다 없다 하고, 조금 폈다는 이유로
곡선이 사라지면 되돌릴 길이 없음. 그래서 별표2 의 155° 생략 규칙을 쓰지 않고
꺾이는 자리에는 전부 곡선을 둠. 상수는 법정 근거를 잃지 않게 남겨만 둠.
실측(용화 예상노선 331점) — 곡선 13 → 26곳, 정점 145 → 181,
반지름 12~28 → 12~199m, 내각 109~177°.
벗어남은 그대로 최대 3.57m · 평균 1.081 → 1.102m (원본 충실도 유지).
시험 하나를 뒤집음 — 「펴진 자리는 곡선을 안 둔다」 → 「펴진 자리에도 곡선을
둔다」. 아주 완만하므로 원본과 거의 겹치는 것도 함께 확인.
pytest 420 passed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
PLAN 3-6 (2026-09-07 사용자 지시 5 「A군뿐 아니라 구조물 전체를 넣을 수 있어야 함」).
원인은 「B06 이 A군만 받는다」가 아니라 **레지스트리의 `enabled:false`** 였음 — B군 6종·
F군 2종·G군 1종이 목록에서 통째로 빠져 있었고, 두 화면이 같은 목록을 쓰므로 B05·B06 이
똑같이 A/C/D/E/G/기타 여섯 군만 보였음. 레지스트리 주석(2026-08-17)이 그 셋을 「B05
선택지에서 빼고 **B06 개별 횡단도 옵션으로 재사용**」이라 적어 둔 자리임.
- `fetchStructureTypes(includeDisabled)` 로 갈래를 둠. B06 만 참으로 부름 — B05 는 종전대로.
- 함께: 3-5 ⓑ 되돌릴 자리 주석(단 2개 이상일 때만 이어 붙이는 한 줄) 표시.
자체검증 — 공용 브라우저에서 두 화면 군 목록 대조:
B06 A·**B**·C·D·E·**F**·G·기타 (8) / B05 A·C·D·E·G·기타 (6, 그대로)
B06 에서 B군 6종(측구·산마루측구·소단측구·도수로·절토사면 배수로·맹암거)과
F군 2종(야생동물 이동통로·비탈면 녹화)이 실제로 목록에 뜸. tsc 통과.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>