Files
Aislo/docs/raw/guidelines/worklessons.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

698 lines
53 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 일하는 법 · 검증 함정 — 겪은 것에서 (2026-09-07 ~ 09)
> **상시계획서 `PLAN.md` 「참고」에서 옮긴 원문임. 줄이지 않았음.**
> 옮긴 까닭 — 계획서의 「참고」는 **코드 작업 중 필요한 기준표·확정사항만** 두기로 되어 있는데
> 여기 든 것은 기준표가 아니라 **일하는 법**임(2026-09-09 사용자 지시 「반영」).
> **다음 세션이 계획서보다 먼저 읽을 것.** 계획서 「참고」에는 이 파일 링크만 남겼음.
>
> 든 것 — 일하는 법 열둘 · 좁은 폭 화면 깨짐 · vite 캐시 · 공용 이름 바꾸기 ·
> [저장]과 횡단 재계산 · 공용 브라우저 조작 주의 셋 · 검증 함정 모음.
### ⚠⚠ 일하는 법 넷 — 2026-09-07 두 창이 겪은 것에서 (다음 세션이 먼저 읽을 것)
이 절의 값어치는 **같은 사고를 다시 겪지 않는 것**이다. 넷 다 하루에 여러 번 나왔다.
#### ㉠ 「숫자가 늘어난 것」은 좋아진 신호가 아니다
배분율 표 25건이 살아났으나 그게 **10 % 짜리**였고, 2단 표 165줄이 늘었으나 상당수가
오독이었다. **늘어난 뒤에는 반드시 최대·중앙값과 손대조를 볼 것.**
(그래서 산출 요약에 최소·중앙·최대를 붙였다 — 8-22 ③.)
#### ㉡ 넓은 규칙이 정상 값을 지운다 — 하루에 **여덟 번** (두 창에서 넷씩)
① 배합 검사가 `막자갈`을 오탐 ② 자원 필터가 정상 자원 70/745 를 지움 ③ 비율 지시를
먼저 봐 강관동바리 소요량표가 참조로 넘어감 ④ 머리글 필터가 등급 글자(상·중·하)를 지움
⑤ 「당」을 느슨히 잡아 `(무한궤도,0.7㎥)` 를 밑수로 읽음 ⑥ 식 칸 집계가 비고 설명문까지
걸어 93 → 31 로 좁힘 ⑦ 「값 못 읽은 줄」 판정이 **값이 없는 게 당연한 머리 줄**까지 세어
28 → 12 ⑧ 표 안 긁기가 비고의 「10㎡」를 그 표의 밑수로 물어 옴.
**`REFERENCE_MARKS` 의 「단 위」 하나가 공종 41건을 참조로 버림** — 그 안에
**돌쌓기(장비) 13-4-5·13-4-2**(우리 매핑이 매일 쓰는 코드)가 있었다. 규모가 가장 컸다.
⑩ 자간 공백 이름 세기가 「단 위」·「모 래」까지 걸어 101 → 20 으로 좁힘.
**⚠ ⑦ 은 이 병을 다섯 번 잡아 놓고 새로 하나 만든 것**이다. 원인은 **짝 시험을 그 자리에
같이 안 둔 것**. 세기 전에 **「이건 세면 안 된다」 쪽 표본을 먼저 정해 둘 것.**
**⚠ ⑨ 의 진단이 이 절의 처방이다** — 그 표지의 원래 겨냥(품셈 1-2-2 「단위 표준」)은
**chapter 규칙이 이미 거르고 있었다.****넓기만 하고 얻는 것이 없었다.**
**⇒ 넓은 규칙을 만나면 그것이 실제로 무엇을 **더** 잡는지 세어 볼 것 — 다른 규칙과 겹치면
얻는 것 없이 잃기만 한다.** 규칙을 지울 근거는 「안 잡히는 것」이 아니라 **「이미 다른 규칙이
잡고 있는 것」**이다.
**⚠ 정규화는 값을 살리지만 잘못된 줄도 함께 살린다** (2026-09-07 서브 창 실례).
기종 이름 공백을 지워 빠졌던 장비 몫을 살렸더니, **같은 표의 버킷계수 `K` 를 소요량으로 읽어**
시간당 사용료가 이중이 될 뻔했다. **살리는 규칙에도 짝 시험을 둘 것.**
**⇒ 규칙을 새로 둘 때마다 「걸려야 한다」와 「걸리면 안 된다」 짝 시험을 함께 둘 것.**
**⇒ 값을 지우는 규칙은 좁게, 표시만 하는 깃발은 넓게.** 깃발은 넓어도 값이 안 사라진다
(`partial_ratio` 를 몫 유무로 넓게 잡은 판단이 그것).
#### ㉢ 검사가 도는 줄 알았는데 안 돌고 있었다 — 하루에 네 번
① 가드를 만들어 두고 안 부름 ② 지문 장치가 원판 지문을 봐서 안 움직임
`npx tsc`**엉뚱한 패키지를 실행**(이 저장소는
`node ./config/node_modules/typescript/bin/tsc --noEmit` 로 부를 것)
④ 사방공에 **없는 `type_id`** 를 적어 영영 안 걸릴 뻔함.
**⇒ 새 검사·장치는 일부러 깨뜨려 보고 실제로 잡히는지 확인할 것.**
**⇒ 이름으로 무언가를 알아보는 코드는 정본(레지스트리·마스터)과 대조하는 시험을 둘 것.**
#### ㉤ ⚠ **먼저 원문부터 뒤진다** — 「관측값인 줄 알았는데 법이었다」가 **여덟 번**
여덟 다 「실무 관측값을 쓰자」·「사용자에게 물어야 한다」로 시작했다가 **품셈 원문에 이미
정해져 있는 것**을 찾았다. 뒤 여섯은 **2026-09-08 재조사에서 네 창이 동시에** 건졌다.
| 물음 | 처음 생각 | 원문 |
|---|---|---|
| 거푸집을 몇 번 쓰나 | 관측값 병용 | **품셈 1-7-1** — 「3회 … 옹벽, 파라펫트, 날개벽」 |
| 철근 갈래가 무엇인가 | 사용자에게 물음 | **품셈 12-3 [주]①** — 「보통: 수문, 반중력식 옹벽 및 교대」 |
| 덤프 t↔㎥ 환산 γt | 지식DB 에 없음 | **품셈 10-12-1 [주]②** — 토사 1.9 · 암 2.4 t/㎥ |
| 공구손료 밑수·제잡비와의 관계 | 두 문서가 어긋남 | **건설품셈 1-2-6** — 「명시된 것은 그것을 계상, 명시 안 된 것만 별도」·「인력품의 3%까지」 |
| 야면석 개수·중량 원단위 | 실무값밖에 없음 | **품셈 13-4** 「돌쌓기의 개수 및 중량의 표준」 — 0.575 / 0.88 / 1.10 |
| 물빼기 구멍 간격·지름 | 사용자에게 물음 | **품셈 13-4-4 [주]④⑤⑦** — 2~3㎡ 마다 Ø3~6㎝ · 모르터 0.009 |
| 돌쌓기 **전면 기울기** | 사용자에게 물음 | **품셈 13-4-4 「표준경사」 표** — 직고×메찰×성절토 20칸 |
| 돌쌓기 **뒷길이** 규격 | 우리 표 네 칸이 전부 | **품셈 13-4-4 「뒷길이 표준」 표** — 일곱 규격, 높이별 |
**⇒ 「이건 관측값밖에 없다」·「이건 사용자가 정할 값이다」로 넘어가기 전에 품셈 원문
본문(표가 아니라 **표 앞뒤 [주]·안내 줄**)을 먼저 볼 것.** 밑수도 같은 자리였다 —
표 안이 아니라 표 바로 위 본문에 있었다.
**⇒ 「지식DB 전수 확인: 없음」은 원문까지 안 보면 절반만 본 것이다** (2026-09-08 넷으로 굳음).
지식DB 는 **요약·후보 가이드**라 원문의 [주]·부속 표가 옮겨지지 않은 자리가 많다.
**조사 보고에 「원문 어디까지 봤나」를 함께 적을 것** — 파일명·줄번호가 없으면 「안 본 것」으로 친다.
**⇒ 원문에 표가 있으면 물음이 「값」에서 「축」으로 바뀐다.** 전면 기울기가 그랬다 —
높이·메찰·성절토를 우리가 이미 아니까 **값은 자동으로 정해지고**, 남은 물음은
「품셈 축(높이·성절토)과 교본 축(시설 종류) 중 어느 쪽이냐」 하나였다.
**⇒ 판정할 수 있는 것을 사람에게 물으면 그것이 곧 미결이 된다.** 물음 목록이 길어질수록
사용자가 답할 수 있는 물음이 묻힌다. 실제로 「철근 갈래 전체」를 물으려던 것이 원문 확인 뒤
**「캔틸레버식 옹벽 하나」로 좁아졌다.**
#### ㉪ ⚠ 계약이 바뀌면 시험부터 의심할 것 — 이틀에 **열두 번**
**2026-09-08 네 번** (덧곱 · 구조물 한 줄 · 사방공 이름 · 뒷길이 접기)
**2026-09-09 여덟 번** — 사용자 확정을 붙일 때마다 **옛 시험이 옛 계약을 지키고 있었음**:
```
버림 콘크리트 「없음」을 못 박은 둘 (구조물 한 줄 · 큰돌쌓기 성분 집합)
층따기 「막힌다」를 못 박은 둘
기울기 「늘 1:0.3」을 못 박은 셋 (실무 시트 대조 시험 포함)
stone_supply 기본값을 「근거 없음」으로 잡은 둘 ← 근거를 적어 예외로 뺌
```
**사용자 확정이 오면 「어느 시험이 옛 계약을 지키고 있나」를 먼저 찾을 것.**
**검사를 눅이지 말 것** — 근거를 문장으로 적어 **예외로 빼는 것**과, 검사 자체를 약하게 만드는 것은 다름.
**옛 시험이 틀린 계약을 못 박고 지키고 있던 자리.** 실물:
```python
def test_표에_없는_뒷길이는_덮는_칸으로_접을것():
assert _back_length({"back_len_cm": "25"}) == 35
assert _back_length({"back_len_cm": "75"}) == 60
```
**「접는 것」이 옳다고 시험이 지키고 있었음.** 그런데 **접을 까닭이 아예 없었음** — 품셈이
일곱 규격(25·30·35·45·55·60·75)을 다 주는데 **우리 표가 네 칸만 들고 있었을 뿐**임
(실무 라이브러리가 네 칸만 옮겨 적은 것을 그대로 받았음).
**시험이 있다는 것이 「맞다」는 뜻이 아님.** 값이 나오면 아무 시험도 안 잡음(㉣ 과 한 벌).
**곁들여 같은 자리에서 나온 것 둘**
-**원문이 「-」인 칸에 0 줄을 만들지 말 것** — 25㎝ 고임돌이 **0.0 으로 서고 있었음**.
**0 은 「없음」과 구별이 안 되고** 받는 쪽이 「값이 0 인 자재」로 읽음. 줄을 빼고 사유를 낼 것.
-**고른 갈래의 빈 칸은 빈 칸으로 덮을 것**`None` 이라고 안 덮었더니
**「야면석 75㎝」에 종전 값(깬돌 0.25)이 조용히 섰음.**
#### ㉦ ⚠ 저장 제원의 키 이름과 엔진이 읽는 키 이름을 대조할 것
레지스트리는 `back_len_cm` 인데 엔진이 `stone_back_length_cm` 을 읽고 있어 **저장값이
영영 안 닿고 늘 기본 45㎝ 로 돌았다.** 사용자가 뒷길이 75 를 골라도 45 계수가 붙었다.
**⇒ 오늘 잡은 「조용히 틀린 값」 가운데 사용자가 고른 값이 직접 버려진 첫 사례다.**
앞의 것들은 우리가 원문을 잘못 읽은 것이었는데 이건 **사용자 입력이 무시된** 것이라
성격이 더 나쁘다.
**⇒ 「이름으로 알아보는 코드는 정본과 대조하라」(㉢)의 데이터 키 판이다.** 대조를 시험으로
둔다(`test_b08_option_keys.py`). **「칸이 아예 없는 것」과 「이름이 어긋난 것」은 다른 것**이라
가려 적는다 — 앞은 「칸이 생기면 받는다」이고 뒤는 **버그**다.
#### ㉥ 폴더 튐은 「내 것이 없어진 것처럼 보이는」 착시까지 만든다
2026-09-07 두 창이 한 저장소에서 일하며 **폴더 튐이 네 번** 났다. 그중 하나는 상대 창이
**남의 폴더 파일을 읽으며 「내 코드가 사라졌다」**고 판단한 것이었고, 자기 폴더에서는 멀쩡했다.
**⇒ 「내 변경이 사라졌다」고 느끼는 순간 먼저 현재 폴더부터 확인할 것.** 그 상태에서
「복구」에 손대면 **없어지지 않은 것을 되살리려다 멀쩡한 것을 덮는** 진짜 사고가 된다.
**⇒ push 전에 `git status` 로 남의 파일이 섞였는지 볼 것**(OWNERS 규칙 2). 남의 파일이
보이면 **경로를 지정해 커밋**하고 자동 스크립트를 쓰지 말 것 — 폴백이 주워 담는다.
#### ㉣ 「값이 있기는 하니 안 보이는」 것이 가장 위험하다
씨앗뿜어붙이기 합계 68.8원 · 측구터파기가 인력 10 % 몫만 · 운반 표가 [확정] 뒤 0줄 ·
부분 성공한 표가 엉뚱한 직종에 붙음. **부분 성공이 완전 실패보다 위험하다**
실패는 눈에 띄고 부분 성공은 안 띈다.
**⇒ 「0 이 아님」이 아니라 「크기가 말이 되나」로 잴 것.**
**⚠ 그 반대 짝도 있다 — 「값이 맞는데 이상해 보이는」 경우.** 반영률이 전부 100 % 라
성토면다짐과 층따기가 같은 수로 나온 것을 「곱하기가 빠졌다」로 오진한 일이 있었다
(두 계열이 같은 사면 면적에서 나오므로 원래 같다).
**⇒ 「두 값이 같다」를 만나면 코드보다 설정·입력을 먼저 읽을 것.**
#### ㉧ 양쪽 근거가 다 있으면 **한쪽으로 몰아 보고하지 말 것** (2026-09-08)
교차검토에서 「제잡비 3% 의 밑수에 굴착기 조종원 노임이 들어가는가」가 나왔음.
산림품셈 13-6-2 [주]③ 은 「**노무비의 합계액**」이라고만 하고 그게 무엇인지 정의를 안 함.
근거가 양쪽에 다 있었음 —
| 쪽 | 근거 |
|---|---|
| 사람 품만 | 건설품셈 제8장이 같은 「제잡비율」을 **「직접노무비」로 못 박음**. 기계를 밑수에 넣을 때는 **「노무비, 기계손료 및 운전경비의 합」이라 따로 적음** |
| 노무비 계정 전체 | 우리 원가 체계에서 「노무비」와 「직접노무비」는 **다른 말**이고, 중기 운전사 노임도 같은 노임표에서 옴 |
한쪽만 실어 보냈으면 받는 창이 그대로 받았을 것임. **양쪽을 나란히 놓자 조율 창이
자기 앞 판정을 뒤집었음**(「사람 품만」으로 확정, 미결 ㉮ 로도 올림).
**⇒ 판단은 받는 쪽이 함. 반대 근거가 없으면 판단 자체가 안 됨.**
**⇒ 문구가 갈리는 자리는 「같은 규정이 다른 데서는 어떻게 쓰였나」를 볼 것** — 이 건은
**「넣을 땐 넣는다고 쓴다」**가 갈랐음.
**⇒ 그리고 그 조항이 무엇을 사는 값인지 읽을 것** — 제잡비는 「콘크리트 버켓 손료·다짐기계
손료」, 즉 **본 자원에 안 선 잔 기계**임. 그 표에 굴착기는 이미 본 자원으로 서 있으므로
잔 기계를 **그 굴착기 조종원 노임에 비례**시키는 것은 뜻이 어긋남.
**⚠ 이 자리는 어떤 시험도 안 잡았음** — 값이 나오고 자원 넷이 원문과 정확히 맞았음.
**손대조로 차액을 역산**해서야 밑수가 드러났음(㉣ 「크기가 말이 되나」의 한 단 아래).
**⇒ 그리고 「없다」를 확정하기 전에 「어디에 없나」를 볼 것** (2026-09-08 ㉗).
품셈에 없다는 것이 **답이 없다는 뜻은 아님.** 같은 날 둘이 그랬음 —
**지장목제거**는 품셈에 공종이 없을 뿐 **수량은 법령이 요구하고 실무가 같은 산식으로 세고 있었음**,
**입목 본수**는 조사값이 아니라 **우리 저장소가 재료(원본 LAS·다중 리턴)를 이미 갖고 있었음**.
**⇒ 찾을 자리는 셋임 — 품셈 · 다른 문서(법령·교본·실무) · 우리가 이미 가진 것.**
**⇒ 값이 움직였는데 코드 diff 에 없으면 **남의 창을 먼저 볼 것** (2026-09-08).
**DB 는 원격 공용 서버 한 대**(`dsm.chemifactory.com`)라 네 환경이 같은 자료를 쓴다.
어느 창이든 [확정]·[저장]을 부르면 나머지 셋의 값이 그 자리에서 바뀐다 — 코드를 하나도
안 건드려도 바뀐다. 실제로 성토가 +1.14 % 움직였고 원인은 랩탑의 실화면 검증이었다.
`cross_sections` 표에 **시각 칸이 없어** 언제·누가 바꿨는지 DB 로는 못 짚는다.
짚는 순서는 ① 커밋 diff 로 코드 배제 ② 서버 로그로 내 창 배제 ③ DB 재계산으로 「후」 값 확인
④ 파일 mtime 으로 시각 잡기. **되받기 전후로 `tmp/snapshot_b08.py` 를 떠 두면 대조가 된다.**
#### ㉩ ⚠ md 에 없으면 원본(pdf·xlsx)을 열어 볼 것 (2026-09-08)
**변환본이 원본을 못 따라가는 자리가 있음.** 「돌쌓기 표준도 그림이 저장소에 없다」고 판단해
원본 확보를 요청하려 했는데, **PDF 원본(173쪽)이 이미 저장소에 있었음.** md 로 옮길 때
**그림 폴더를 안 받아 둔 것**이었고, PDF 에서 그대로 뽑아 넣으니 끝났음.
- 오늘 같은 계열이 셋임 — **표가 코드를 못 따라감**(8-25 화면 칸이 낡음) ·
**내 출력이 실물을 못 따라감**(옵션 넷만 골라 찍고 「빠졌다」로 오해) ·
**변환본이 원본을 못 따라감**(이번).
-**「없다」를 말하기 전에 원본 형식(pdf·xlsx·las)을 한 번 볼 것.** 특히 **그림·서식·병합셀**은
텍스트 변환에서 빠지기 쉬움.
#### ㉨ ⚠ 「사유」 칸에는 왜 막혔나만 — 설명은 비고로 (2026-09-08, 하루에 **세 번**)
**받는 쪽은 사유 칸을 「상태」로 읽는다.** 설명을 거기 적으면 그 줄이 통째로 막힌 것이 된다.
- **겹침 설명을 `blocked_reason` 에 넣었더니** B09 가 「막힌 줄」로 읽어 금액을 안 붙였음.
- **`blocked_kind: None`** — 「다른 표에서 이미 섬」은 막힌 것이 아니라 **여기서 세면 안 되는 줄**임.
막힘으로 보내면 받는 쪽이 「만들어야 할 것」에 얹어 **결국 이중계상으로 감.**
- **사유를 뭉뚱그리면 할 일이 갈리지 않음** — 「일위대가 없음」 하나로는
「아직 안 만든 것」과 「성분이 빠져 못 세운 것」이 구분되지 않음. 앞엣것은 우리가 만들면 되고
뒤엣것은 **참조 대상을 이어야** 풀림. 사유를 갈라 적으니 다음 일감이 바로 보였음.
**⇒ 사유 칸은 「왜 막혔나」만. 설명·계산 경위는 비고로. 그리고 사유를 갈라 적을 것.**
#### ㉫ ⚠⚠ **법이 두 동작을 묶은 자리 — 앞 동작만 세고 있지 않은지** (2026-09-09, 하루에 **두 번**)
**막힘 목록에도 없고 어떤 검사에도 안 걸리는 자리임.** 줄이 서 있고 값도 맞으니
**「없는 줄」을 아무도 못 봄.** 오늘 둘 다 별표2 원문을 읽다가 나왔음.
```
표토 별표2 「표토는 전량 **제거한 후** … 최고 홍수위보다 높은 장소로 **운반하고 쌓아두어야**」
우리 — 표토제거(9-15) 한 줄만. **운반·적치가 없었음**
뿌리 품셈 9-20 가. 「벌개·제근 → 뿌리다듬기 → **적재** → **운반**」 (시공 과정 표준)
별표2 「입목…과 **그 뿌리**, 표토는 전량 제거한 후 … 운반하고 쌓아두어야」
우리 — 「제근·뿌리다듬기」 한 줄뿐. **적재·운반이 없었음**
```
**⇒ 어떻게 찾나** — 원문에서 **동사가 둘 이상 이어진 문장**을 볼 것.
`제거한 **후** … 운반하고 쌓아두어야` · `~하고 ~하여야` · `→ 로 이어진 과정 표준`.
**앞 동작에 우리 줄이 있으면, 뒤 동작에도 줄이 있어야 함.**
**⇒ 찾은 뒤 지킬 것 셋**
- **물량을 다시 세지 말 것** — 뒤 동작의 밑수는 **앞 동작의 물량 그대로**임(표토 운반 = 표토 제거량).
다시 세면 이중계상.
- **앞이 안 서면 뒤도 안 섬** — 밑수가 그 줄이므로 함께 막히게 할 것.
- **법 문구를 사유에 실을 것** — 화면에서 **왜 이 줄을 세는지**가 보여야 함.
**품셈에 그 공종이 없을 수 있음**(뿌리 운반이 그랬음 — 10장 어디에 붙는지 원문이 안 말함).
그때도 **줄은 세우고 코드만 비울 것.** 지어내지 말고 사유로. → 아래 ㉬.
#### ㉬ ⚠ **「수량은 서는데 품셈에 공종이 없는 줄」은 한 물음임 — 모아서 한 번에 물을 것** (2026-09-09)
따로 만나면 매번 새 물음처럼 보이나 **답은 하나임.**
```
뿌리 운반 9-20 가. 가 과정으로 두었는데 붙을 공종이 10장에 없음
도수로·절토사면 배수로 「수로」 전수 검색에 셋뿐 — 그 셋에 없음
지장목제거 수량은 서는데 단가 공종이 없음
부대시설 넷 국가지점번호판·안내판·차단기·수방자재 — 개소를 넣어도 단가 없음
```
**⇒ 선례가 답을 정해 둠 — 확정 8-4** 「편책은 바자얽기(5-15)를 **빌려 쓰지 않음. 일위대가를 따로
만들어 연결**」. 즉 **품셈에 없으면 우리가 만들어 잇는다**가 사용자 방침임.
**빌려 쓸 후보가 있어도 답이 「만들라」일 수 있음** — 편책이 그랬음. **후보 유무로 미리 가르지 말 것.**
**⇒ 물을 때는 한 표로** — `공종명 | 수량은 서나 | 빌려 쓸 후보 | 없으면 새로 만들 성분`.
넷을 따로 물으면 사용자가 네 번 답해야 하고, 그 사이 창마다 다르게 처리함.
#### ㉭ ⚠⚠ **대조표를 인용하기 전에 「그것이 언제 것인지」부터 볼 것** (2026-09-09, 한 건이 **세 번** 뒤집힘)
**오늘 가장 비싼 헛걸음임.** 구조물 터파기 한 줄을 두고 조율 창·데스크탑 메인·랩탑 보조가
**세 바퀴**를 돌았고, **뿌리는 「오전에 뜬 대조표를 오후에 그대로 인용한 것」** 하나였음.
```
대조표(오전) 「우리 2.30」 = 2.0 × (0.95 + 0.2) ← 옛 두께식 · 기초 몫 없던 때
그 사이 바뀐 것 확정 ② 두께 → 뒷길이 기반 (`8141eb34`)
확정 3차 → 기초유/기초버림 축, 기초 몫 0.45·0.07 이 붙음 (`b4ae32d1`·`759343e3`)
실제(오후) H=2.0 · 두께 0.83 · 기초유 → **2.510** = 실무 정본 2.51 과 자릿수까지 같음
```
**⇒ 그 낡은 2.30 하나가 만든 잘못된 결론 셋**
```
1. 「우리가 6.6배 과다 — 955만원을 줄일까요」 ← 사용자에게 갈 뻔한 틀린 물음
2. 「기초 몫 0.45 를 빠뜨렸다」 ← 이미 들어 있었음
3. 「화면 0.5 와 수량 H 가 다른 말을 한다」 ← 셋이 같은 말을 하고 있었음
```
**⇒ 지킬 것**
- **대조표·실측표를 인용할 때 날짜와 커밋을 함께 적을 것.** 「오늘」은 날짜가 아님 — 하루에 두 번 바뀜.
- **금액이 걸린 인용은 그 자리에서 다시 재 볼 것.** 재는 데 몇 분, 헛걸음은 세 창 몇 시간.
- **표를 만들 때 머리에 「잰 시각 + 그때 커밋」을 박아 둘 것.** 표가 스스로 낡았다고 말하게.
-**이 병은 「값이 틀린 것」이 아니라 「값이 옛것인 것」이라 어떤 검사에도 안 걸림.**
시험도 통과하고 숫자도 맞음 — **그때는** 맞았기 때문.
**⇒ 곁들여 나온 것** — 그럴듯하게 맞아떨어지는 설명이 **같은 건에서 두 번** 틀렸음
(`0.7 × 0.5 = 0.35` · 「H × 평균두께」 모양 일치). **딱 맞아떨어질수록 곱셈을 해 볼 것.** → ㉧
**⇒ 같은 집안 하나 더 — 「잘라서 인용한 자리」를 「없는 자리」로 읽지 말 것** (2026-09-09).
규준틀 사유를 **44자로 잘라** 보고하다 **「아무 표시가 없다」**로 냈음. 실제로는 그 뒤에
「재료량은 품셈 11-2 [주]④ 「설계수량에 따른다」라 미확보」가 **적혀 있었음.**
**길이를 줄여 인용할 때는 「…(잘림)」을 붙일 것.** 안 붙이면 **자기가 자른 자리를 사실로 읽음.**
⇒ 그리고 **「아무도 말 안 함」과 「이쪽만 말 안 함」은 다름** — 보내는 쪽이 이미 말하고 있을 수 있음.
#### ㉮ ⚠⚠ **시험이 「지금 동작」을 베껴 적으면 버그를 잠근다** (2026-09-09, 실물 한 건)
**제근 단가가 두 배로 서 있던 것을 시험이 「맞다」고 지키고 있었음.**
```
있던 시험 「제근은 보통인부 줄이 **둘**이다」
실제 그 둘이 곧 이중계상 — 품셈은 「0.2㎥를 쓰면 이 값 **또는** 0.7㎥를 쓰면 저 값」
⇒ 시험이 버그를 계약으로 못 박아, 고치려 하면 **시험이 먼저 막아섬**
```
**⇒ 어떻게 생기나** — 시험을 쓸 때 **원문을 안 보고 지금 출력을 베껴** 적으면 이렇게 됨.
「돌려 보니 둘이 나오네」 → 「둘이어야 한다」. 그 순간 **틀린 값이 정본이 됨.**
**⇒ 지킬 것**
- **시험의 기대값은 「돌려 본 결과」가 아니라 「원문」에서 올 것.** 기대값 옆에 **근거를 한 줄** 적을 것
(품셈 절 번호·실무 시트 셀 좌표). 근거를 못 적겠으면 **그 시험은 아직 쓰면 안 되는 것**임.
- **개수를 세는 시험**(「줄이 둘이다」·「성분이 N개다」)이 가장 위험함 — 값이 아니라 **모양**을 잠그는데
모양이 틀렸을 때 그것을 알려 줄 것이 없음.
- **뒤집을 때는 「왜 뒤집혔는지」를 그 시험 설명에 남길 것.** 안 남기면 다음 사람이 되돌림.
**㉪(계약이 바뀌면 시험부터 의심할 것)과 다른 병임.** ㉪는 **시험이 낡은 것**이고,
이것은 **시험이 처음부터 틀린 것**임. 낡은 시험은 계약이 바뀔 때 깨져서 드러나는데,
**처음부터 틀린 시험은 영영 안 깨짐** — 지키는 대상이 실제로 그렇게 돌고 있으므로.
#### ㉯ ⚠ **검산은 「어디까지 쟀는지」를 함께 적을 것** (2026-09-09)
뒤채움 두께가 품셈 범위 안에 드는지 재면서 **H 4.0m 까지만 재고 「세 자리 다 범위 안」**으로 냈음.
그물을 시험으로 옮겨 **H 6.0 까지** 늘리자 **4.0~4.5 사이에서 범위 밖으로 나감**이 바로 걸렸음.
```
우리 두께 직고에 0.30/m 로 **계속** 붙음 (확정 ② 실무 정본 식)
품셈 표 하부 두께가 **1.40m 에서 멈춤** ([주]⑨ 직고 7m 칸 상한)
⇒ 높이 축의 기울기가 다름. 어느 쪽도 상대를 틀렸다고 못 함 — **갈리는 자리를 값으로 남길 것**
```
**⇒ 「범위 안이다」는 잰 범위 안에서만 참임.** 시험 이름·주석에 **잰 구간**을 박을 것.
**⇒ 그리고 범위 검산은 등호로 바꾸지 말 것** — 밖으로 나가면 「틀림」이 아니라 **「봐야 함」**임.
우리 식이 맞을 수도 있음.
#### ㉰ ⚠⚠ **표를 읽을 때 칸을 이어 붙이면 「원문에 없는 값」이 만들어진다** (2026-09-09)
**앞 칸의 숫자 + 뒤 칸의 단위가 붙어, 원문 어디에도 없는 밑수가 섰음.**
```
품셈 13-2-4 … 0.28 | **0.36** **㎥당** | 0.60 … ⇒ 「0.36㎥당」 (0.36 은 뒷길이 60 의 ㎡당 값)
품셈 5-19-2 15 | **30**(표토두께 ㎝) **㎥ 당** | … ⇒ 「30㎥당」 (30 은 두께)
```
**잘못된 값이 아니라 「없는 값」임.** 그리고 **두 숫자 다 그 표 안에 실재**해서
**그럴듯함 — 어떤 검사에도 안 걸림.**
**⇒ 지킬 것**
- **표를 훑을 때 「칸 하나 안에서만」 볼 것.** 셀을 죽 이어 붙여 정규식을 걸면 이 병이 남.
「100㎥당」처럼 **한 칸에 다 든 것**만 인정.
- **밑수가 늘어난 것은 좋아진 신호가 아님** — 이 건에서 밑수 확보가 **182 → 180 으로 줄어든 것이
개선**이었음(없는 값 둘이 빠지고 넷이 「미확보」로 정직해짐). → ㉠
- **잡은 실례를 그대로 재는 시험을 남길 것.** 그래야 다음에 정규식을 넓힐 때 걸림.
**같은 날 잡은 「인」 오독과 다른 병임** — 「인」은 **잘못된 단위**(원문에 있는 글자를 잘못 읽음),
이것은 **없는 숫자**(원문에 없는 조합을 만들어 냄). **고치는 자리도 다름.**
#### ㉱ ⚠⚠ **코드 추적으로만 낸 「원인」은 자료를 재 보기 전까지 가설임** (2026-09-09, 하루를 돌아감)
계획서 3-14 에 **「원인 찾음(코드 추적)」**이라 적어 두었는데 **자료를 안 재 봤고, 재 보니 틀렸음.**
```
적어 둔 원인 「관을 나중에 놓으면 그 측점이 안 생긴다」 — 측점 생성 호출부를 따라가 낸 결론
실제 측점 440.0 · 620.0 · 720.0 · 900.0 이 **이미 있었고 구조물 이름표까지 달고 있었음**
진짜 원인 관 자리와 측점 자리가 **최대 0.5m 어긋나는 것이 설계**(정수 미터 격자로 스냅)인데
**붙이는 쪽이 0.02m 로만 봐서** 안 붙었음
```
**⇒ 그 틀린 원인이 하루 동안 판단 셋을 끌고 다녔음**
```
1. 「B06 자리」로 넘겨 B08·B09 가 손을 못 댐
2. 조율 창이 **「0.05 와 0.5 를 맞추지 말라」**고 지시 — **좁은 쪽이 스냅을 모르는 것**이 진짜 문제였음
3. [측점 만들기] 단추를 **주 해법**으로 잡음 (실제로는 0개짜리 예외 처리였음)
```
**⇒ 지킬 것**
- **「원인」이라 적을 때 어떻게 알았는지를 함께 적을 것** — `코드 추적` / `실측` / `원문`.
**추적만으로 낸 것은 「가설」이라 쓸 것.**
- **가설을 남에게 넘기기 전에 한 번 잴 것.** 이 건은 **「그 측점이 정말 없는가」를 한 번 조회**하면
끝날 일이었음.
- **남이 넘겨준 진단을 그대로 이어받아 지시하지 말 것** — 조율 창이 그 위에 지시 둘을 얹었고
**둘 다 틀렸음.**
**㉭(대조표가 낡음)과 다른 병임.** 그건 **한때 맞았던 값**이 낡은 것이고,
이것은 **처음부터 안 재 본 것**임. 낡은 값은 날짜로 걸러지지만 **안 잰 것은 걸러지지 않음.**
#### ㉲ ⚠⚠ **「막혔다」고 말하기 전에 차단 표시를 볼 것 — 화면 문구는 근거가 아님** (2026-09-09, 하루에 **두 번**)
**주의 문구를 차단으로 읽어 두 번 헛돌았음.**
```
아침 겹침 설명을 `blocked_reason` 에 넣었더니 **B09 가 「막힌 줄」로 읽어 금액을 안 붙였음**
⇒ 비고 칸으로 옮기고 막힘은 **진짜 막힐 때만** 넣게 고침
밤 「관종을 안 정해 기본값(파형강관)으로 섰습니다」를 **조율 창과 B09 창이 「막는 사유」로 읽음**
실제 `blocked_kind` 는 **공종코드를 못 찾을 때만** 섬(`if code is None`).
기본값 파형강관은 코드가 있어 **줄도 서고 금액도 섬** — 그것은 `kind_note`(주의)였음
⇒ 「칸을 만들라」는 지시가 두 창에 나갔고 **칸은 이미 있었음**
```
**⇒ 지킬 것**
- **차단 여부의 근거는 `blocked_kind` 하나임.** 사유 글자·주의 문구·「미정」이라는 말은 근거가 아님.
- **막힘을 보고할 때 `blocked_kind` 값을 함께 적을 것.** 「막혔음」만 적으면 받는 쪽이 문구를 읽음.
- **화면에서도 「주의」와 「차단」을 갈라 보일 것** — 같은 자리에 뜨면 사람도 코드도 헷갈림.
-**남이 옮겨 준 사유 문구를 그대로 판단 근거로 쓰지 말 것.** 두 번 다 **문구를 옮긴 창과 받은 창이
함께 틀렸음.** → ㉱
**㉨(「사유」 칸에는 왜 막혔나만)의 반대쪽 얼굴임.** 그건 **쓰는 쪽** 규칙이고 이것은 **읽는 쪽** 규칙임.
사유 칸에 설명을 섞으면 읽는 쪽이 막힘으로 읽고, 읽는 쪽이 표시를 안 보면 안 막힌 것을 막혔다고 함.
#### ㉳ ⭐⭐ **예외는 「git 밖 시험 목록」이 아니라 「정본 스키마」에 적을 것** (2026-09-09)
**칸은 건너가는데 까닭은 안 건너감** — 그래서 **같은 판인데 창마다 시험 결과가 다름.**
```
정본 등록부 B05_Profile_Structure_Types.json git **안** ⇒ 다른 창에 감
정책 시험 tmp/tests/…_registry_policy.py git **밖**(CLAUDE.md 4장) ⇒ **안 감**
⇒ 칸을 열고 시험의 예외 목록에 까닭을 적으면 **칸만 건너가고 까닭은 안 건너감**
⇒ 연 창은 통과, 받은 창은 실패. **둘 다 맞는 상태인데 서로 틀렸다고 함**
```
**⇒ 고침 — 「예외」를 목록에서 스키마로 옮길 것.** 실제로 세운 두 갈래:
```
empty_means 「비워 두는 것이 뜻인 칸」 — 비면 계산 쪽이 기준값으로 돌고 그 사실이 화면에 뜸.
값을 넣으면 그 값이 이김. **까닭 한 줄을 값 옆에** 적음
default_basis 기본값이 **도메인 확정값이 아닐 때** 그 뜻(「다단 없음」·「안 더함」).
법정·확정 수치면 **비워 둠** — 비어 있는 것이 「확정값」이라는 뜻
```
⇒ 시험은 **목록을 읽지 않고 그 칸을 읽음.** 까닭이 한 낱말이면 잡는 시험도 함께 둘 것
(빈 문구로 통과하는 것을 막음).
**⇒ 규칙 한 줄 — 등록부에 칸을 열 때 예외 목록을 고치지 말고 까닭을 값 옆에 적을 것.**
**다만 구조를 고쳐도 「옮기는 일」은 남음** (2026-09-09 그날 저녁 실측).
스키마를 고치면 **그 스키마를 읽는 시험 파일도 바뀌는데**, 그 파일이 `tmp/tests`(git 밖)라
**여전히 안 건너감.** 실제로 한 창이 스키마를 고친 뒤 다른 창이 **옛 시험으로 2건 실패**했음.
```
⇒ 스키마를 고쳤으면 **그 시험 파일 내용을 메시지로 보내** 각 창이 자기 폴더에 놓을 것
⚠ 남의 폴더에 직접 쓰지 말 것 — 보내고, 받는 쪽이 놓음
⇒ 「내 창은 통과인데 저쪽은 실패」가 보이면 먼저 **시험 파일 판을 맞출 것**
```
흙깎기 장비 이름이 표에 없고 **[주]① 이 지정**
기초잡석 운반 표에 「덤프트럭(15ton)」 줄만 있고 시간이 빔 — **10-12 가 거리로 냄**(같은 15ton)
커플링밴드 표 「EA | (빈칸) | 필요시적용」 — **[주]① 「1EA/6m 이며 필요시 별도 산정」**
```
**⇒ 빈 칸을 만나면 그 표의 [주]·비고·앞뒤 절 참조를 먼저 훑을 것.** 셋 다 **표만 보면 「없음」**이었고
**[주]를 보면 있었음.**
**⇒ 사유 문구를 갈라 쓸 것 — 다음 사람이 볼 때 뜻이 아주 다름**
```
「원문에 값이 없음」 ⇒ 영영 막힌 것으로 읽힘. **지어내지 말라는 뜻**
「[주]에 있음 — 조건이 붙음」 ⇒ 조건만 정하면 서는 것
「다른 절이 냄 — 거리/규격 필요」 ⇒ 그 값이 오면 저절로 섬
```
⚠ 셋을 뭉쳐 「값 없음」으로 적으면 **사용자가 없는 것을 정하러 감.** → ㉲
⚠ **㉢(밑수 단위가 표 밖)과 같은 집안이나 다른 것임** — 그건 **단위**가 표 밖에 있는 것이고
이것은 **값 자체**가 표 밖에 있는 것임. 둘 다 「표만 읽는 코드」가 못 잡음.
#### ㉵ ⚠⚠ **`0` 과 「없음」을 같은 것으로 두지 말 것** (2026-09-09, 하루에 **세 번**)
```
표토 대상 면적이 **0 ㎡** 인데 **0 ㎥ 를 「값 있음」으로**
⇒ 받는 쪽이 「표토가 없는 노선」으로 읽음. 두께 미입력은 막고 있었는데 면적 0 만 통과
사토장 구조물을 지웠는데 **설계 결과가 0.0 을 늘 실어** 횡단도·면적표에 계속 남아 있었음
⇒ 「칸이 있는 것」과 「값이 있는 것」이 달랐음
말풍선 폭을 이분법으로 찾아 남은 **0.0063㎥** 에 「⚠ 못 담음」 경고가 늘 떴음
⇒ 표시 자릿수에서 안 보이는 몫을 「남았다」고 말했음
```
**⇒ 세 가지 다 「0 이라는 값」이 거짓말을 한 것임.** 값이 없어서가 아니라 **있어서** 안 걸림.
**⇒ 지킬 것**
- **「없음」은 `None`(빈 값)으로 낼 것.** `0` 은 **「재 보니 0 이었다」**는 뜻으로만 쓸 것.
- **얹는 코드를 쓰면 지우는 길도 함께 쓸 것.** 사토장이 그 자리였음 — 얹기만 하고 안 지워
**구조물을 없앤 뒤에도 값이 남았음.** 시험은 「놓았을 때」만 보고 「지웠을 때」를 안 봤음.
- **경고는 사람이 보는 자릿수로 판정할 것.** 0.0063 처럼 화면에 안 보이는 몫을 경고하면
**경고 전체가 믿을 것이 못 됨.**
⚠ **셋 다 시험은 통과했고 화면에서 잡혔음.** 「있다」와 「돈다」의 그 자리임 — **지우는 길·빈 자료·
표시 자릿수는 시험이 잘 안 훑는 세 곳**임.
### ⚠ 좁은 폭에서 화면이 깨지면 볼 자리 셋 (2026-09-09 B03 업로드 현황에서 셋이 한꺼번에)
셋 다 **넓은 화면에서는 안 보이다가 창을 줄이면 터짐.** 다른 페이지도 같은 짜임이면 같이 깨짐.
- **`minmax(260px, 1fr)` 는 「260보다 좁아질 수 없다」는 뜻** — 컨테이너가 그보다 좁아지면
**격자가 컨테이너를 뚫음.** ⇒ `minmax(min(260px, 100%), 1fr)` 로 적을 것.
- **접는 분기가 아예 없는 2열** — `repeat(2, minmax(0,1fr))` 는 좁아져도 2열을 고집해
열마다 카드 하나도 못 담음. ⇒ `repeat(auto-fit, minmax(min(360px,100%), 1fr))`.
- **flex 자식의 `min-width: 0` 누락** — 기본값이 `auto` 라 **내용보다 안 줄어듦.**
안쪽 격자가 넘치면 **페이지 몸통이 통째로 가로로 밀림.**
**판정은 눈이 아니라 숫자로** — 폭을 여러 개(1400·1000·760·620·480) 돌며
`scrollWidth clientWidth` 가 0 인지 볼 것. 창이 눈에 안 따라와도 **뷰포트 폭만 바꾸면
레이아웃은 다시 계산되므로 측정은 됨.**
**⚠ 같은 짜임이 다른 화면에도 있음 — 훑은 목록** (2026-09-09, 아직 안 고침)
```
㉡ 접는 분기 없는 고정 2열 — 아홉 곳 (B03 에서 깨진 것과 같은 짜임)
B01_Dashboard_UI_Style.css:22 · :134 · :166
B02_ProjRegister_UI_Style.css:18 ← 등록 폼, 사용자가 자주 봄
B05_Profile_UI_Style.css:303 · :338 · :436
B05_Profile_UI_Style_Structures.css:157
B06_Section_UI_Style.css:286
B06_Section_UI_Style_Cross_Controls.css:314
B07_DesignDetail_UI_Style.css:55 (1fr 1fr)
⚠ B05·B06 은 좌측 패널 안이라 폭이 원래 좁음 — B03 보다 먼저 깨질 수 있음
㉢ min-width:0 누락 — 62 곳. 전부가 문제는 아니고 ㉡ 와 겹치는 자리부터 볼 것
㉠ minmax(고정px) — B01_Dashboard_UI_Style.css:231 한 곳뿐(150px 라 위험 낮음)
@media 가 아예 없는 화면 — B05_* · B06_* · B08_* · B09_*
⇒ 좁은 폭 대응을 안 한 화면이 넷임
```
**고칠 순서 제안** — ① B02 등록 폼 ② B05·B06 좌측 패널 2열 ③ 나머지.
### ⚠ **`reload()` 만으로는 vite 가 옛 모듈을 물고 있을 수 있음 — 캐시부터 비울 것** (2026-09-09)
지침이 「페이지·공용 코드는 `page.reload()` 한 번(vite dev)」이라 적고 있으나 **그것으로 안 걷힌 자리**가 나왔음. **한 증상을 두고 세 창이 차례로 헛짚었음.**
```
증상 벽 터파기가 화면에 0건 (관 터파기는 뜸)
헛짚음 ① 「그리기 가드가 먹는다」 ② 「초안 경로에서만 그려진다」 ③ 「백엔드 재시작을 안 했다」
실측 백엔드 stale:false · API 상세에 revetment.foundation="기초유" **이미 옴** · 화면만 옛것
답 CDP Network.clearBrowserCache + sessionStorage.clear + reload → **바로 뜸**
```
⇒ **TS 를 고쳤는데 화면이 옛것 같으면 「캐시부터」.** `page.goto`·`reload` 만으로는 부족할 수 있음.
⚠ **브라우저 창은 재시작하지 말 것** — 캐시 비우기로 끝남(지침 4장 그대로).
### ⚠ 공용 이름을 **갈거나 지울 때는 창들에 먼저 알릴 것** (2026-09-09, 하루에 세 번)
**값을 한 벌로 두어도 이름이 바뀌면 깨진다.** 「공용 상수를 직접 읽으니 저절로 따라온다」가
**반만 맞는 말**임 — 값은 따라오고 **이름은 안 따라옴.**
```
① BLANK_DRAWINGS·BLANK_LABELS 를 걷어냄 → 남의 tmp/tests 가 수집 단계에서 터져 **267건이 통째로 멈춤**
② tmp/tests 는 git 밖 → 병합에 안 실림. **지운 쪽이 알리는 것 말고 길이 없음**
③ 두께 상수 이름 셋을 갈이 → 남의 표준도 그림이 KeyError 로 **죽음**
thickness_base_m·_top_coeff·_bottom_coeff → _top_add_m·_slope_per_m·_height_base_m
```
⇒ **새로 넣는 것은 안전함. 갈거나 지우는 것만 알리면 됨.**
⇒ 셋 다 **실화면·실행에서만** 드러났음 — 코드만 봐서는 안 잡힘.
### ⚠ **[저장]은 횡단 설계를 다시 계산하지 않음 — 새 키를 넣었으면 「재생성」을 돌릴 것** (2026-09-09)
**설계 엔진에 키를 새로 넣었는데 저장분에 안 들어오는 자리.** 하루를 「값이 0 이다」로 보냄.
```
[저장] 편집을 적용하고 Node 로 **구조물 면적·유토곡선만** 다시 냄(_recompute_stored_designs)
⇒ 저장분이 **옛 34키 그대로** — 새 키(bench_cut_length_m 등)가 안 들어감
재생성 파이썬 설계 엔진이 **전 측점을 새로 냄** ⇒ design 키가 **38개**로 늘어남
```
**세 창에 돌린 절차 (순서를 지킬 것)**
```
① git sync
② npm run build:server-calc ⚠ 번들은 git 밖 — 창마다 각자 해야 함
③ 백엔드 재시작 ⚠ @lru_cache 가 옛 등록부·config 를 물고 있음
④ B06 열고 카드가 다 뜰 때까지 기다림
⑤ POST /api/projects/{pid}/sections/{routeId}/regenerate
body {"cross_half_width_m": <지금 쓰는 반폭>}
(routeId·반폭은 GET /api/projects/{pid}/sections/context 의 route_id·defaults)
⑥ 확인: GET …/sections/{routeId}/detail → **design 키 38개**
```
⚠ **[확정]은 필요 없음** — 저장·재생성만으로 정본이 섬.
⚠ **추정치를 그대로 쓰지 말 것** — 같은 노선에서 추정 12,931㎡ → 실측 **13,699.7㎡ (+6%)** 로 갈렸음.
### ⚠ 창 크기를 바꿀 때 `page.set_viewport_size` 를 부르지 말 것 — `resize(w, h)` (2026-09-09)
**그 한 줄이 화면을 그 크기에 박아** 사용자가 창을 끌어도 안 따라옴. `Emulation.
clearDeviceMetricsOverride` 로도 안 풀림(Playwright 가 다시 걺) — **드라이버 재시작 말고는
푸는 길이 없음.** 데스크탑이 그렇게 하루를 그 상태로 보냈고, **랩탑이 멀쩡했던 것은
그 호출을 안 썼기 때문**이지 배율·인자 차이가 아니었음.
- 드라이버에 **`resize(w, h)`** 를 넣었음 — CDP `Browser.setWindowBounds` 로 **창 자체**를 바꿔
화면이 따라옴. `page.set_viewport_size` 는 이제 **막힘 문구와 함께 거부**됨.
- ⚠ **`.claude/` 는 git 밖이라 커밋으로 안 건너감** — 시놀로지 동기화에 기대거나 창마다 붙여야 함.
붙었는지는 `grep -n "def resize" .claude/browser_driver.py` 로 확인.
- **화면이 안 따라와도 폭 측정은 됨** — 뷰포트 폭만 바꾸면 레이아웃은 다시 계산되므로
`scrollWidth clientWidth` 로 재는 검증은 그대로 유효함. 눈으로 보는 확인만 뒤로 미룰 것.
### ⚠ 브라우저 명령 파일은 **다른 이름으로 쓴 뒤 `mv` 로 넣을 것** (2026-09-09, 하루에 두 번)
`tmp/browser/cmd/` 에 파일을 **직접 쓰면 드라이버(1초 폴링)가 쓰는 도중에 집어감.**
**실행은 되고 `OK` 도 뜨는데 내용이 비어 아무 일도 안 일어남** — driver.log 에도 안 남음.
⇒ **딴 이름으로 다 쓴 다음 `mv` 로 옮길 것.** 「도는 척만 하는 검사」와 같은 계열이라 특히 나쁨(㉢).
### 공용 브라우저가 로그인 화면에서 멈췄을 때 (2026-09-08 — 두 창이 같은 자리에서 막힘)
**튕기면 서버가 아니라 쿠키를 의심할 것.** 세션은 DB 표(`sessions`)라 **서버 재시작으로 안 지워진다.**
날아가는 것은 브라우저 쿠키 쪽이다(새 프로필·만료·캐시 비우기에 함께).
쿠키는 포트를 안 가려 **8000·5173 중 한 번만 로그인하면 둘 다 붙는다.**
**되살리는 법** — 자격증명은 `tmp/aislo_cred.json`(git 밖, 사용자가 유지 — **지우지 말 것**).
없으면 **짐작하지 말고 사용자에게 요청**할 것. 명령 파일에서:
```python
import json
cred = json.load(open("tmp/aislo_cred.json", encoding="utf-8"))
api = page.context.request # ⚠ 브라우저 컨텍스트의 요청 — 쿠키를 공유한다
if api.get("http://localhost:8000/api/auth/session").status != 200:
api.post("http://localhost:8000/api/auth/login/request", data=cred)
log(api.get("http://localhost:8000/api/auth/session").status) # 200 이어야 함
```
**`page.evaluate` 안에서 `fetch` 로 하지 말 것** — 로그인 화면이 그 사이 이동해
`Execution context was destroyed` 로 죽는다(2026-09-08 두 번 겪음). `page.context.request`
페이지 이동과 무관하다.
`dev_up.py`·`browser_driver.py`**로그인 처리가 없는 것이 맞다** — 드라이버는 창만 띄우고
로그인은 **명령 파일 몫**이다. grep 해도 안 나온다.
**`shot()` 은 확장자를 붙일 것** — `shot("이름")``unsupported mime type ""` 로 죽는다.
`page.screenshot(path="tmp/browser/shots/이름.png")` 로 쓰는 편이 안전하다.
**값을 만지기 전에 「어느 프로젝트가 열려 있나」부터 볼 것**
`localStorage['frd_current_project_id']`. 사용자 프로젝트(`5cff3920`)면 **읽기만** 한다.
### 검증 함정 모음 — 재기 전에 볼 것 (2026-09-07 정리)
끝난 절을 `plans/` 로 옮기며, **다시 걸리면 또 오진할 것들만** 여기로 건져 왔음.
괄호는 원래 절 번호(`docs/raw/plans/2026-09-07_plan_completed_items.md` 에 원문 있음).
-**시험을 저장소 실물에 매지 말 것 — 만든 창에서만 통과함** (2026-09-09).
`test_prj_identify.py::test_compound_vertical_prj` 가 특정 프로젝트의
`…/input/prj/result.prj`**실경로로 열어** epsg 를 확인하는데, **그 프로젝트가 지워지자**
두 PC 에서 `FileNotFoundError` 로 깨졌음. `tmp/tests` 는 git 밖이지만 시놀로지로 **파일은
건너오고 저장소 자료는 창마다 다름** — 그래서 **남의 PC 에서만 깨지는 시험**이 됨.
**⇒ 실물 파일이 필요하면 없을 때 `skip` 하게 가드를 붙일 것.** 같은 파일의 다른 넷은 이미
그렇게 돼 있었고 하나만 빠져 있었음. **깨진 것을 「병합이 망가뜨렸다」로 오진하기 쉬운 자리임.**
- **옛 코드로 도는 서버에 속지 말 것** — 속도·화면을 재기 전에 ① 백엔드를 확실히 재시작하고
**그 수정의 로그나 새 응답 항목이 실제로 있는지** 먼저 볼 것. 하루에 두 번 속았고, 한 번은
응답에 새 항목이 아예 없는데 화면은 정상처럼 보였음(다른 값으로 세는 폴백). (0-12)
- **캐드(B07) 화면은 `npm run build` + 캐시 비우기** 를 해야 바뀜 — 안 하면 옛 화면을 봄. (4-4)
- **화면이 백지가 되면 옛 vite 프로세스를 먼저 볼 것** (2026-09-07 보조 창, 하루 세 번 겪음) —
증상: 해시 이동은 되는데 `document.body` 가 빈 문자열, 콘솔에 `ERR_CONNECTION_RESET`.
원인: 백엔드를 다시 띄우면 `main.py` 가 vite 를 새로 띄우는데 **옛 vite 가 남아** 탭이 죽은
소켓을 물고 있음(실측: 5174 를 쥔 node 하나 + 유령 둘). 캐시만 비워서는 안 살아남.
되살리는 순서 — ① `Get-NetTCPConnection -LocalPort <포트>` 로 주인 확인, 유령 node 정리
② 백엔드 재시작 ③ **`about:blank``http://localhost:<포트>/`(wait "load") → 그다음 해시**.
해시로 곧장 가면 계속 백지였고, 루트를 먼저 거치면 살아남.
- **조정창은 두 벌이고 조작이 갈려 있음** (2026-09-07 두 창이 각각 헛짚음) — 좌측 [횡단 조정]
dock 에는 **이동 십자(▲▼◀▶)·집수정 9키가 CSS 로 숨겨져** 있고(`_Style_Cross_Controls.css:244~`),
그 십자는 **도면 위에 뜨는 오버레이 창에만** 있음. 반대로 오버레이에는 값·형식 행이 숨겨져
있음(2026-08-29 「값은 좌측, 위치제어는 도면 위」). dock 에서 ◀ 를 찾으면 DOM 에는 있는데
`vis:false` 인 것이 **정상**임 — 결함으로 오해하지 말 것.
- **요소가 보이는지는 `offsetParent` 로 재지 말 것** (2026-09-07, 두 창이 같이 걸림) —
**고정 위치(`position: fixed`) 요소는 보이든 안 보이든 `offsetParent` 가 늘 `null`** 이라
「안 보인다」로 오판함. 크기로 잴 것 — `getBoundingClientRect()` 의 width/height, 또는
`offsetWidth || offsetHeight`. 둘 다 0 이면 실제로 안 그려진 것임.
- **UI 를 붙였으면 크기도 같이 잴 것** (2026-09-07) — 알약 레인을 종단 패널에 넣고 「알약이 뜬다」만
보고 넘겼다가, 잰 높이에서 레인 몫을 안 빼 되먹임이 생겨 **패널이 16,664px 로 부풀었음**.
그 상태에서는 카드·알약을 아예 못 눌러 다음 검증이 통째로 막혔음.
- **3D 클릭을 잴 때** — `__corridorScene.project()` 좌표는 DOM 이 덮고 있는지를 안 봄.
클릭 직전에 `document.elementFromPoint(x, y) === document.querySelector('canvas')` 를 확인하거나
종단 오버레이 핸들(`.ui-workflow-overlay__handle`)을 먼저 접을 것. 이걸 몰라 두 번 오진. (2-2)
- **조정창은 셋으로 쪼개져 있음 — 「단 수」는 어느 조정창에도 없음** (2026-09-07, 3-7 에서
한참 헤맴). ① **카드 위 조정창** = 이동 십자·집수정 9키 **전용** ② 좌측 **[횡단 조정] dock**
= 높이·길이·기준측점 전/후 ③ **단 수(추가 기슭막이)와 옵션(연동·경사)** 은 둘 다 아니고
**좌측 구조물 폼의 유입구/유출구 칸**(`.b05-structure__adjust-slot`)으로 옮겨 붙음.
카드 조정창 안에도 그 행의 DOM 은 있지만 CSS 로 `display:none` 이라 눌리지 않음 —
`innerText` 로만 보면 「있는데 왜 안 눌리지」로 헤매게 됨. **크기(rect)로 볼 것.**
- **B06 조정창을 볼 때 — 두 번 걸린 함정임(3-4 · 3-7).** `.b06-structure-panel` 은 **카드마다
하나씩** 있음(66~67개). 목록에서 `find` 로 아무거나 집으면 **다른 카드의 빈 패널**이 먼저 잡혀
크기 0 · `is-hidden` 으로 나옴. 2026-09-07 에는 이것 때문에 두 창이 「십자 단추가 안 그려진다 =
사용자도 벽을 못 옮긴다」로 잘못 결론냈다가 되물렀음(고른 벽의 카드에서 재니 조정창 124×185,
이동 행 107×86 으로 멀쩡했음). **반드시 그 카드 안의 것**(`card.querySelector`)을 볼 것.
또 **벽을 안 그리는 카드**를 고르면 굳히기 루프가 0회 도니
`card.querySelector('.b06-chart__culvert-revet-hit')` 로 먼저 거를 것.
- **화면 서버(vite)는 백엔드(`main.py`)의 자식임** (`main.py → npm → vite`, `.claude/dev_up.py:40`).
백엔드를 트리째 끄면 **화면 서버도 같이 죽고** 열려 있던 탭이 옛 모듈을 붙들어 **백지**가 됨.
재시작이 비싼 진짜 까닭은 서버가 아니라 이쪽임. 조작 전에 8000·5173(또는 8001·5174) **둘 다**
200 인지 볼 것. (옛 「7. 개발 환경」에서 건져 온 것)
- **로그인 세션은 DB 표**(`sessions`, `common_util/common_util_auth.py:115`)라 재시작으로 안 지워짐.
**그래도 브라우저 쪽 세션은 끊김** — 2026-09-08 데스크탑 두 창이 같은 자리에서 막혔음.
화면 검증 전에 `#/a06-login` 으로 튀는지 먼저 볼 것. (로그인하는 절차는 랩탑 창 확인 중)
-**화면이 막혀도 엔진은 파이썬에서 직접 부를 수 있음** — 라우터가 인계를 함수로
부르므로(`from B08_Quantity… import get_handoff`) HTTP·로그인을 안 탐.
⚠ 다만 **`await init_db_pool()` 을 먼저 부를 것** — 안 부르면 예외가 응답 본문으로 나가
**빈 응답처럼 보이고 「자료가 없다」로 잘못 읽힘**(2026-09-08 실측). 그리고 **한 이벤트
루프 안에서** 다 끝낼 것(따로 돌리면 `Event loop is closed` 가 쏟아짐).
-**되받기 전후로 인계 응답을 파일로 떠 둘 것** — 「합계가 221원 줄었다」를 짚을 때
**어느 줄이 얼마나 바뀌었는지**를 대조할 자료가 없으면 짐작이 됨(2026-09-08).
마침 화면 로그에 옛 수량이 남아 있어 짚었으나 그건 운이었음.
⚠ 다만 「재시작하면 다시 로그인해야 했다」는 기록이 있으므로 **다른 까닭이 있을 수 있음**
실측은 안 해 봤음.
- 🔴 **횡단 카드를 확대·이동하면 그 측점의 면적이 달라질 수 있음** (2026-09-07 3-9 뒷정리에서
발견). 확대·이동은 그 측점의 **「표시 반폭」**(`display_half_width_m`)을 바꾸고(실측 12 → 27m),
**사면이 반폭 안에서 지반을 못 만나는 측점**은 잘린 만큼 면적이 달라짐(성토 **57.45 → 62.79㎡**).
세션 `crossw` 에 남으므로 **검증하러 확대하는 것만으로 수치가 흔들림.**
· 설계상 그런 자리는 `slope_unclosed` 경고가 이미 뜨므로 **결함이라기보다 「반폭이 좁으면 면적이
잘린다」는 성질**임. 다만 검증할 때는 **확대 전 반폭을 적어 두고 끝나면 되돌릴 것.**
- **화면 요소가 보이는지 `offsetParent` 로 재지 말 것** — 고정 위치 요소는 늘 `null` 이라 늘
안 보이는 것으로 나옴. `getBoundingClientRect()` 크기로 잴 것. (3-7)
- **단계바 `.ui-workflow-layout__step` 은 0번이 대시보드** — 종단설계는 3번. (2-7)
- **살아 있는 프로젝트에 검증용 `solve_route` 를 돌리지 말 것** — `route_main.geojson` 은 프로젝트당
한 벌이라 노선 행이 늘어도 파일은 덮임(실제로 덮였고 되돌렸음). (0-10)
- **이미 만든 프로젝트는 그대로임** — 노선이 폴리라인 기준이 되려면 새로 업로드해야 함. (0-10)
- **곡선 생략(내각 155°) 규칙을 쓰지 않기로 해 곡선 개수가 늘었음**(용화 13 → 26곳) —
평면 R 을 읽는 자리(3-1 확폭 · 2-6 구조물)는 그것을 감안할 것. (0-12)
- **3-5 물량 파급을 되돌릴 자리** — `Cross_Culvert_Geom.ts``extendTrimSlope` 위 주석.
사용자가 「그대로 둠」으로 확정했으므로 근거로만 남긴 것. (3-5)
- **확폭이 수량에 실리는 것은 B08 재작업 때** — `carriageway_width_m`(= 3.0 + 확폭)을 쓰면 됨. (3-1)