Merge remote-tracking branch 'origin/dev' into sub_laptop_1

This commit is contained in:
2026-09-13 16:47:07 +09:00
16 changed files with 29066 additions and 1789 deletions
@@ -1,207 +0,0 @@
# 완료분 이관 — 2026-09-13 (데스크탑 메인)
`PLAN.md` 에서 **미체크 항목이 하나도 없는 절**을 통째로 옮긴 것. 원문 그대로이고 줄여 쓰지 않음.
같이 옮긴 것은 **그 절이 낳은 참고 자료**(검증 기록 · 실측값 · 커밋 번호 · 계획 대비 차이)임.
**옮긴 날** 2026-09-13 · **옮긴 창** 데스크탑 메인(`main_desktop_1`) · **그때 저장소** `7bd6d101`
**남겨 둔 것** — 아래 절은 완료로 보이나 살아 있는 실마리가 붙어 있어 계획서에 그대로 둠.
| 절 | 남긴 까닭 |
| --- | --- |
| 사토장 | 「아직 정할 것」 셋이 열려 있음 |
| 사용자 확정 5차 | 큰 것 3·8 · 여섯째 · 작은 것 5 가 자료·답 대기(⏸) |
| 사용자 확정 4차 (흙막이) | 일곱 형식 원단위가 자료 대기 |
| 사용자 확정 2차 | ② 벽 평균두께가 미체크 |
| 확정 1~11 | 3·4·5·8-3·8-4 가 대기(⏸) |
| 4-13 중간 점검 상태 | 물구멍 관 길이 · 뒷채움 폭 두 물음이 살아 있음 |
| 8-7 이중계상 금지 규칙 | 「원문을 남기는 자리」로 못 박은 절 |
| 8-31 개발용 단추 | 오해를 막는 경고라 계획서에 있어야 함 |
| 9-9 카탈로그 열한 줄 | 9-10 이 이 절을 가리킴 |
---
## 사용자 확정 3차 — 2026-09-09 밤 (여섯 중 다섯)
### ⭐ 사용자 확정 3차 — 2026-09-09 밤 (여섯 중 다섯)
**「1~6번은 권장하던 대로, 6번은 자재로」** — 사용자 원문. 아래가 정본임.
| # | 물음 | **확정** |
| --- | ---------- | ------------------------------------------------------------- |
| 1 | 흙깎기 작업효율 E | **0.50** (영월 방식 · 현행 산림품셈 인자와 전부 일치) |
| 2 | 기초잡석 두께 | **0.2 m** (폭 1.5m 는 관측 역산 ⇒ 옹벽 0.30㎥/m) |
| 3 | 사토장까지 거리 | ⚠**미정 — 권고한 값이 없어 그대로 물음으로 남김**(현장값) |
| 4 | 구조물 터파기 용수 | **육상(용수 없음)** ⚠ 내가 권고한 적 없어 **통상값으로 적용 + 노티스**. 사용자가 뒤집을 수 있게 |
| 5 | 관급 자재 | **전부 사급** |
| 6 | 콘크리트 자리 | **자재 축에 세움**(㉮) — 사용자 명시 |
**3·4 는 내가 권고를 낸 적이 없는 자리임.** 3 은 물음으로 남기고, 4 는 통상값으로 넣되
**「사용자 확정이 아니라 통상값 적용」**을 사유·노티스에 적을 것. 뒤집히면 한 줄임.
**⚠⚠ 그리고 사용자 지시 — 「대신 페이지에 남길 것」.**
**여섯 값 전부를 코드에 박고 끝내지 말 것.** **화면에 칸으로 세우고, 지금 값과 그 근거를 보이고,
사용자가 바꿀 수 있게** 할 것.
```
흙깎기 작업효율 0.50 칸 + 근거(현행 산림품셈 9-3-2 · 영월 방식)
기초잡석 두께 0.2 m 칸 + 근거(폭 1.5m 는 관측 역산 · 두께는 원문에 없음)
사토장 거리 칸(비어 있음) — 넣으면 덤프 금액이 섬
용수 유무 칸(기본 육상) + **「통상값 · 사용자 확정 아님」** 표시
관급/사급 칸이 이미 있음 — 전부 사급으로 세움
콘크리트 자재 축에 섬
```
**정한 값이 화면에서 안 보이면 다음 사람이 왜 그 값인지 모름.** 오늘 지켜 온
「사유를 드러낸다」와 같은 결임.
- [x] **구현 상태 (2026-09-13 전수 대조)** — 1(`5780eb93` E 0.50) · 2(`99248440` 0.2m) · 4(용수 육상+통상값 표기) · 6(`a57e1bbe` 자재 축) 코드에 섬. 화면 칸도 섬 — 흙깎기 E(B09 기초자료 탭) · 기초잡석 두께·사토장 거리·용수(B08 설정, `ab51fa38`) · 콘크리트(자재총괄). 3 은 칸만 서고 값은 물음 그대로. 검증 대기.
- 5(전부 사급) — 검증 기록 `2026-09-09b_확인으로_닫은것.md` 로 닫힘(79da822d 자재 여덟 줄 사급 실측). ⚠ 단 **새 프로젝트의 기본값은 여전히 「미정(발주기관 결정)」** 으로 섬(`common_util_project_settings.py:97` 주석 「발주 결정이라 비워 둠」). 기본값을 사급으로 바꿀지는 **사용자 확인 자리**로 남김.
---
## 확정 12~16 · 확정에 딸려 나온 남은 물음 넷
### 확정 12~16 (알려만 드린 다섯 → 전부 「하라」로 바뀜)
| # | 무엇 | **확정** |
| --- | --------- | ------------------------------------------------------------------------------------- |
| 12 | 내야 할 표 | **16개 전체가 있어야 함.** 안 내던 7개(중기경비계산서 등)를 다 만들 것. **실무 문서에서 확인 가능** |
| 13 | 부대시설 다섯 줄 | **산출에 추가 반영.** 들어가야 할 내용임 (국가지점번호판·안내판·차단기·가설창고·수방자재) |
| 14 | 버림 콘크리트 | **추가.****실무문서 최신본을 따르되 법령·시행령·지침이 있으면 그 값이 우선** |
| 15 | 옛 프로젝트 금액 | **옛 테스트 버전이면 삭제해도 됨.****유가정보는 전국단위 공시가 / 지역단위 공시가를 고를 수 있어야 함** |
| 16 | 도자 한계거리 | **60m.** 상수는 이미 60(`config_system_design.py`)이었고 **설명 주석만 70으로 낡아 있어 맞춤**(2026-09-09) |
- [x] **구현 상태 (2026-09-13 전수 대조)** — 12(`3257fe97` 16장) · 13(`42cd2b8f` 부대시설 다섯 줄 — 금액 넷은 5차 3번 대기) · 14(`48597351` 버림 KDS 100㎜ — 화면에서 빼는 칸은 위 8-2) · 15(`f39c195b` 유가 전국/지역) · 16(`674d4ef1` 60m) 코드에 섬. 검증 대기.
### ⚠ 남은 물음 넷 — 확정에 딸려 나온 것
- **⭐ 중기 조종원 노임 산식** — 우리 값이 실무보다 **−36%**. 실무 두 공사지가 쓰는 계수
`× 16/12 × 25/20` 를 우리가 안 씀. 고칠 자리는 `record.operator_person_days / 8` **한 줄.**
**넣으면** 굴착기 0.7 시간당 86,096 → **106,384(+23.6%)** · 불도저 19톤 124,842 →
**148,861(+19.2%)** — **기계가 든 공종 전부**가 오름.
**아직 안 넣었음** — 계수의 원문(기재부 예규)이 저장소에 없어 **「원문 미보유」**로 남기고
중기목록표 비고에 사실만 드러내 뒀음. **사용자 확정 자리.**
- [x]**위 서술은 낡았음 (2026-09-13 대조)** — 계수는 **이미 들어가 있음**: 커밋 `6d607934` · `B09_Estimation_MachineCost.py:51-78`(16/12 × 25/20 ≒ 1.667) · 조종원 줄 비고 「1일 8시간 × 제수당·상여·퇴직충당 16/12 × 25/20」. 25/20 의 원문 확인 못 함은 주석에 그대로 남아 있음. 검증 대기.
---
## 0-13. 계획노선 편집 손질 여덟
### 0-13. 계획노선 편집 손질 여덟 (2026-09-12 사용자 지시, 랩탑 메인) — 검증 대기
대상은 `B05_Profile/B05_Profile_UI_RouteEdit*.ts``B05_Profile_UI_Style_RouteEdit.css` 한 벌임.
④ 「표고의 뜻」은 물음이라 답으로 닫음 — 횡단 판 아래 줄의 표고는 **그 횡단에 그려진 선(원지반+계획 횡단)의 해발 범위(최저~최고)** 이고 계획고·절토고가 아님.
- [x] **① 교각점 고정 단추** — 곡선 패널에 「교각점 고정」 한 줄. 켜면 그 꺾임점의 X·Y 를 못 박아 **노드 끌기·손잡이 끌기가 둘 다 막히고** 반지름·곡선길이 칸 입력으로만 바뀜. 상태는 `apexLock: boolean[]``curveLock` 옆에 두어 되돌리기·초기화에 같이 실림.
- **검증** — 랩탑_메인 프로젝트(노드 28개) 편집 창에서 23번째 꺾임점을 잡고 잼. **안 잠근 채 끌면** 계획노선 1078.0m → 1067.2m 로 바뀌고, **잠그고 같은 만큼 끌면** 1078.0m 그대로임. 상태줄이 「교각점을 고정했습니다 … 고정 1곳」으로 뜨고, [초기화] 뒤 「고정」 표기가 사라져 사진첩에 함께 실린 것도 확인함.
- [x] **② 절·성토 교차점 밖은 계획선을 안 그림** — ⚠ **계획 대비 바뀐 점**: 처음에는 「원지반을 자른다」로 적었으나, **B06 횡단 카드가 2026-08-20 같은 지적을 받아 이미 계획선을 끊고 있음**(`appendCrossDesignOverlay` 주석 「사면 끝 바깥은 손대지 않는 원지반이라 지반선이 이미 표현한다」). 두 화면이 달라지면 안 되므로 **계획 횡단선**을 교차점에서 끊고 원지반선은 전 폭을 그대로 둠. 교차점 찾기는 `B06_Section_UI_Cross_Fit.ts``toeOffsets` 를 열어 씀(좌·우 따로, 외삽 자리는 안 씀).
- **검증** — 측점 1+0.0 에서 SVG 점을 읽음. 고치기 전에는 원지반·계획선이 **둘 다 10~368px(±20m 전 폭)** 이라 겹쳐 그려졌고, 고친 뒤 원지반 10~~368px · 계획선 **110~~258px**(사면 끝 사이)로 갈림. 성토사면·절성토 면적 표기는 8.85m · 7.20㎡ · 6.69㎡ 로 안 바뀜.
- [x] **③ 횡단을 벡터로 + 휠 줌·팬 (공용화)** — 캔버스 그림을 SVG 선으로 바꿈. 줌·팬 본문을 **공용 조각**(`A00_Common/b_svg_zoom_pan.ts`)으로 빼고 `B06_Section_UI_Cross_View_Zoom.ts` 는 여백만 씌우는 감싸개로 남김(B06 부르는 쪽은 안 고침). B06 은 휠을 줌에 안 묶고 B05 는 휠 = 줌 — `wheelZoom` 한 칸으로 가름.
- **검증** — 휠 한 칸에 배율 1 → 1.15 → 1.3225, 반대로 돌리면 1.1506, 더블클릭에 `scale(1)` 로 되돌아옴. 가운데 버튼으로 (40, 20)px 끌면 `translate` 가 그만큼 따라옴. `viewBox` 378×312 가 판 크기 377×311 과 1:1 이라 커서 자리가 안 밀림.
- [x] **⑤ 이전 횡단 판은 계획선이 바뀔 때만** — 측점 누르기로는 아래 판을 안 채우고, 계획 평면선을 고쳤을 때만 고치기 전 것이 내려감.
- **검증** — 측점 1+0.0 을 **두 번** 눌러도 아래 판이 「노선을 고치면 …」 빈 상태 그대로임. 노드를 끌어 계획노선 1078.0 → 1075.7m 로 고친 뒤에야 아래 판에 1+0.0 횡단이 앉음.
- [x] **⑥ 「거리 재기」 눌린 표시** — `is-active` 가 붙는데 꼴이 없어 겉모습이 안 바뀌던 것. `.b05-routeedit__actions .ui-btn.is-active` 한 줄 추가.
- **검증** — 단추를 누르면 바탕 `rgb(185,154,255)`(`--color-primary`) · 글자 흰색 · 테두리 투명. 다시 누르면 원래 꼴로 돌아옴.
- [x] **⑦ 등고선 높이 라벨 맨 위로 + 촘촘히** — 라벨을 노선·측점 눈금보다 **나중에** 그려 안 가려지게 함(띠 300m 는 다시 씌움). 촘촘함은 라벨 솎기 예산을 이 화면에서만 350 → 1200 으로 올려서 냄.
- **검증** — 화면 갈무리에서 905·910·915m 가 나란히 떠 **5m 눈금**(LAS 등고선이 낼 수 있는 가장 촘촘한 단위)까지 내려온 것을 봄. 그리기 차례는 등고선 → 노선·눈금 → **라벨** 순으로 바뀜.
- [x] **⑧ 곡선 길이(L) 하한도 지킴** (2026-09-12 사용자 추가 지시 「반경과 길이 둘 다 지켜야 함」) — L = R·Δ 라 한 자리의 하한을 **반지름 하나로 모아** 봄(`radiusFloorM` = max(R 하한, L 하한/Δ)). 값 끌어올리기·모자란 양 판정·칸 막기가 모두 이 값을 씀.
**예외 — 내각 155° 이상**(별표2 Ⅰ.2.다.(1) 「곡선을 설치하지 아니할 수 있다」)은 L 하한을 안 걺. 거기서 L 을 채우라 하면 교각이 0 에 가까워 R 이 수십 배로 부풀어 **안 고친 구간이 휨**. 그 자리는 곡선 패널에 「155° 이상 — 곡선 생략 가능」으로 적어 냄.
- **검증** — 실측: 곡선 26곳 중 L 5m 미달 **6곳이 전부 내각 158.8~174.8°**(예외 구간)이고 155° 미만 미달은 0곳. 창을 열어도 계획노선 1078.0m 그대로(조용한 변형 없음). 패널에서 내각 115~147° 자리는 R 하한 12 · L 하한 5 가 걸리고, 157° 자리는 L 하한이 안 걸림(`min` 1). 내각 134° 자리에 L 을 3m 로 적으면 5m 에서 멈추고 R 하한 12m 이 이겨 L 9.7m 로 되돌아옴.
- **시험** — `resources/tester/test_b05_curve_radius_floor.py` 7건(Node 헬퍼가 `_Edits.ts` 를 그 자리에서 트랜스파일). L 하한이 R 을 끌어올리는 자리(내각 150° · L 20m → R 38.2m)까지 못박음.
- [x] **자체검증 한 벌**`tsc --noEmit` 통과 · `prettier` 적용 · `pytest resources/tester/ -q` **1330 통과 · 22 건너뜀**. 화면 검증은 ORCA 탭에서 사용자처럼 조작해 위 수치로 판정함.
-**못 본 것** — ORCA 내장 창이 637px 라 오른쪽 횡단 칸이 접히는 폭임. 검증 동안만 칸을 띄워 두고 봤으므로, **1500px 이상 창에서의 실제 배치**는 사용자 화면에서 한 번 봐 주셔야 함.
---
## 3-17. 횡단도 손질 다섯
### 3-17. 횡단도 손질 다섯 (2026-09-12 사용자 지시, 랩탑 보조)
**① 노면이 넓어 보이는 까닭 — 조사 끝**
노견은 안 늘어남 — 좌·우 각 0.5m 고정(`config_system_design.py` 표준값). 넓어진 것은 **차도**이고, 까닭이 둘이라 겹쳐 보임.
- **곡선부 확폭** — 평면 곡선반경 표(별표2 Ⅰ.2.나.(4))로 **곡선 바깥쪽 편측**에만 붙음. 화면 라벨이 `규격 3.00 + 확폭 1.50` 으로 이미 갈라 보여 줌. 1.50m 은 반경 15~18m 구간값이고, 확폭 뒤 유효너비 상한은 5.0m.
- **기슭막이 노폭 연장** — 벽이 1:1.2 성토사면 착지점보다 바깥에 서면 물매를 눕히지 않고 **노견을 그만큼 수평 연장**함(`fillSlopeOf()`). 몫은 벽 라벨 `· 노폭 연장 N.NNm` 으로 뜸.
- [x]`**B06_Section_UI_Page.ts` 828줄** — 700줄 한도 넘음(계획선 편집 배선 +28줄, 손대기 전 800줄). 다음 손질 대상.
**자체검증 (2026-09-13, 랩탑 보조)** — 착수 시점 실측은 **853줄**(적힌 828줄보다 25줄 더 늘어 있었음).
**순수 이동**으로 둘로 뗌 — 동작 변경 없음.
- `B06_Section_UI_Page_Design_Sync.ts` (218줄) — 설계 선택 반영·낡음 재계산·소단 동기화·[전체 반영]
- `B06_Section_UI_Page_Grade_Edit.ts` (85줄) — 종단 계획선 ▲▼ 제공자
- 남은 `B06_Section_UI_Page.ts` **644줄** (한도 안)
- 검사 — `npm run typecheck` 통과 · `prettier` 적용 · `pytest resources/tester/ -q` **1330 passed, 22 skipped**
(소스 문자열 감시 시험 셋이 옮겨 간 자리를 못 봐 실패 → 시험이 분리 모듈까지 읽게 고침:
`test_b05_stale_design_sync.py` · `test_b06_ditch_choice.py` · `test_masshaul_single_source.py`)
- 화면 판정 (ORCA 탭 · 포트 5174 · 프로젝트 `랩탑_보조`) — 측점 20.0m 기준
· 계획선 ▲ 한 번 — 계획고 **871.93m → 872.03m**(+0.10), 세션 초안에 `station_offsets {"20.000": 0.123218}` 기록
· 이어 전 측점 재계산이 돌아 그 카드가 바뀜 — 성토사면 **8.85m → 9.24m**, 절토 계 **3.33㎡ → 3.52㎡**
· 카드(측점) 선택 — 4번째 카드 클릭 → 구조물 넘김값 `{"at":60}` 기록(선택 연동 정상)
· 되돌림 — 세션 초안·넘김값 지우고 새로고침 → 871.93m · 8.85m · 절토 1.79/1.54 · 카드 62개로 복귀
- 계획 대비 차이 — 없음(파일 이동만). 다만 700줄을 넘는 B06 파일이 더 남음:
`_UI_Cross_Design.ts` 782 · `_UI_Cross_View.ts` 756 · `_UI_Section_View.ts` 720 · `_UI_Cross_Culvert_Geom.ts` 720.
---
## 3-15. B06_Section_Engine_Design.py 700줄 내리기
### 3-15. `B06_Section_Engine_Design.py` 가 700줄을 넘음 (2026-09-09)
```
남길 것 compute_cross_design 본체 + 결과 조립 (짝이 걸린 자리)
떼는 것 ① SectionGeometry 클래스 → B06_Section_Engine_Design_Geometry.py
(TS 짝은 이미 common_util_cross_design_geometry.ts 로 떨어져 있음 — 같은 선)
② 표준값 해석·저장분 읽기 도우미 → B06_Section_Engine_Design_Specs.py
(_resolve_group · _as_float · curve_widening_args — TS 에 짝이 없는 파이썬 전용)
```
```
⇒ ①은 **TS 가 이미 그 선으로 갈라 둔 자리**라 거울이 안 깨지고, ②는 짝이 없는 몫임.
착수 전 이 축을 한 번 더 보이고 승인받을 것.
```
- [x] **700줄 이하로 내림** — ①만 떼어 냄(커밋 `2972e4cc` · 924 → 522줄). ②는 **안 뗌** — ① 만으로 한도 안에 들어와 뗄 까닭이 없어짐(계획 대비 차이).
- **검증** (2026-09-13 랩탑 메인) — 줄 수 실측: `B06_Section_Engine_Design.py` **485줄** · `_Geometry.py` 412줄. 검증 대기.
---
## 검증 V-11 · V-12 · V-16 · V-17 — 닫힌 네 건
### 지금 사람이 볼 수 있는 것 — 4건
| # | 확인할 것 | 어떻게 | 안 하면 |
| -------------- | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---- |
| ~~**V-11**~~ ⭕ | 반대 방향 검사가 **새 칸에도** 걸리나 | ✅ **시험으로 옮김**(2026-09-09 밤 · `test_b09_verify_guards`) — 칸을 늘려도·빠뜨려도 걸리는 것을 못 박음 | — |
| ~~**V-12**~~ ⭕ | 준비공 **여섯 줄이 B09 에 다 가나** | ✅ **시험으로 옮김**(2026-09-13 · `5e09a861` · `test_b09_prep_handoff_bill`) — 준비공·부대시설 **14줄 전부**가 B09 에서 내역 본체(표토제거·뿌리 적재·제근) 또는 제외 줄(사유째)로 섬, 어디에도 없는 줄 0. 표토제거 **2,359.035㎥ 가 수량째 본체에 섬**. ⚠ 단가는 아직 안 붙음 — 표토제거 「일위대가 없음」(시트 9번 표토 축) · 제근 「후보 6건」(임목축적 입력) · 뿌리 적재 「단위 불일치 ㎡ vs 주당」 — 셋 다 답·입력 대기 자리라 이 줄에서 안 다룸 | — |
| ~~**V-16**~~ ⭕ | 불도저 운반 **네 줄이 금액으로** 서나 | ✅ **화면 실측**(2026-09-09 밤) — 「네 줄」은 실은 **불도저 둘 + 덤프 둘**이었고 **불도저 둘은 금액으로 섬**: `3-1 dozer 운반 토사 17.39㎥ × 1,712.0 = 29,770원` · `3-2 리핑암 61.13㎥ × 3,911.3 = 239,105원`. **덤프 둘은 「일위대가가 아직 없습니다」** 로 뜸(운반거리 대기 — 아래 사토장 줄과 같은 것). 갈래마다 단가가 서는지 시험으로도 못 박음 | — |
| ~~**V-17**~~ ⭕ | 공구손료가 **이중으로 안 붙나** | ✅ **밑수가 겹치지 않음**(2026-09-09 밤) — 공구손료는 **주재료비 → 재료비**, 제잡비는 **노무비 → 경비**. 한 공종에 둘 다 있어도 이중계상이 아님(시험으로 못 박음). 지금은 **주재료비가 0** 이라 3% 를 넣어도 한 원도 안 움직임. ⚠ **석공사·철골공사는 특수공구라 건설품셈 1-2-6 이 「별도 계상」**으로 둠 — 일반 비율을 그대로 붙이면 안 되는 자리라 **칸 사유에 적어 둠** | — |
---
## 검증 V-14 — 관급/사급 「발주기관 결정」 문구
| ~~**V-14**~~ ⭕ | 관급/사급 **「발주기관 결정」 표시** 문구 | ✅ **고쳤음**(2026-09-09 밤 · 커밋 `89effeca`) — 표기를 **「미정(발주기관 결정)」** 으로, 경고 머리를 **「관급/사급 미정 — 발주기관이 정하는 자리입니다」** 로. **값(`unknown`)·판정은 그대로**이고 문구만 바꿨음(시험도 그 낱말로 고침) |
---
## 검증 닫은 것 — V-8 · V-9 · V-10 · V-15
### 닫은 것 (2026-09-09 밤 · 근거는 검증 폴더와 아래 절)
- **V-8 산출근거 문구 ↔ 길이** — ⭕ 어긋날 자리 없음. B08 줄이
「돌쌓기(찰) H=2.5 · L=10.0m · **NO.4 ~ NO.4+10**」로 뜨고 정본 구간 80~90m 와 한 값
(표기는 `stationLabel` 한 벌로 냄, 커밋 참조).
- **V-9 표준도 하단표** — ⭕ 뜨고 있음. `standard_sheet_3` 에 Table 엔티티 22행이 들어 있고
갈무리로도 **찰 15줄**이 수량·단위까지 차 있음(메는 13줄). 「찰 11 · 메 8」은 옛 값.
- **V-10 표준도 4장** — ⭕ 도면 목록에 넷 다 뜨고 blank 0. **전체 장수는 기준이 못 됨.**
- **V-15 층따기 길이 칸** — ⭕ 칸이 서 있고 **면적 × 길이 → ㎥** 로 곱해짐. 길이가 비면
막고 사유(`input_missing`). 시험 둘이 잠금.
File diff suppressed because it is too large Load Diff
@@ -1,412 +0,0 @@
# 축 C 1장 첫 일감 — 공종을 이름으로 잇는 자리 전수 조사
작성 2026-09-13 · **랩탑 메인** · 읽기 전용 조사(코드 무변경) · 계약 문서는 `2026-09-13_축C_명세.md`(브레인 관리)
> **무엇을 훑었나** — B05·B06·B07·B08·B09 의 `.py`·`.ts` 전부(openwebcad 제외) + `resources/data_*` 매핑·원단위·갈래표.
> **물음 셋** — ① 공종을 **문자열로 잇는 자리** ② 흩어진 **별칭** ③ 품셈에 **코드가 없는 공종**.
> **읽는 법** — 줄 번호는 조사 시점(`main_laptop_1`, dev 병합 직후) 기준. ✔ 표시는 랩탑 메인이 소스·실행으로 직접 대조한 것.
**파일 줄임말** — 앞머리 `B08_Quantity_` · `B09_Estimation_` · `B07_DesignDetail_` 는 뗌.
| 줄임 | 파일 | 줄임 | 파일 |
| --- | --- | --- | --- |
| `MAP` | `resources/data_work_item_mapping/work_item_mapping_2026-01-01.json` | `MST` | `resources/data_work_item_master/work_item_master_2026-01-01.json` |
| `Mapping` | `Engine_Handoff_Mapping.py` | `Rows` | `Engine_Handoff_Rows.py` |
| `Prep` | `Engine_Preparation.py` | `Trench`·`Spoil` | `Engine_Handoff_Trench.py`·`_Spoil.py` |
| `ESum` | `Engine_EarthworkSummary.py` | `UQ`·`REV` | `Engine_UnitQuantity.py`·`_UnitQuantity_Revetment.py` |
| `MAT`·`RM` | `Engine_MaterialSummary.py`·`Router_Material.py` | `HSUM`·`HIN` | `Engine_HaulSummary.py`·`Engine_HaulInputs.py` |
| `UP`·`BoQ` | B09 `UnitPrice.py`·`BillOfQuantities.py` | `BoQR` | B09 `BillOfQuantities_Rows.py` |
| `RAX` | B09 `ResourceAxis.py` | `STJ` | `B05_Profile/B05_Profile_Structure_Types.json`(레지스트리) |
---
## 0. 한눈에
| 물음 | 결과 |
| --- | --- |
| ① 이름으로 잇는 자리 | **공종 찾기 12 · 성분 이름 24 · 갈래 이름 22 · 사유·상태 문구 5 · 자원 이름 11** (자리 단위, 4장 표) |
| ② 같은 것의 다른 이름 | **지반 갈래 어휘 8벌** · 공종 이름 별칭 **19묶음** · 별칭 사전 **11곳** · 정규화 규칙 사본 **4계열** |
| ③ 코드 없는 공종 | **품셈 밖 공종 11** · **구조물 일위대가 후보 17** · 코드는 있는데 줄에 안 붙는 것 **9** |
| 생산 쪽 코드 흐름 | **B05·B06·B07 에서 코드가 한 번도 안 흐름**(`FP-` 0건). 코드는 **B08 매핑표에서 처음 붙음** |
**가장 무거운 것 셋**
1. **부모 코드에 수량이 붙어 금액이 안 서는 공종이 실재함** ✔ — 흙깎기(리핑암) `FP-09-04` · 덤프·사토·표토 운반 `FP-10-12` ·
표토제거 `FP-09-15` 는 일위대가 제목이 **아예 없음**(2장 1-2)
2. **자원 축 파일 418/497 은 옛것·안 쓰는 것** ✔ — 지금 코드로 돌리면 **575줄 · 못 맞춤 508**(2장 1-1)
3. **이름이 한 글자만 어긋나도 값이 조용히 0·누락되는 자리 9곳** — 그중 **자재총괄에서 유로폼·면목·철근·떼가 빠지는 자리** ✔(7장)
---
## 1. ⚠ 명세와 어긋나는 실측 — 브레인 판정 필요
> 명세는 **고치지 않았음.** 판정 거리만 적음.
### 1-1. 자원 축 「418줄 · 못 맞춘 497줄」은 낡은 파일임 ✔ (명세 §1·§2③·§9·§11)
- `write_resource_axis()`(`RAX:665`)를 **부르는 곳이 없음** — 파일은 옛날에 한 번 쓰이고 멈춤
- 파일의 `source_master_file.sha256` = `3752af1e…` ≠ 지금 마스터 `34628999…` (마스터가 09-09 에 다시 생성됨)
- B09 는 파일을 안 읽고 **일위대가를 만들 때마다 메모리에서 새로 돌림**(`UP:594``build_resource_axis`)
- **지금 코드 · 지금 마스터로 돌린 값** (2026-09-13, 쓰기 없이 메모리 실행)
```
자원 줄 575 (노임 532 · 기계 43 · 자재 0) 공종 139 개에 붙음
못 맞춤 508 (공종 166 개)
건너뛴 표 참조 63 · 미판정 73 · 계수 19 ← 명세의 64·75·19 와 다름
```
| 못 맞춘 사유 | 줄 | STmate 목록(§11)으로 풀리나 |
| --- | --- | --- |
| 카탈로그에 없는 이름 | **271** | ✅ 풀릴 후보 |
| 뭉친 자원 줄의 이름을 못 풂 | **167** | ❌ **표 읽기 문제** — 목록이 와도 안 풀림 |
| 자원은 알았으나 값을 못 읽음 | 20 | ❌ 표 읽기 |
| 숫자 칸 수가 자원 열과 안 맞음(여러 모양) | 25 | ❌ 표 읽기 |
| 규격이 없어 같은 이름 중 못 고름 | 7 | △ `size` 칸이 있으면 |
| 작업조 표 · 같은 갈래 두 번 · 한 칸 밀림 등 | 18 | ❌ 표 읽기 |
**자재 줄이 0** — 관급 자재 카탈로그 6,999건을 싣고 도는데 품셈 자재 이름이 하나도 안 맞음.
### 1-2. 「부모 코드에 수량을 붙이지 말 것」(명세 §2) — 현행이 어기는 자리 ✔
부모에는 **두 모양**이 있어 규칙 한 줄로 못 닫음.
| 공종(우리 이름) | 붙은 코드 | 잎 | 모양 | 일위대가 제목 실측 |
| --- | --- | --- | --- | --- |
| 흙깎기(리핑암) | `FP-09-04` 암절취 | 01 암파쇄 · 02 집토 | **단계 합산형** | **없음** |
| 흙깎기(발파암) | `FP-09-05` 발파암 | 01 절취(발파 10%) · 02 깍기(90%) · 03 집토 | **단계 합산형** | `-01` 하나뿐 |
| 유로폼(옹벽 묶음 조각) | `FP-12-38` | 01 사용횟수 · 02 사용수량 · 03 설치·해체 | **단계 합산형** | `-03#간단/보통/복잡` 만 |
| 덤프 운반 · 사토 운반 · 표토 운반 · 뿌리 운반 | `FP-10-12` | 01 토사 · 02 암절취 · 03 발파암 | 갈래 고르기형 | **없음** (`find_variant_code("FP-10-12","토사")``None`) |
| 표토제거 | `FP-09-15` | 01 답구간 · 02 답외구간 | 갈래 고르기형 | **없음** |
| 초류종자살포 | `FP-05-24` | 01 기계/일반 · 02 기계/마사토 | 갈래 고르기형 | 잎 둘 있음, 부모는 없음 |
| 구조물터파기(용수 미입력 때) | `FP-09-13` | 18구분 | 갈래 고르기형 | 잎만 있음 |
- **갈래 고르기형** — 잎 하나를 고르면 됨. `Prep:441-447``FP-10-12` + `variant_value "토사"` 를 보내는데
B09 는 `#갈래` 제목만 찾음(`UP:499-503`) → 잎 `FP-10-12-01` 로 못 내려감 → 「후보」에 머묾(`BoQR:214-233`)
- **단계 합산형** — **내역 한 줄이 곧 부모**이고 단가는 잎의 합. 잎에 수량을 붙이면 오히려 틀림
**판정 물음**: 합산형 부모를 「부모에 수량 허용 + 일위대가가 잎을 합침」으로 둘지, `AX-ST` 묶음으로 세울지
### 1-3. 별칭은 **표 안에서만** 참임 (명세 §5 「양방향 · 한 이름이 두 코드에 걸리면 오류」) ✔
- `UP:464-477` 원문 주석 — 「리핑암 = 파쇄암」은 **일반적으로 거짓**. 발파암도 캐고 나면 파쇄암 상태임.
**산림품셈 10-11 f 표 안에서만** 성립(10-12 [주]③ 암절취 1.35 = 10-11 f 파쇄암 1/1.35)
- ⇒ 전역 양방향 사전으로 두면 「파쇄암 → 리핑암」이 발파암 운반에도 걸릴 수 있음.
**별칭표 줄에 「어느 코드 범위에서」 칸이 필요함** (예: `리핑암 ↔ 파쇄암 @ FP-10-11`)
- 지금 별칭이 **세 벌이고 서로 다름**
- `MAP` `ground_aliases` — 리핑암 → [파쇄암, **암절취**]
- `UP:478-480` `VARIANT_ALIASES` — 리핑암 → 파쇄암 (**암절취 없음**)
- B08 인계본 `ground_class_aliases`(`Handoff.py:170`) — **B09 가 안 읽음**(시험만 읽음)
### 1-4. `#갈래` 규약 자체가 「이름으로 잇기」임 (명세 §2 ④-5 ↔ §2 「이름으로 맞추면 결함」) ✔
- 일위대가 제목 **446개 중 202개**가 `B-<코드>#<한글 갈래 이름>` 꼴(`#파쇄암` · `#철근구조물` · `#굴착기(무한궤도) 0.2 · 소`)
- B08 이 보낸 원본값을 **공백 제거 + 물결표 통일** 뒤 글자 동등으로 맞춤(`UP:458-503`), 안 맞으면 수의 짝 · 「이하」 구간으로 내려감(`UP:506-537`)
- 마스터 표마다 **`variant_key` 칸이 이미 있음(456표)** — B09 는 안 읽고 원문에서 다시 뽑음
-**판정 물음**: `#갈래` 를 표시 이름 그대로 둘지, 갈래 번호(표 행 차례·`variant_key`)로 옮길지
### 1-5. 콘크리트가 어느 축인지 두 벌 (명세 §6)
- 전개식 `UQ:387-393` — 콘크리트·채움·버림콘크리트 → `material`(사용자 확정 3차 ⑥)
- 관측 원단위 `resources/data_structure_unit/structure_unit_observed_2026-01-01.json` — 같은 이름 → `unit_price`
- 배합 성분 막이 `verify_no_mix_components`(`UQ:1363-1374`)는 **알리기만** 하고 막지 않음
---
## 2. 공종이 태어나 흘러가는 길
```
B05 레지스트리(STJ) type_id · 한글 옵션값(흄관·찰쌓기·기초유…) 코드 없음
B06 횡단 설계 soil/ripping_rock/blasting_rock · 영문 면적 키 코드 없음
B07 횡단도 표 깍기·측구·쌓기·편책·노면다짐… (자기 이름) 코드 없음 · B08 이 안 읽음
B08 토공집계(ESum) 「흙깎기」+「토사/연암…」 · 「성토면다짐」… ← 공종이 **이름으로 태어남**
B08 구조물 전개(UQ) 성분 이름 「터파기」「기초잡석」「콘크리트」…
▼ Mapping.for_earthwork(이름, 갈래이름) · for_structure(type_id) · kind_codes[한글]
MAP 이름 → FP 코드 ← **코드가 처음 붙는 곳**
▼ 인계 줄 = work_item_code + name + variant_value(한글) + ground_class(한글)
B09 내역(BoQ) 코드로 찾음 + `B-코드#한글갈래` 로 단가 제목 맞춤 + 이름 부분문자열 검사
```
---
## 3. ① 이름으로 잇는 자리
표 끝 칸 — **곁에 코드**: 그 자리에서 코드가 이미 있는데 이름을 쓰나. **어긋나면**: 이름이 틀렸을 때 일어나는 일.
### 3-1. 공종 찾기 — 이름 → 코드 (12)
| 자리 | 문자열 | 무엇과 무엇을 잇나 | 곁에 코드 | 어긋나면 |
| --- | --- | --- | --- | --- |
| ✔ `Mapping:224-235` `for_earthwork` | (구분, 갈래) 한글 짝 | 집계 줄 → `MAP.earthwork[]` | ❌ | 코드 None → unmatched |
| ✔ `ESum:29, 108-221` | 흙깎기·측구터파기·보정량계·성토·성토면다짐·초류종자살포·지장목제거·층따기 | 공종 **이름이 태어나는 곳** | ❌ | — |
| ✔ `ESum:96` | 비율 없으면 갈래 이름 「암」 한 줄 | `MAP` 에 「암」 없음 | ❌ | 조용히 unmatched |
| ✔ `Trench:42-45, 176, 246, 292` | 구조물터파기·기초잡석·되메우기 | **성분 이름 = 구분 이름**으로 `for_earthwork` | ❌ | 코드 None |
| ✔ `Rows:111-118` | `GROUND_SPLIT_GROUPS` · `SLOPE_GROUPS` · `SUBTOTAL_GROUPS` · `HAUL_SUMMARY_GROUPS` | 구분 이름으로 줄 성격 판정 | ❌ | 합계 줄이 내역에 섬 |
| ✔ `Rows:88` | `AREA_TIMES_DEPTH_GROUPS {"층따기"}` | 면적×길이 환산 대상 | `FP-09-18` 있음 | 환산 안 됨 |
| ✔ `Rows:351-358` + `MAP:198-202` | 메쌓기·찰쌓기 | `bond` 옵션값 → `FP-13-06-01/02` | ❌ | 공종 미정 |
| `Engine_Pipe.py:122, 141-143` + `MAP:376-380` | 파형강관·흄관·VR관 | `pipe_kind` 옵션값 → `FP-12-11-0x` | ❌ | 막힘 사유 |
| `MAP:226-228``Preparation_Ancillary.py:56-60` | 가설창고(컨테이너) | 매핑은 이름 키 · 파이썬은 코드 직접 | 두 벌 | 밑수 대조 누락 |
| `MAP:235-267` `pending_user` | 지장목제거 · 흙깎기/측구터파기 암 | 이름 키로 후보 목록 | — | — |
| `B09 MachineProductivity_Reference.py:130-133, 159` | 「…와 동일」 원문 → 마스터 **이름** → 코드 | 원문 참조 따라가기 | 원문에 코드 없음 | 이름 겹치면 실패(`:162`) |
| `B09 FactorChoices.py:200, 211, 223` | 흙깎기(기계)·노체포설·층따기 | 표시 이름 박힘(코드 키) | ✅ | 표시만 |
### 3-2. 성분 이름 — 원단위 성분 → 축·공종·자재 (24)
| 자리 | 문자열 | 무엇과 무엇을 잇나 | 곁에 코드 | 어긋나면 |
| --- | --- | --- | --- | --- |
| ✔ `UQ:377-416` `DESTINATION` | 성분 23개 | 성분 이름 → 축(토공·자재·일위대가·참조) | ❌ `Component` 에 코드 칸 없음 | KeyError 또는 기본값 |
| ✔ `REV:361, 481, 511, 584` | `DESTINATION.get(name, "quantity")` | 유로폼·면목·이형철근 D13·떼 → **없는 축** | ❌ | **자재총괄에서 빠짐**(7장) |
| `UQ:1004` | `DESTINATION.get(stone_name, "material")` | 돌 종류 → 축 | ❌ | 기본값이 `REV` 와 반대 |
| ✔ `UQ:1488` | `== "버림콘크리트"` | 버림 줄 → 기초잡석 줄 산출 | `FP-12-25` | **기초잡석 줄 사라짐** |
| `UQ:1601` · `HIN:43-44, 104, 106` | 채집석 · 잔토처리 | → 사토 공제 · 유토곡선 입력 | ❌ | **0 으로 감** |
| ✔ `Trench:165, 218-233` | 터파기·되메우기·잔토처리·기초잡석 | `destination=="earthwork"` + 이름 | 매핑엔 있음 | 줄 안 섬 |
| `REV:455-457` | 원문 「바닥파기」를 「터파기」로 개명 | 토공 축에 닿으려고 이름을 맞춤 | ❌ | 주석에만 있는 의존 |
| ✔ `Rows:320-330` + `MAP:160, 174, 195` + `UQ:751, 934` | 돌쌓기·큰돌쌓기 | `billing_component` 이름 → ㎡ 수량 | 코드 있음 | **㎡ 수량 잃음** |
| ✔ `Rows:476, 529` | 콘크리트·버림콘크리트·레미콘 | 타설 대상 성분 | ❌ | 타설 줄 누락 |
| ✔ `Mapping:128, 369-379` + `MAP:349` | 이형철근·원형철근·철근(앞머리) | 성분 이름 → 철근/무근구조물 | ❌ | **무근으로 조용히** |
| ✔ `MAP:279-322` + `Mapping:305-319` | 콘크리트·버림콘크리트·합판거푸집·유로폼·이형철근 D13/D16·기초잡석 | 묶음 조각 ← 성분 이름 | 조각은 코드 | 조각 물량 0(`MAP:327` 자인) |
| `UQ:1372` · `:421-423` | 시멘트·모래·자갈… 정확 이름 | 배합 성분 막이 | ❌ | 표기 변형이면 통과 |
| ✔ `MAT:115, 137, 234-237` | 이형철근 D13/D16→이형철근 · 콘크리트류→레미콘 | 성분 → 할증표 이름 | ❌ | 「할증률 미확보」 |
| ✔ `MAT:325` | `rate_for(lookup)` 조건 없이 부름 | 레미콘 무근/철근 · 철근 복잡 | — | **늘 기본 율**(7장) |
| `MAT:271-273` | 키 `(name, unit)` | 규격(채움콘크리트 180/210 · 관경) 버림 | ❌ | 다른 규격이 합쳐짐 |
| `MAT:316` · `MatGrid.ts:335-357` | 자재 이름 키 | 관급/사급 선택 저장 | ❌ | 이름 바뀌면 선택 고아 |
| ✔ `RM:265` | `.endswith("규준틀")` | 준비공 줄 → 규준틀 자재 | `FP-11-02/03` 곁에 있음 | **규준틀 자재 전부 빠짐** |
| `Prep:459-463, 468, 485, 509``B08_Quantity_UI_Page.ts:131-141` | 각재 50×50 · 판재 T12 · 못 · 비탈 규준틀 | 재료 이름·손율·준용 주석 | `FP-11-02` | 사용자 값 무시 |
| `Engine_Formwork.py:30, 97` | 합판거푸집·유로폼·문양거푸집·거푸집 | 성분 → 전용 횟수 | 매핑엔 있음 | 횟수 없음 |
| `Engine_ObservedUnit.py:53-64, 117-131` | 반중력식·A-TYPE·「800」 · 단위 m/㎡ | 옵션값 ↔ 관측표 규격 · 단위 → 배율 | ❌ | 800.0≠"800" · **모르는 단위 ×1** |
| `UQ:1230, 1239` | 「A-TYPE+집수정」 | 날개벽 옵션 → 이중계상 경고 | ❌ | 경고 사라짐 |
| B09 `MaterialCatalog.py:80-95` | 이름·규격 정확 일치 | B08 자재 → 관급 품목 코드 | ❌ 인계 자재에 코드 없음 | 못 찾음 |
| B09 `Guards.py:230, 255-259, 272, 283, 293-297` | 물빼기파이프·물구멍·고임돌·채움콘크리트 · 「큰돌쌓기」 in source | 자재 이름 · 공종 이름 부분문자열 | 공종 쪽은 `FP-13-06` | 가드 빗나감 |
| B07 `Engine_Standard_Sheet.py:79-86, 102-120` · `Router_Standard.py:114` | `sheet_key` = json(type_id·높이·옵션) · 성분 이름 | 표준도 장 ↔ 편집 저장 | ❌ | 편집 고아 |
### 3-3. 갈래 이름 — 지반·관종·구조물 종류 (22)
| 자리 | 문자열 | 무엇과 무엇을 잇나 | 곁에 코드 | 어긋나면 |
| --- | --- | --- | --- | --- |
| ✔ `Rows:49-61` + `Mapping:122` | 연암… + ripping/blasting → 리핑암·발파암 | 사용자 갈래 → 매핑 갈래 이름 | ❌ | 「시공법 미지정」 |
| ✔ `Rows:55` · `Handoff.py:178` | `== "토사"` | 토사 판정 | ❌ | 안내 목록 틀림 |
| ✔ `Rows:260-261` | `variant_value = "리핑암"` | 운반 갈래 → B09 `#파쇄암` | 부모 코드만 | 별칭 없으면 단가 없음 |
| ✔ `Spoil:57, 168-169` | ea/rr/br → 토사·리핑암·발파암 · **`variant_axis None`** | 사토 운반 갈래 | ❌ | 운반 줄과 모양이 다름 |
| ✔ `Prep:441-447` | `FP-10-12` + 「토사」 | 표토 운반 갈래 | 부모만 | **금액 안 섬**(1-2) |
| ✔ `Trench:58-60, 68-79` | soil/ripping_rock/blasting_rock · 육상/용수 · 0~1m | 세 축 **자리 셈**으로 잎 코드 조립 | 부모 | 품셈 차례 바뀌면 조용히 틀림 |
| `Trench:106-111` · `UQ:820-821` · B06 `UI_Cross_Excavation.ts:30-35` · B07 `Engine_Cad_StandardFigure.py:193-195` | 기초유·기초버림 | 옵션값 → 기초 깊이 | ❌ | 비탈분만 셈 |
| ✔ `UP:483-537` `find_variant_code` | 정규화 갈래 이름 · 수의 짝 · 「이하」 | 원본값 → `B-코드#갈래` | 부모까지만 | 후보에 머묾 |
| `B09 MachineProductivity_Dozer.py:343-355, 410-418` | `"암" in label` · `.get("토사")` | f 표 파쇄암/발파암 ↔ E 표 암석 | ❌ | 계수 빠짐 |
| ✔ `BoQ:507-510` | `"깎기" in name or "절취" in name` | 줄 이름 → 총 절토량(운반 ≤ 절토 검산) | ✅ 코드 곁에 있음 | 검산 틀림 |
| ✔ `BoQR:31-36` | `row.name == item.name` | 운반 줄 이름(`"dozer 운반"`) → 첫 줄 단가 | ✅ | **토사·리핑암 줄 이름이 같아 엉뚱한 단가** |
| B09 `QuantityDigits.py:30-63, 76-85` | 공사연장·떼·돌쌓기·옹벽·콘크리트·철근 부분문자열 | 줄 이름 → 소수 자리 | ✅ | 자리수 틀림 |
| `HSUM:41-43, 192-198` | 토사·리핑암·발파암 차례 목록 | 운반 줄 갈래·정렬 | ❌ | 끝으로 밀림 |
| `Engine_EarthworkTable.py:59, 181` | `factors.get(kind) or factors["soil"]` | 저장 암 종류 → 환산계수 C | ❌ | **토사 계수로 조용히** |
| B06 `UI_Cross_Design.ts:80-84` · `Router_Design.py:189` | 「암」이면 모두 ripping_rock · 기본값 ripping_rock | 지반유형 저장 | ❌ | `FP-09-03-02``FP-09-04` 갈림 |
| `B08_Quantity_UI_Page.ts:408-431` · `Router_Earthwork.py:141-142` | 「암」·「토사」 · 암 갈래 이름 키 | 입력 칸 · `rock_ratios_pct` 키 | ❌ | 세트 바꾸면 고아 |
| ✔ `Mapping:243-257` · `MAP.structure[]` | `type_id` | 구조물 → 공종(영문 id 가 공종 대리) | ❌ | unmatched |
| `REV:29-32, 63-85, 160, 263` | 돌쌓기(찰)·돌붙임(메)·「돌」·`spillway == "없음"` | 옵션값 → 계수·차감 | ❌ | 「없음」 외 글자면 **차감됨** |
| `UQ:97, 134, 221, 479, 519, 695` | 야면석·호박돌 · 성토/절토 · 찰/메 · 「40~60」 | 옵션값 → 표 키 | ❌ | 기본값 · 물결표 불일치 |
| B06 `Engine_Structures_Wall.py:33-39``common_util_structure_walls.ts:56-62` | type_id → 돌쌓기(찰)/(메) · TS `includes("찰")` | 벽 높이 한계 | ❌ | 큰돌쌓기가 bond 무관하게 「메」 |
| B05 `STJ` `pipe_kind`·`bond`·`foundation`·`inlet_type`·`wing_wall_type` | 한글 옵션값 | B08 로 가서 코드·갈래가 됨 | ❌ | 위 각 줄 |
| B05 `Structures_Schema.py:120-122` · `Engine_Sections.py:198-207` · `Structures_Migration.py:53, 64-78` | 측점 이름표 `"{관종} D{관경}"` · 한글 이름 집합 | 측점 식별 | ❌ | 이관 누락 |
### 3-4. 공종 밖 문자열 — 사유·상태 문구가 판정 키 (5)
| 자리 | 문자열 | 무엇을 가르나 |
| --- | --- | --- |
| ✔ `Rows:295-304` `blocked_of` | 「입력되지 않았습니다」·「자료에 없습니다」·「산출식이 아직 없습니다」 | 사람 말 문구 조각 → 막힘 갈래 |
| `Prep:629-630` · `Engine_Preparation_Status.py:3-13` | `status == "값 있음"` | 한글 표시 문구가 상태값 |
| B09 `UI_Page.ts:318-319` | `note === L("…Adopted")`(채택) | 서버 문구 → CSS (곁에 `key` 있음) |
| B05 `STJ:490` · `RM:73-76` · `common_util_structure_lengths.py:73` | `design_owner == "횡단 설계"` | 한글 문구가 건너뛰기 깃발 |
| B07 `Engine_Cad_Table.py:24-58` → manifest `quantity_table(s)` | 깍기·편책·노면다짐… | **읽는 곳 없음**(막다른 길) |
### 3-5. 자원 이름 — B09 자원 축 (11)
품셈 칸에는 코드가 없어 **이름 잇기가 불가피한 자리**가 대부분임. 「곁에 코드」가 ✅ 인 줄만 고칠 거리.
| 자리 | 무엇 | 곁에 코드 |
| --- | --- | --- |
| `RAX:124-143, 321-350` | 품셈 칸 → 공백 지운 이름 색인 → 카탈로그 코드(규격은 동률 깨기) | 불가피 |
| `B09 MachineProductivity.py:158-185` | 기계 별칭 → 이름 포함 → 궤도 낱말 → 용량 | 불가피 |
| `B09 ResourceAxis_Transposed.py:278-302` | 뭉친 칸 `startswith` | 불가피 |
| `B09 MachineProductivity_Reference.py:233-255` | 「대형브레이커(㎥/hr)」 부분문자열 | 불가피 |
| `B09 MachineProductivity_Dozer.py:205-211` | `"습지 불도저"`·`f"불도저({궤도})"` 조립 후 `==` | △ 코드표로 대체 가능 |
| `B09 ResourceAxis_ChooseOne.py:148-151` | 같은 이름 다른 규격 감지 | △ 코드 앞머리 |
| `UP:157, 290-293` | 브레이커·리퍼·집게 낱말 → 부착장비 | ✅ 코드 앞머리가 `:205` 에 이미 있음 |
| `UP:647-661` | 카탈로그 기계 이름 ⊂ 줄 이름 | 불가피 |
| `B09 MachineOperating.py:46, 157-169` | 덤프트럭·트럭·트레일러 → 운전 직종 | ✅ 기계 코드 앞머리 |
| `B09 Lists_Sources.py:159-164` | 건설기계운전사… 이름 → A10 표 | ✅ 노임 1048/1049/1050 |
| `RAX` `ResourceCatalog.aliases` | **읽어 오고 안 씀** | — |
---
## 4. ② 흩어진 별칭
### 4-1. 지반 갈래 어휘 — **8벌**
| # | 어휘 | 쓰는 자리 |
| --- | --- | --- |
| 1 | `soil` · `ripping_rock` · `blasting_rock` | B06 `Section_Schema.py:44` · `config_system_design.py:335-339` · `Trench:58` |
| 2 | 토사 · 리핑암 · 발파암 | `MAP.earthwork` · `ESum` · `HSUM:41` · `Spoil:57` · `B08_Quantity_UI_ConversionFactors.ts:39-43` |
| 3 | `ea` · `rr` · `br` (`_m3`) · EA/RR/BR | `HSUM:41` · `Spoil:57` · `Router_Earthwork.py:189` · `common_util_mass_haul_balance.ts:414-423` · locale |
| 4 | 암절취 · 발파암 (품셈 절 이름) | `MST` 9-12·10-12·9-13 잎 · **`UQ:1399-1403`·`HIN:130` 은 ripping_rock → 「암절취」** (1·2벌과 충돌) |
| 5 | 파쇄암 (품셈 10-11 f 표) | `UP:478-480` · `MAP.ground_aliases` · `Dozer` |
| 6 | 풍화암 · 연암 · 보통암 · 경암 · 「암」 | `config_system_design.py:389-392` · `common_util_project_settings.py:57-61` · `ESum:96` |
| 7 | `ripping` · `blasting` (시공법) | `Mapping:122` |
| 8 | 암석 (품셈 E 표) | `Dozer:343-355` |
**기본값도 갈림** — B06 `Router_Design.py:189` ripping_rock · `B06 Engine_SpoilFill.py:195` soil · B07 `Router_Support.py:352` soil · `Engine_EarthworkTable.py:42` ripping_rock.
### 4-2. 공종 이름 별칭 — 19묶음
| 같은 것 | 이름들 (자리) |
| --- | --- |
| 흙깎기 | 흙깎기(`ESum`) · **깍기**(B07 `Engine_Cad_Table.py:32`, 오기) · 절토(locale) · 토사깍기(`MST` 9-3) |
| 측구터파기 | 측구터파기 · 측구(B07) · 측구(옆도랑) · 옆도랑 · `ditch_side`**갈래 수 답 대기** |
| 성토 | 성토 · 쌓기(B07) · 노체포설(`MST` 9-16-1) · `embankment`·`fill_area_m2` |
| 노면다짐 | 노면다짐(B07) · 노면고르기(실무) · 노체다짐(9-16-2) — **B08 줄 없음 · 답 대기** |
| 초류종자살포 | 초류종자살포(성토면/절토면) · 성토파종/절토살포(B07) · 법면보호공(종자파종) · 「초류종자 살포」 · 씨앗뿜어붙이기(`MST` 5-24) · `seed_spray_*`·`slope_protection` |
| 지장목제거 | 지장목제거 · `tree_removal` · `obstacle_removal` · 뿌리뽑기+잡관목제거(실무 두 줄) |
| 층따기 | 층따기 · `benching` · `bench_cut` |
| 면고르기 | 면고르기 · `grading_*` · `face_dressing` |
| 제근 | 제근 · `grubbing` · 제근·뿌리다듬기 |
| 운반 | 무대(종방향유용토)/도자운반/덤프운반(`ESum:238`) · `free_haul`/`dozer`/`dump_truck` · 종무대/**도쟈**/덤프(locale 오기) · 불도저 운반/덤프 운반(`MST`) · **「dozer 운반」**(인계 줄 이름 `Rows:237`) |
| 사토장 | 사토장 · 유용토운반작업장(구 사토장) · `spoil_bank`/`spoil_fill` · 사토 |
| 레미콘 | 콘크리트 · 레미콘 · 레디믹스트 · `ready_mixed` |
| 집수정 | 집수정(`MAP:185`) · 유입부 집수정(`UQ:1223`) · 배수관 유입부 집수정(`Wording.py:29`) |
| 가설창고 | 가설창고(컨테이너)(`MAP`·`Ancillary`) · 가설창고(개소)(locale) |
| 기슭막이·대피로 | 기성막이(옛) ↔ 기슭막이 · 대피로 ↔ 대피소 — py·ts 두 벌(`Structures_Migration.py:44` · `B05_Profile_UI_Page.ts:96`) |
| 배수관 | 배수관 · 배관 · 배수시설 · `"{관종} D{관경}"` · 관종 흄관/VR관/파형강관 ↔ config 이중벽관/삼중벽관/파형강관(`Section_Common.ts:69` 는 config 이름만 앎) |
| 돌쌓기 | 돌쌓기(찰/메) · 돌(찰/메) · 돌붙임(찰/메) · 찰/메쌓기 — `includes` 로만 붙음 |
| 규준틀 재료 | 각재 50×50 ↔ 각재 · 판재 T12 ↔ 판재 (할증표 이름과 안 맞음) |
| 철근 | 철근 현장가공 및 **조립**(`MAP`) ↔ **조림**(`MST` FP-12-03, 원문 오기) · 이형철근 D13/D16 ↔ 이형철근 |
### 4-3. 별칭 사전·이름표 자리 — 11곳
| # | 자리 | 무엇 | 비고 |
| --- | --- | --- | --- |
| 1 | `MAP.ground_aliases` | 리핑암 → 파쇄암·암절취 | 공식 |
| 2 | `UP:478-480` `VARIANT_ALIASES` | 리핑암 → 파쇄암 | 1과 다름 |
| 3 | `Handoff.py:170` `ground_class_aliases` | 1을 인계본에 실음 | **B09 안 읽음** |
| 4 | `B09 MachineProductivity.py:53-59` | 유압식백호우·백호·굴삭기 → 굴착기 | BACK-HOE·B/H 없음 |
| 5 | `B09 ResourceAxis_UnitBasis.py:50, 111` | (FP-13-02-04, 인부) → 보통인부 | 코드 범위 있음 ⭕ |
| 6 | `B09 MachineProductivity_Reference.py:36, 92-93` | 용수 → 육상 | 이름 고쳐 쓰기 |
| 7 | `MAT:234-237` | 이형철근 D13/D16 · 콘크리트류 → 할증표 이름 | D 규격 둘뿐 |
| 8 | 운반 이름표 — `ESum:238` · `SummaryGrid.ts:85-89` · locale · B07 `Engine_Cad_MassHaul.py:100-104` | 4벌 | |
| 9 | 지반 이름표 — `HSUM:41-43` · `Router_Earthwork.py:189` · `ConversionFactors.ts:39-43` · `UQ:1399-1403` · `HIN:130` | 5벌, **값이 서로 다름** | |
| 10 | 구조물 이름표 — `STJ name` · `Wording.py:22-38` · `B05 Page_Helpers.ts:239-245` · `B07 Engine_Cad_Plan.py:74-78` · `Box_Panel.ts` · `Ford_Panel.ts` · locale | 6벌 이상 | |
| 11 | 관급·타설 이름표 — `MAT:70-83``MatGrid.ts:216-226` · `UI_Page.ts:223-227` ↔ locale `:727-729` · `masonry_class` JSON ↔ `MAP` bond_codes | py·ts 두 벌씩 | |
### 4-4. 정규화 규칙 사본 — 4계열
| 계열 | 사본 | 어긋남 |
| --- | --- | --- |
| 물결표·붙임표 | `RAX:152`(단일 정본이라 선언) · `B09 FactorChoices.py:43` · `B09 MachineProductivity_Reference.py:56` · `ThreeAxis.py:52` · `MachineOperating.py:138` | `FactorChoices``` 빠짐 · `Reference```·`` 빠짐 · 붙임표를 범위로 보는 곳과 뺄셈으로 보는 곳이 갈림 |
| 공백 제거 | 약 15곳 | `split()`(모든 공백) ↔ `replace(" ", "")`(ASCII 공백만) 두 방식 |
| 단위 표기 | `Engine_BasisUnit.py:27-37` · `BoQR:452-470` · `ResourceAxis_UnitBasis.py:44` · `MachineProductivity.py:191` · `ResourceAxis_Sources.py:160, 179` | 5벌. `MAT`·`ObservedUnit` 은 BasisUnit 안 씀. kg/㎏ 둘 다 쓰임 |
| 계수 기호 k·f·E·Cm | `Reference.py:39-47` = `FactorChoices.py:46-54` · `MachineProductivity.py:45-48` | 3벌 |
### 4-5. 매핑표 밖에 박힌 공종 코드 — 두 번째 코드 장부
| 자리 | 코드 |
| --- | --- |
| `Prep:174, 189, 199, 291, 314, 317, 368, 388, 441, 443, 533, 553` | FP-09-15 · 09-21 · 08-11 · 14-02 · 09-20-02 · 10-12 · 11-02 · 11-03 |
| `Preparation_Ancillary.py:59` | FP-11-01 (`MAP` 과 두 벌) |
| `resources/data_masonry/masonry_class_2026-01-01.json:46-53` | FP-13-06-01/02 (`MAP` 과 두 벌) |
| B09 `Transport.py:41-60` · `BasisSheet.py:146` | FP-10-04 (+ 지어낸 갈래 `#트레일러20ton`) · 146 은 상수 대신 글자 |
| B09 `KnownGaps.py` · `FactorChoices.py:198-234` · `Compaction.py:48-71` · `WorkItemUnit.py:147-164` | 원문 [주] 를 코드 키로 옮겨 적은 곁표 |
---
## 5. ③ 품셈에 코드가 없는 공종
갈래 칸은 **후보 제안**임(명세 §2 ② `AX-ST`·`AX-WK` 기준). 확정은 브레인·사용자.
### 5-1. 품셈 밖 공종 — `AX-WK` 후보 (11)
| 공종 | 지금 식별 | 자리 |
| --- | --- | --- |
| 지장목제거 — 뿌리뽑기 · 잡관목제거 | 이름 · 코드 None · `pending_user`(FP-04-01/02/03 후보) | `ESum:181-215` · `Prep:121-142` · `MAP:239-246` |
| 도수로·산비탈수로 (`chute`) | type_id · `pending_user` | `STJ:608` · `MAP:256` |
| 절토사면 배수로 (`slope_drain`) | type_id · `pending_user` | `STJ:640` · `MAP:262` |
| 노출형 횡단수로 (`cross_drain_exposed`) | type_id · 매핑 없음 | `STJ:451` |
| 콘크리트 포장 (`pavement_concrete`) | type_id · 매핑 없음 | `STJ:2347` |
| 편책 | B07 표 칸만 · 「별도 일위대가 필요」 | B07 `Engine_Cad_Table.py:48` · `Cross_Quantity.py:28` |
| 노면다짐 | B07 표 칸만 · B08 줄 없음 · 답 대기 | B07 `Engine_Cad_Table.py:57` |
| 국가지점번호판 · 임도 안내판 · 차단기 · 수방대책 자재 | `key` + 이름 · `code None` | `Preparation_Ancillary.py:24-73` |
### 5-2. 구조물 — `AX-ST` 후보 (17)
| 구조물 | 지금 식별 | 자리 |
| --- | --- | --- |
| 옹벽 | `composite` 묶음(조각은 FP) | `MAP:273-328` |
| BOX암거 · 세월교 · 소형사방댐 · 소단 | type_id · 매핑 없음 | `STJ:205, 339, 1803, 1481` |
| 기슭막이 · 골막이 · 흙막이 · 개거 · 바닥막이 | type_id · **원단위는 서는데 코드 없음** · 사유 문구 낡음(「원단위 없음」) | `UQ:1149-1168` · `Prep:586-597` |
| 관보호공 날개벽 | type_id · 매핑 없음 | `UQ:1146, 1224` |
| 대피소 · 작업장 · 회차로 · 가드레일 | type_id · 「quantity」 보기만 있음 | `STJ:2042, 2071, 2098, 2140` |
| 사토장 · 토취장 | type_id · 매핑 없음 | `STJ:2198, 2258` |
### 5-3. 코드는 있는데 줄에 안 붙거나 금액이 안 서는 것 (9)
| 공종 | 까닭 | 자리 |
| --- | --- | --- |
| 흙깎기·측구터파기 암(풍화암·연암·보통암·경암) | 시공법(리핑/발파) 미지정이면 코드 None | `Rows:49-61` · `MAP:248-254` |
| 흙깎기(리핑암) · 덤프 운반 · 표토제거 | 부모 코드 → 일위대가 제목 없음 | 1-2 |
| 뿌리 운반 | `FP-10-12` 부모 + **갈래 없음** | `Prep:376-389` |
| 표토 운반·적치 | 대기 상태일 때 코드 None, 값 서면 FP-10-12 — **코드가 상태에 따라 바뀜** | `Prep:406-441` |
| 뿌리 적재 | `FP-09-20-02` 인데 단위 ㎡ ↔ 원문 10주당 | `Prep:357-369` |
| 구조물터파기 | 용수 미입력이면 부모 `FP-09-13` | `Trench:246-264` |
| 물넘이포장 | `FP-12-06` 밑수가 「일당」(작업조형) · `thickness_cm` 옵션이 레지스트리에 없음 | `MAP:188-191` · `UQ:1141` |
| 노체다짐 `FP-09-16-02` | 줄로 안 셈 — 답 대기(PLAN 5장) | `MAP:65` |
| 돌붙임(골막이·바닥막이 안) | `FP-13-05-01/02` 가 마스터에 있으나 **매핑 없음** · 성분 이름으로만 감 | `REV:287-288, 552` |
### 5-4. 공종이 아닌 것 — 설계상 코드 없음 (참고)
- **내역에 안 서는 줄**: 무대(`MAP:147-151` null) · 보정량계 · 잔토처리(`Trench:307-317`) · 입적·석적(`UQ:942-981` reference) · 사방 시설 「해당 없음」 줄(`Prep:574-585`)
- **자원·자재 축**: 채집석(haul_deduction) · 모르터(unit_price, 코드 없음) · 고임돌·막자갈·깬돌·물구멍관 · 자재총괄 전 줄
- **B09 금액 줄**: 법정경비 14종·안전관리비·관급자재대·폐기물처리비 — 영문 `key`(`B09 Statutory.py:59-105` · `Engine_Cost.py:314-521`)
- **B09 지어낸 코드**: `M-FUEL-경유`(`UP:72`) · `X-<코드>#조합`(`UP:354-376`) — 명세 §2 ①에 `#조합` 은 있음, `M-FUEL-*` 은 없음
---
## 6. B07 — 조사 결과 요약
- **횡단도 아래 표**(`Engine_Cad_Table.py:24-58`)는 **B07 만의 이름**(깍기·측구·쌓기·편책·노면다짐·사토장)이고 코드 없음
- 층따기·면고르기·지장목제거·성토파종은 측점별 **사면 길이**를 읽음 — B08 집계(측점 사이 면적·반영률)와 **다른 값**
- 표토제거·편책·제근·노면다짐 칸은 `source["quantities"]` 를 읽는데 **그 키가 없어 늘 빈칸**(`Router_Support.py:288, 298-299`)
- **표준도**(3장 이관 대상)는 B08 `build_unit_table` 을 그대로 부름 — **계산 중복 없음**. 제목은 레지스트리 `name`, 줄 이름은 B08 성분 이름
- **CAD 편집값**은 확정 때 manifest 에 저장되나 **읽는 곳 없음**(`Router.py:430-449` · `Router_Support.py:645-649`)
---
## 7. 곁에서 드러난 것 — 이름 때문에 값이 조용히 빠지는 자리
조사 범위(이름 잇기)에서 드러난 **결함 후보**. 고치지 않았음.
| # | 자리 | 무엇이 빠지나 | 확인 |
| --- | --- | --- | --- |
| 1 | `REV:361, 481, 511, 584` | 개거·흙막이의 유로폼·면목·이형철근 D13·떼가 `DESTINATION` 에 없어 축 「quantity」 → **자재총괄에서 빠짐**, 건너뛴 수는 화면에 안 뜸 | ✔ 소스 |
| 2 | `RM:265` | 준비공 줄 이름이 「…규준틀」로 안 끝나면 **규준틀 자재 전부 빠짐** | ✔ 소스 |
| 3 | `UQ:1488` | 「버림콘크리트」 이름이 다르면 **기초잡석 줄 사라짐** | ✔ 소스 |
| 4 | `MAT:325` | 할증 조건을 안 넘김 → 레미콘 늘 무근 율 · 철근 복잡 율 안 붙음 | ✔ 소스 |
| 5 | `BoQR:31-36` | 운반 단가를 **줄 이름**으로 찾음 — 토사·리핑암 줄 이름이 같아(「dozer 운반」) 첫 줄 단가로 무대 검산 | ✔ 소스 |
| 6 | `Prep:441-447``UP:499-503` | 표토·덤프·사토 운반 **금액 없음**(`FP-10-12` 제목 0) | ✔ 실행 |
| 7 | `Engine_EarthworkTable.py:59, 181` | 모르는 암 종류면 **토사 환산계수**로 조용히 | 도우미 조사 |
| 8 | `REV:263` | `spillway` 가 「없음」 외 글자면 관 단면을 **차감** | 도우미 조사 |
| 9 | `Engine_ObservedUnit.py:117-131` | 관측표 단위가 m·㎡ 가 아니면 **×1 개소**로 | 도우미 조사 |
**범위 밖이나 무거운 것 하나** ✔ 소스
B07 도면 확정(`Router.py:455-495``Router_Support.py:666-692`)이 B06 `compute_cross_design`
**지반유형·단면유형·측구위치·곡선확폭만 넣고 다시 돌려** `cross_sections` 설계에 **덮어씀**.
암반 경계(`rock_boundary_offset_m`)·표준값·사토 성토·소단·측구 형식·포장·절토 경사가 빠짐 →
**B08 이 읽는 토사/암 나눔·측구 나눔·사토 공제가 확정 순간 바뀔 수 있음**. 수치 재현은 안 함.
---
## 8. 1장 다음 일감에 넘기는 것
| 1장 일감 | 이 조사에서 넘길 것 |
| --- | --- |
| 별칭표를 한 파일로 | 4-3 의 11곳 · 4-1 의 8벌 · **「코드 범위」 칸**(1-3) |
| B08 ↔ B09 문자열 → 코드 | 3-1 의 12자리 · 3-3 의 `BoQ:507-510`·`BoQR:31-36`·`QuantityDigits` · **부모 코드 판정**(1-2) |
| 못 맞춘 줄 줄이기 | 기준 수치는 **508**(1-1) · 271 만 목록으로 풀림 · 167 은 표 읽기 |
| 이중계상 경계를 축으로 | 1-5 콘크리트 두 벌 · `UQ:1363-1374` 알리기뿐 · 3-2 성분 이름 막이 |
| `undetermined` 판정 | 지금 코드 기준 **73표**(명세 75) |
@@ -241,6 +241,21 @@ AR-<종류>-<8자리 16진> 카탈로그에 없는 자원
`소운반인부`**카탈로그에 아예 없음** ⇒ 보통인부로 붙이는 것은 **대체**이지 별칭이 아님.
**대체는 도메인 판단이라 사용자 확인 자리임.**
### ⭐ 후보 넷 판정 (2026-09-13 브레인, 사용자 위임)
랩탑 메인이 「범위 별칭 후보」로 올린 넷을 갈랐음. **별칭은 하나뿐임.**
| 후보 | 판정 | 까닭 |
| --- | --- | --- |
| 화약공 → **화약취급공** | ✅ **별칭** · scope `FP-09-05` 계열 | 「화약취급공」이 건설업 노임 **표준 직종명**이고 품셈이 표 안에서 줄여 적은 것. 같은 사람이라 단가가 갈릴 이유 없음 |
| 소운반인부 → 보통인부 | ⛔ **별칭 아님 — 「표 읽기」로 옮김** | 품셈 원본에 **0건**이고 어느 사전에도 없음. 표준품셈에서 **소운반은 직종이 아니라 작업 성격**이고 품은 보통인부로 계상함. `FP-12-10`·`FP-12-15` 에서 나온 것으로 보아 **표의 한 칸을 자원 이름으로 읽었을** 가능성이 큼 |
| 소형브레이커 → (공압식) | ⛔ **별칭 아님 — 「규격 미정 · 후보 2」** | 공압식·전기식은 **다른 장비**이고 단가가 다름. 품셈이 형식을 안 정했다는 뜻 |
| 공기압축기(3.5㎥/min) → (이동식) | ⛔ **별칭 아님 — 「규격 미정 · 후보 N」** | 품셈은 **용량**으로, 카탈로그는 **형식**으로 부름 — 축이 다름. ⚠ 이동식 목록에 3.5㎥/min 이 있으면 **규격으로 바로 걸림**(별칭 불필요) — 그것부터 볼 것 |
**별칭 1 · 표 읽기 1 · 규격 고르기 2.**
⚠ 「이름이 안 맞는다」를 전부 별칭으로 풀려 하면 **근거 없이 단가가 붙음.**
먼저 **표를 잘못 읽은 것인지**, 다음 **규격이 안 정해진 것인지**, 마지막이 별칭임.
흩어진 세 벌(`Handoff_Mapping` 의 파쇄암·암절취 · B09 의 파쇄암만 ·
B08 인계본 `ground_class_aliases`**B09 가 안 읽음**)을 한 벌로 모으되
**범위를 붙여 옮길 것.** 범위 없이 합치면 발파암이 리핑암으로 조용히 섞임.
@@ -521,6 +536,66 @@ IF 36(비교식 11) · VLOOKUP 13(**전부 근사**) · SUM 47.
---
## 16. ⭐ 일위대가 레시피 사전 350개 — 확보 (2026-09-13)
표준일위대가 배포본은 암호화라 못 읽지만 **실무에서 실제 쓴 조합은 출력 엑셀에 평문**으로 남음.
```
resources/knowledge/original/경쟁사 프로그램/STmate 분석/30_원자료/일위대가_레시피.json
350개 = 일위대가 132 · 단가산출 136 · 중기 82 · 구성행 1,729 (조합당 평균 4.9)
한 항목 = { 종류 · 명칭 · 규격 · 단위 · Q식 · 품셈근거 · 구성[{명칭·규격·단위·수량}] }
누락 감사 — 호표 526개 전수, **설명되지 않는 누락 0건**
```
### 우리 설계에 바로 걸리는 것 셋
1. **150/350 이 다른 호표를 참조**(2단 이상) — 명세 4장 「하위 일위대가 재귀」가 **실데이터로 확인됨**.
예) `기슭막이 H=2.0m` → `깬잡석찰쌓기` → `석공 0.08인 + 보통인부 0.04인 + 굴삭기 0.3시간`.
⇒ **위상순서 계산이 반드시 필요함.** 취향이 아니라 자료가 그럼
2. ⭐ **규격이 바뀌면 구성 목록은 같고 수량만 비례함**
(기슭막이 H=2.0 ↔ 2.5 → 2.09/2.61 · 0.31/0.39).
⇒ **「한 조합 + 규격별 수량표」로 접힘.** 4장 라이브러리 양식형 자료구조가 이 모양이어야 함 —
규격마다 항목을 새로 만들면 350개가 수천 개로 불어남
3. `건설표준품셈` 근거 문자열이 **23개 조합에 박혀 있음**(`▣ 토공의 비탈규준틀[개소]2-6-1` 꼴)
⇒ 현행 품셈으로 수량을 갱신할 때 **대조점**이 됨
### ⚠⚠ 읽을 때 함정 둘 — 브레인 실측, 원문 문서에 없음
**① `종류`에 따라 `구성.수량`의 뜻이 다름**
```
일위대가 · 중기 수량 = 진짜 수량 (0.4 · 9.95 · 0.2085 · 5.0)
단가산출 수량 = ⚠ 금액 ("274,978" = 형틀목공 노임)
```
구성행 1,729 중 **511(29%)이 1,000 을 넘음** — 대부분 단가산출 줄임.
⛔ **일률적으로 수량으로 읽으면 29% 가 금액인 채 들어감.** `종류`로 먼저 가를 것.
**② 중기 손료 줄은 단위가 「천원」인데 값은 율임**
```
{ "명칭": "굴삭기(0.2m3)", "규격": "무한궤도", "단위": "천원", "수량": 0.2085 }
↑ 취득가가 아니라 시간당 손료율
```
⇒ **손료 = 취득가(천원) × 0.2085** 임(7장 중기 조립과 같은 자리).
단위 칸만 보고 금액으로 읽으면 **취득가 자리에 율이 들어감.**
### ⚠ 조합은 가져오되 값은 가져오지 말 것
**수량은 그 시점 품셈값임.** 「**조합 = STmate 에서, 값 = 현행 표준품셈에서**」 (사용자 확정 방향).
레시피는 **뼈대를 베끼는 자료**이지 값을 베끼는 자료가 아님.
### 세 자료가 한 벌임
| 자료 | 무엇 | 명세 |
| --- | --- | --- |
| `stmate_code_name_list.csv` | 자원 **코드·명칭·규격** 894개 | 11장 |
| `일위대가_레시피.json` | **조합** 350개 | 16장 |
| `조달청_공사원가호환규정.xsd` | **납품 규격** | 12장 |
---
## 10. 고칠 때
이 문서는 **브레인이 관리함.** 코드 창이 계약을 바꿔야 하면 **고치지 말고 브레인에게 알릴 것**
@@ -1,121 +0,0 @@
# 축 C 1장 — 자재 0 과 못 맞춘 줄 진단
작성 2026-09-13 · **랩탑 메인** · 읽기 전용 진단(코드 무변경) · 앞 조사 `2026-09-13_축C_공종이름잇기_전수조사.md` 의 이어짐
> **방법** — 지금 코드·지금 마스터로 `build_resource_axis` 를 메모리에서 돌림(쓰기 없음).
> 진단 스크립트는 세션 임시 폴더에만 두었음 — 같은 수치를 다시 내려면 이 문서의 기준을 따르면 됨.
> **기준** — 공종 마스터 477공종·456표 · 카탈로그 = 노임 132 + 기계 613 + 관급 자재 6,999 ·
> STmate 목록 `…/STmate 분석/30_원자료/stmate_code_name_list.csv`(M 228 · X 96 · L 37).
---
## 0. 한눈에
| 물음 | 답 |
| --- | --- |
| 자재가 왜 0 인가 | **사급 자재 카탈로그가 아예 없음.** 합친 카탈로그의 자재는 **관급(나라장터) 6,999건뿐**이고 품셈 자재(종자·비료·결속선·못·폭약)는 사급이라 이름부터 없음 |
| 못 맞춘 508 중 임도가 쓰는 공종 몫 | **138줄 · 42공종** (임도가 쓰는 코드 60개 기준). 나머지 370줄은 조림·방제·벌채 장 |
| 138 중 진짜 자원 이름 | **약 40종** — 나머지는 표의 갈래 딱지(보통암·깨기·치즐소모량·토질 이름)를 이름으로 읽은 것 = **표 읽기 문제** |
| STmate 목록이 풀어 주는 몫 | 40종 중 **이름이 겹치는 것 13종**, 그중 품셈 칸 규격만으로 1건이 정해지는 것 **거의 없음** — 규격은 **설계가 골라야 함** |
**⇒ 명세 1장 「카탈로그에 없는 이름 271 = STmate 로 풀릴 후보」는 너무 넓음.**
271 대부분(체인오일·예취기·농약·벌채산물)은 **임도 밖 공종**이고, 임도 안 몫도 절반은 표 읽기 문제임.
---
## 1. 자재 0 — 뿌리 넷
| # | 뿌리 | 실측 | 풀 길 |
| --- | --- | --- | --- |
| 1 | **사급 자재 원천 없음** | `load_combined_catalog()` = 노임+기계+관급. `B09_Estimation_MaterialCatalog.py` 머리말 「사급 = 없다(유료 물가지 미결 No.18)」 · 관급 파일도 철근·레미콘·아스콘 **빠짐** | 사급 자재 **이름·규격·단위 목록**(`AR-M`) — 단가는 보류(명세 §2③) |
| 2 | **품셈 칸에 규격이 없음** | 관급에 이름이 있어도(각재 8 · 철선 13 · 박리제 4 · 아스팔트 6) 품셈 칸은 「각재」 뿐 → `ResourceCatalog.resolve` 가 None | 규격은 **설계·라이브러리가 고름**. 매칭은 「규격 미정 — 후보 N」으로 드러냄(성공 아님) |
| 3 | **자재 칸에 자재 아닌 것이 섞임** | 공구손료(버켓손료·와이어손료) · 운반비 · 연료비 · 작업 단계(단끊기·골파기·씨덮기·새채집·모래부설·유공관설치·철근가공조립) | 자원 종류 가름(경비·하위 공종) — **이름 목록으로는 안 풀림** |
| 4 | **구조물 표는 값 없이 이름만** | 12장 측구·집수정·맨홀·날개벽 표 — 「콘크리트 \| 펌프차타설 \| ㎥ \| (빈칸)」. 숫자가 없어 머리 줄로 걸러짐(자원 0줄) | 수량은 **B08 구조물도 원단위**에서 옴 — 3장 구조물도가 이 자리를 채움 |
**품셈 전체의 자재 후보 줄**(분류 딱지 「자재·재료·잡재료」 또는 자재 단위 + 숫자) — 약 90줄, 그중 값이 있고
공종 형태가 쓸 수 있는 줄 **71줄(65종) 전부 못 맞춤 목록에 올라 있음**. 조용히 사라진 자재 줄은 없음(값 없는 머리 줄 제외).
---
## 2. 임도 범위로 좁힌 못 맞춘 줄 — 138줄
**임도가 쓰는 코드** = 매핑표(`resources/data_work_item_mapping/…`) + B08·B09 소스에 박힌 `FP-` 60개와 그 잎.
| 사유 | 줄 |
| --- | ---: |
| 카탈로그에 없는 이름 | 91 |
| 뭉친 자원 줄의 이름을 못 풂 | 27 |
| 자원은 알아봤으나 값을 못 읽음 | 12 |
| 규격이 없어 같은 이름 여럿 중 못 고름 | 3 |
| 값 묶음·갈래 수 안 맞음 · 장비 규격×갈래 · 작업조 표 | 5 |
**맞춘 줄(임도 범위)** 244 = 노임 218 · 기계 26 · **자재 0**.
### 2-1. 「카탈로그에 없는 이름」 91 의 속 — 둘로 갈림
| 갈래 | 예 | 공종 | 어느 일감 |
| --- | --- | --- | --- |
| **진짜 자원** | 종자·비료·피복제·침식안정제·색소 | FP-05-24-01/02 | **이름·규격 목록** |
| | 폭약·뇌관·비트·착암기·화약공 | FP-09-05-01 | 〃 |
| | 콘크리트커트·브레이드·잡자재 | FP-09-11-01 | 〃 |
| | 공기압축기(3.5㎥/min)·소형브레이커·어어호스(3/4인치) | FP-09-19-03 | 〃 |
| | VR관·고무링·지수활제·기초콘크리트·거푸집 · 흄관·접합몰탈·크레인 · 파형강관·커플링밴드 | FP-12-11-01/02/03 | 〃 |
| | 유공관설치·소운반인부 · 봉상후렉시블 다짐기·설치비 | FP-12-10 · FP-12-15 | 〃 (설치비는 경비) |
| **표 딱지를 이름으로 읽음** | 보통암·경암·연암·풍화암 | FP-09-04-01 · 09-05-02 · 09-19-01 | **표 읽기** |
| | 깨기 · 치즐소모량(본/hr) | FP-09-13-07~18 (12공종) | 〃 |
| | 모래ㆍ사질토ㆍ점토… (토질 갈래) | FP-09-19-01 | 〃 |
| | 단목·전간목·전목 | FP-04-01-01 | 〃 |
| | 대형브레이커+유압식백호우(무한…) (조합 딱지) | FP-09-04-01 | 〃 |
### 2-2. 「뭉친 줄」 27 · 「값 못 읽음」 12 — 전부 표 읽기
- FP-04-02-01 100본당(흉고 구간 13줄) · FP-12-04 합판거푸집(합판·각재·철선·못·박리제) · FP-12-09-02/03 측구 · FP-13-04-04 찰쌓기
- FP-09-13-02~18 「장비(90%) \| 유압식백호우…」 · 「들어내기 \| 유압식백호우…」 — **배분율·단계 딱지 줄의 값**을 못 읽음
**FP-12-04 합판거푸집은 두 일감에 걸림** — 뭉친 줄을 풀어도(표 읽기) 합판·각재·철선·못·박리제가
사급이라 목록이 있어야 섬(자재 0 뿌리 1·2).
---
## 3. 진짜 자원 약 40종 — 어디서 코드를 받을 수 있나
| 이름(품셈) | 우리 카탈로그 | STmate 목록 (`name size`) | 판단 |
| --- | --- | --- | --- |
| 대형브레이커 | 기계 정확 6규격 | 대형브레이커 〔0.7㎥〕 | 규격을 **옆 칸·조합에서** 받으면 섬 |
| 소형브레이커 | 기계 「소형브레이커(공압식)/(전기식)」 | 없음 | **범위 별칭**(형식 고르기) |
| 공기압축기(3.5㎥/min) | 기계 「공기압축기(이동식)」 | 없음 | **범위 별칭** + 규격 |
| 크레인 | 기계 크레인(무한궤도/타이어) 45 | 크레인(타이어) 〔10톤〕 · 트럭탑재형 크레인 〔5톤·10톤〕 | 설계 고르기 |
| 화약공 | 없음 | L 목록에 **화약취급공** | **범위 별칭** — 근거 확인 필요 |
| 소운반인부 | 없음 | 없음 | 보통인부로 볼지 **답 필요** |
| 종자 | 없음 | 종자 〔생태복원형,초본류 / 목본류〕 6 | `AR-M` + **규격 설계 고르기** |
| 비료 | 없음 | 복합비료 〔21-17-17〕 · 인산질비료 | `AR-M` + 규격 |
| 폭약 · 뇌관 | 없음 | 폭약 〔28mm(메가마이트Ⅰ)〕 · 전기뇌관 〔3.5m〕 | `AR-M` |
| 합판 · 각재 · 철선 · 못 · 박리제 | 관급에 이름 있음(규격 여럿) | 합판(내수) 〔12×1220×2440〕 · 각재 〔외송〕 · 결속선(철선) 〔#20, 0.9mm · 철못 〔N 50〕 | 관급 코드 또는 `AR-M`**규격 고르기** |
| 파형강관 | 없음 | 파형강관 〔Φ800·Φ1000, 2.7t 7 | `AR-M` + 관경은 **B08 관 정본**에서 |
| 모래 | 없음 | 모래 〔지역별 상차가 6〕 | `AR-M` — 규격이 **지역**임 |
| 이형철근 | 없음(관급에서 빠짐) | 이형철근 D13 · SD400 15 | `AR-M` + 규격 |
| 치즐 | 없음 | 치즐 0.7m3 3 | `AR-M` |
| 피복제·침식안정제·색소·착암기·콘크리트커트·브레이드·잡자재·에어호스·VR관·고무링·지수활제·흄관·접합몰탈·커플링밴드·다짐기·유공관 | 없음 | **없음** | `AR-M/X` 새로 — 이름·단위는 **품셈 원문**에서 |
**STmate 가 이름·규격을 대 주는 것 13종**, 나머지 약 27종은 **품셈 원문 이름으로 `AR-` 를 새로 냄**.
**규격은 대부분 한 벌로 안 정해짐** — 명세 §11 「(이름 + 규격) 한 쌍이 키」에 따라 품셈 칸만으로는 성공 아님.
---
## 4. 곁 발견
- **기계 카탈로그 이름 5건에 표 머리글이 붙어 있음** — `5220-0015 소형브레이커(전기식)시 간 당(10-7)분류번호…` ·
`6532-0220` · `7101-0450` · `7120-0746` · `7995-0050`. 원천 `mach_base_2026.json` 파싱 흔적. 이름으로 찾으면 안 걸림
- **품셈 원문 오기가 이름으로 들어옴** — 「어어호스」(에어호스) · 「뿜어뿥이기」(FP-05-24-01)
- **요소·인산의 「46%·20%」** (FP-05-10 F0115) — 소요량이 아니라 **성분비**. 값으로 읽으면 틀림
---
## 5. 판정·답이 필요한 것
| # | 물음 | 누구 |
| --- | --- | --- |
| 1 | 못 맞춘 줄 일감의 **범위를 임도 60코드로 좁혀도 되나** (370줄은 뒤로) | 브레인 |
| 2 | 품셈 칸에 규격이 없는 자원 — **「규격 미정 · 후보 N」으로 드러내고 설계·라이브러리가 고르게** 두는가 | 브레인 |
| 3 | 범위 별칭 후보 — 화약공→화약취급공 · 소형브레이커→(공압식) · 공기압축기→(이동식) · 소운반인부→보통인부 | **사용자 확인** (도메인 판단) |
| 4 | 사급 자재 `AR-M` 목록의 자리 — 새 폴더 한 벌(이름·규격·단위·출처·STmate 코드) | 브레인 |
@@ -37,6 +37,7 @@
| 28 변환·수량산출·식언어 | **식 변수의 대상/저장 비목** · 수량산출서 셀별명 · 조달청 변환 · 직종 106종 | (저장소 파일) |
| 29 암호화자료의 뼈대 | **값 없이 구조만** — 스키마 평문, 카탈로그 규모, 분류 4~5단, 단위 59~130종 | (저장소 파일) |
| 30 조달청 호환규정 XSD | ⭐ **국가 표준 납품 규격 전문** — 문서 뼈대·제경비 적용산식·기계경비 산출 | (저장소 파일) |
| 31 일위대가 레시피 사전 | ⭐ **조합 350개 전수** — 누락 감사 포함. 값 없이 조합만 확보 | (저장소 파일) |
> **정본은 저장소 파일이다** — `resources/knowledge/original/경쟁사 프로그램/STmate 분석/20_분석/`.
> 위 Orca 링크는 보기용이며, 문서를 지식DB 자리로 옮기면서 주소가 한 번 바뀌었다.
@@ -137,6 +138,9 @@
| 식을 데이터로 저장 | 우리 계약 | **규격이 `적용산식`·`적용산식설명`을 요구** | 30번 §3 |
| 노임 환산 갈래 | 화면 문자열 | **`H0`~`H5` 규격 코드** (일반·터널·장대터널·준설선 3종) | 30번 §4 |
| 손료 산출 | 한 갈래로 봄 | ⚠ **주간/야간/2교대/3교대 네 갈래** — 뽑은 25개는 `E0` 뿐 | 30번 §4 |
| **일위대가 조합** | 표준본 암호화라 못 얻는 줄 앎 | ⭐ **실무 조합 350개 확보** — 일위대가 132·단가산출 136·중기 82 | 31번 |
| 조합 누락 | — | **일위대가표·중기사용료 0건.** 설명 안 되는 누락 0건 | 31번 §2 |
| 규격별 조합 | — | **구성은 같고 수량만 비례** — 한 조합 + 규격별 수량표로 접힘 | 31번 §3.3 |
분석일: 2026-09-13
대상: `C:\경영정보(주)`
@@ -0,0 +1,150 @@
# 일위대가 레시피 사전 — 어떤 조합인가, 빠진 것은 없는가
작성일: 2026-09-13
목적: **값이 아니라 조합**을 확보하는 것. 표준 일위대가 배포본(`표준일위대가_*.STBSC`)은 암호화라 못 읽지만, **실무에서 실제 쓴 조합**은 출력 엑셀에 평문으로 남는다.
산출물: `30_원자료/일위대가_레시피.json` (265.8 KB)
재현 스크립트: `40_스크립트/recipe_extract.py`
## 1. 왜 이것이 값어치가 있나
```text
STmate 표준일위대가(암호화) = 조합 + 값(반기마다 바뀜)
우리가 확보한 것 = 조합 350개 ← 임도·사방 실무에서 실제 쓴 것
+ 현행 표준품셈 = 값
──────────────────────────────────────────────
= Aislo 표준일위대가
```
**값은 반기마다 바뀌므로 가질 이유가 없고, 조합은 잘 바뀌지 않는다.** 게다가 이 350개는 범용 카탈로그가 아니라 **임도·사방 설계서 6건에서 실제로 쓰인 것**이라 우리 쪽에 더 맞는다.
⚠ 수량(`0.08`·`2.09`)은 **그 시점 품셈값**이다. **조합은 그대로 쓰되 수량은 현행 품셈으로 다시 맞춰야 한다.**
## 2. 누락 감사 — 호표 526개를 하나씩 셌다
| 시트 | 호표 | 정상 추출 | 중복 | 제목 못 잡음 | 빈 호표 | 못 잡음 |
|---|---:|---:|---:|---:|---:|---:|
| 일위대가표 | 170 | **170** | 38 | 0 | 0 | **0** |
| 중기사용료 | 143 | **143** | 61 | 0 | 0 | **0** |
| 단가산출근거 | 213 | 187 | 51 | 0 | 5 | **21** |
| **합계** | **526** | **500** | 150 | 0 | 5 | **21** |
**일위대가표·중기사용료는 한 개도 안 빠졌다.** 「중복」은 같은 조합이 여러 프로젝트에 나온 것으로, 중복을 걷어내면 **서로 다른 조합 350개**다.
### 2.1 못 잡은 21개의 정체 — 전부 확인함
| 갈래 | 예 | 왜 |
|---|---|---|
| **운반 계산** | `중기운반(현동) L=36.1km` · `기계운반` · `시멘트운반 L=62.5km` | 구성이 자원 목록이 아니라 **거리·속도 인자**(`L1`·`L2`·`V1`~`V4`)로만 이뤄짐 |
| **기계+인력 조합 터파기** | `측구터파기(토사) 기계100%` · `구조물터파기 기계90%+인력10%` · `구조물 되메우기` | 기계분과 인력분을 비율로 섞는 꼴이라 한 줄 패턴으로 안 잡힘 |
**둘 다 값을 못 읽은 것이 아니라 「자원 조합」이 아닌 것이다.** 운반 인자와 기계·인력 비율은 23번 §4(운반 사이클)·26번 §1(`Cm` 54꼴)에서 이미 따로 다뤘다.
### 2.2 빈 호표 5개
`무대처리` · `흙 쌓 기` · `사토 무대처리` 등 **합계가 0원인 항목**이다. 내역에는 수량만 잡고 금액이 0인 공종으로, 조합이 없는 것이 정상이다.
**설명되지 않는 누락은 0건이다.**
## 3. 확보한 조합 350개
| 종류 | 개수 | 무엇 |
|---|---:|---|
| **일위대가** | **132** | 자원·하위 호표를 조합해 공종 단가를 만듦 |
| **단가산출** | **136** | 기계 `Q`식으로 시간당 사용료를 작업량으로 나눔 |
| **중기** | **82** | 손료 + 운전원 + 연료 + 잡품 |
| 구성행 | 1,729 | 조합당 평균 **4.9개** |
| `Q`식을 가진 것 | **107** | |
### 3.1 구성 개수 분포
```text
1개 11 · 2개 14 · 3개 17 · 4개 90 · 5개 29 · 6개 14 · 7개 15
8개 10 · 9개 5 · 10개 3 · 11개 3 · 12개 1 · 13개 1 · 17개 1
```
**4개짜리가 90개로 최다** — 중기 호표(손료·운전원·연료·잡품)가 그 꼴이다.
### 3.2 ⭐ 150 / 350 이 다른 호표를 참조한다
조합이 **2단 이상으로 쌓인다.** 17번 §5의 위상순서 계산이 여기서도 확인된다.
```text
기슭막이(깬잡석,찰쌓기 L3=45m) H=2.0m / M ← 9개 구성
깬잡석채집 L=45cm 내외 M2 2.09
깬잡석찰쌓기 L3=45cm내외 M2 2.09 ← 다른 호표
고임돌채집 기계(0.7㎥,100%) M3 0.31
막자갈채집 기계(0.7㎥,100%) M3 0.48
기초다짐 및 뒤채움 소형 장비사용 시공 M3 0.48
PVC파이프 VG1(50m/m) M 0.94
유로폼 설치 및 해체 간단 m2 0.85
레미콘타설(장비) 무근 m3 0.31
콘크리트믹서사용 0.45 m3 m3 0.42
└ 깬잡석찰쌓기 L3=45cm내외 / M2 ← 3개 구성
석공 인 0.08
보통인부 인 0.04
굴삭기(타이어) 0.6㎥,(부착용집게) 시간 0.30
```
### 3.3 ⭐ 규격이 바뀌면 수량만 비례한다
```text
기슭막이 … H=2.0m 깬잡석채집 2.09 · 고임돌 0.31 · 막자갈 0.48 · PVC 0.94
기슭막이 … H=2.5m 깬잡석채집 2.61 · 고임돌 0.39 · 막자갈 0.79 · PVC 1.28
```
**구성 목록은 같고 수량만 높이에 따라 달라진다.** 규격별로 조합을 새로 만들 필요 없이 **한 조합 + 규격별 수량표**로 둘 수 있다는 뜻이다.
### 3.4 가장 많이 쓰인 구성 자원
| 자원 | 단위 | 등장 조합 수 | | 자원 | 단위 | 등장 |
|---|---|---:|---|---|---|---:|
| 잡품 | % | **70** | | 화물차운전사 | 인 | 15 |
| 경유 | L | **68** | | 굴삭기(무한궤도) | 천원 | 13 |
| 건설기계운전사 | 인 | **52** | | 면목 | M | 13 |
| 보통인부 | 인 | **43** | | 콘크리트비빔 | m3 | 12 |
| 유로폼 설치 및 해체 | m2 | 24 | | 철근가공 및 조립 | ton | 11 |
| 석공 | 인 | 23 | | 덤프트럭 | 천원 | 11 |
| **건설표준품셈** | — | **23** | | 레미콘 | M3 | 11 |
| 고임돌채집 | m3 | 21 | | 기초잡석채집및운반부설 | M3 | 11 |
| 레미콘타설(장비) | m3 | 19 | | 막자갈채집 | m3 | 11 |
**`건설표준품셈`이 23개 조합에 근거로 적혀 있다.** 어느 품셈 조항에서 왔는지가 텍스트로 남는다(`▣ 토공의 비탈규준틀[개소]2-6-1` 같은 꼴). 현행 품셈으로 갱신할 때 **대조점이 된다.**
`잡품`이 70개 조합에 나오는 것은 중기 호표마다 붙기 때문이고, **수량×단가가 아니라 연료비에 비율을 곱하는 가산 행**이다(17번 §3).
## 4. 산출물 구조
`30_원자료/일위대가_레시피.json`
```json
{
"종류": "일위대가",
"명칭": "깬잡석찰쌓기",
"규격": "L3=45cm내외",
"단위": "M2",
"Q식": null,
"품셈근거": null,
"구성": [
{"명칭": "석공", "규격": "", "단위": "인", "수량": 0.08},
{"명칭": "보통인부", "규격": "", "단위": "인", "수량": 0.04},
{"명칭": "굴삭기(타이어)", "규격": "0.6㎥,(부착용집게)", "단위": "시간", "수량": 0.3}
]
}
```
단가산출 계열은 `Q식`(예: `3600*q*K*f*E/Cm`)과 `품셈근거`가 함께 담긴다.
## 5. 이대로 쓰려면 해야 할 것
1. **수량을 현행 표준품셈으로 갱신** — ✅ 32번에서 350개 전수 장부화. 직접 일치 2개, 나머지는 근거·조건별 수동 대조 필요.
2. **자원 이름을 우리 코드에 잇기**`30_원자료/stmate_code_name_list.csv`(894개)와 짝지어 쓴다.
3. **못 잡은 21개 보완** — ✅ 32번 및 `특수_단가산출_21개.json`에 6유형·원문 산식·인자식으로 전수 구조화.
4. **규격별 수량표로 접기** — §3.3처럼 같은 조합이 규격만 다른 것이 많다.
## 6. 한 줄 정리
| 물음 | 답 |
|---|---|
| 어떤 조합인가 | **350개** — 일위대가 132 · 단가산출 136 · 중기 82. 구성행 1,729 |
| 빠진 것은 없나 | **일위대가표·중기사용료 0건 누락.** 단가산출 21개는 「자원 조합이 아닌 것」(운반 인자·기계인력 비율)이고 5개는 금액 0인 빈 호표. **설명 안 되는 누락 0건** |
@@ -0,0 +1,53 @@
# 특수 단가산출 21개 구조화와 현행 품셈 대조
작성일: 2026-09-13
대상: `31_일위대가_레시피_사전.md`의 후속 작업 1·3
현행 원문: 산림청고시 제2025-82호 산림사업 표준품셈(2026-01-01 시행), 2026년 건설공사 표준품셈
## 1. 특수 단가산출 21개
산출물: `30_원자료/특수_단가산출_21개.json`
재현: `40_스크립트/recipe_extract.py`
| 유형 | 개수 | 구조 |
|---|---:|---|
| 구역화물 운반 | 9 | 거리구간 운임 ÷ 부가세 ÷ 적재량, 필요 시 적상·적하비 합산 |
| 기계작업 사이클 | 4 | `Q=3600×q×K×f×E/Cm`, 기계·인력 비율을 각 성분에 적용 |
| 중기운반 사이클 | 3 | `L1·L2·V1~V4·t1~t4 → Cm → N·OH` |
| 직접산식 | 2 | 떼 하차비, 제근처럼 자원단가에 작업조건식을 직접 적용 |
| 일작업량 환산 | 2 | 일 노임·중기사용료를 일작업량 `Q`로 나눔 |
| 인력운반 사이클 | 1 | `N=V×T/(120×L+V×T1)`, `Q=N×운반중량` |
| **합계** | **21** | 원문 산식행과 인자를 함께 보존 |
기존의 「운반 / 기계+인력」 두 갈래만으로는 5개 유형을 잃는다. JSON은 계산값을 임의로 다시 만들지 않고 **원문 산식행 전체와 추출한 인자식**을 보존한다.
## 2. 레시피 350개 × 현행 품셈 1차 대조
산출물: `30_원자료/일위대가_현행품셈_대조.json`
감사표: `30_원자료/일위대가_현행품셈_대조_감사.txt`
재현: `40_스크립트/recipe_current_quantity_audit.py`
| 상태 | 개수 | 뜻 |
|---|---:|---|
| 현행 원문 수량문자 전부 확인 | **2** | 현행 절과 조건을 직접 확인한 항목 |
| 현행 절 확인·수량 수동대조 필요 | 11 | 근거 절은 찾았으나 규격·조건 해석 필요 |
| 현행 절 후보·수동대조 필요 | 161 | 명칭으로 후보 절만 찾음 |
| 근거문구·현행 위치 미확인 | 69 | 과거 절 번호가 개편됐거나 복수 절을 참조 |
| 근거 미기재 | 107 | 실무 출력에 품셈 근거문구가 없음 |
| **합계** | **350** | 전 항목 장부화 완료 |
### 원문에서 직접 일치한 2개
| 조합 | 실무 관측 | 2026 현행 원문 | 판정 |
|---|---|---|---|
| 단끊기(절취없음) | 보통인부 0.34·0.36·1.17인/100m | 산림사업 표준품셈 5-16-1의 수평잡기·잡석/뿌리정리·절성토면 고르기와 각각 일치 | 일치 |
| 제근(밀림) | 보통인부 0.05인 | 산림사업 표준품셈 9-21 밀림 0.05인과 일치 | 일치 |
나머지를 `변경`으로 판정하지 않았다. 같은 숫자가 없다는 사실만으로는 규격·토질·장비·일작업량 조건이 달라진 것인지 품이 개정된 것인지 가를 수 없기 때문이다.
## 3. 판정 경계
- 실무 레시피 350개 중 품셈 근거문구 보유는 82개뿐이다. 근거 없는 268개는 현행값으로 임의 확정하지 않는다.
- 과거 임도품셈 절 번호가 현행 산림사업 표준품셈에서 크게 바뀌었다. 번호만 같은 다른 절을 자동 연결하지 않는다.
- JSON의 `수량문자_확인`은 후보 절 본문에 같은 숫자가 있는지를 세는 1차 검색값이다. `일치` 확정은 위 두 항목처럼 조건과 행을 직접 대조한 경우만 한다.
- 후속 수동 대조는 근거 절이 확인된 11개부터 하고, 복수 절 참조 69개는 과거판-현행판 절 대응표가 있어야 안전하다.
@@ -0,0 +1,26 @@
### 누락 감사 — 출력 엑셀 6개
종류 호표 정상 중복 제목없음 빈호표 구성없음
일위대가 170 170 38 0 0 0
중기 143 143 61 0 0 0
단가산출 213 187 51 0 5 21
호표 526개 중 500개 처리 (95.1%) · 못 잡은 것 26개
2024년 산불진화임도 실시설계( 단가산출근거 호표14 측구터파기(토사) 기계100%(굴삭기 0.4 줄67
2024년 산불진화임도 실시설계( 단가산출근거 호표21 구조물터파기(육상토사0~1m) 기계90%+인 줄66
2024년 산불진화임도 실시설계( 단가산출근거 호표23 구조물터파기(육상토사0~1m) 기계100%( 줄32
2024년 산불진화임도 실시설계( 단가산출근거 호표24 구조물 되메우기 기계100%(굴삭기 0.7M 줄32
2024년 산불진화임도 실시설계( 단가산출근거 호표59 중기운반(현동) L=36.1km 줄68
2024년 산불진화임도 실시설계( 단가산출근거 호표60 기계운반(현동) L=36.1km 줄33
2024년 산불진화임도 실시설계( 단가산출근거 호표61 시멘트운반(현동) L=62.5km 줄66
2024년 산불진화임도 실시설계( 단가산출근거 호표64 철근운반(현동) L=62.5km 줄31
2024년 간선임도사업(기번3) 단가산출근거 호표21 중기운반(울진군 울진읍 대흥리) L=19.0 줄68
2024년 간선임도사업(기번3) 단가산출근거 호표24 시멘트운반(울진군 울진읍 대흥리) L=19. 줄66
### 서로 다른 조합 350개
단가산출 136개
일위대가 132개
중기 82개
구성행 합계 1729 (조합당 평균 4.9)
Q식을 가진 조합 107개
→ 일위대가_레시피.json 265.8 KB
@@ -0,0 +1,10 @@
### STmate 일위대가 350개 × 2026년 현행 품셈 대조
근거문구_현행위치미확인: 69
근거미기재: 107
현행원문_수량문자_전부확인: 2
현행절_확인_수량수동대조필요: 11
현행절_후보_수동대조필요: 161
※ '수량문자 확인'은 현행 절 본문에 같은 숫자가 존재한다는 1차 검사이며 확정 판정이 아니다.
※ 근거가 없거나 조건·규격 해석이 필요한 값은 사용자 협의 없이 갱신하지 않았다.
@@ -0,0 +1,532 @@
[
{
"원본": "2024년 산불진화임도 실시설계(기번41_현동)_ L=3.46km",
"호표": 14,
"명칭": "측구터파기(토사) 기계100%(굴삭기 0.4m3)",
"규격": "",
"단위": "M3",
"유형": "기계작업_사이클",
"인자": {
"E": "0.45-0.05= 0.40",
"K": "0.7 E=0.45-0.05= 0.40",
"f": "1/1.175 = 0.85 Cm=15 sec(90˚)",
"Cm": "15 sec(90˚)",
"Q": "3600*q*K*f*E/Cm= 22.85 ㎥/hr",
"q": "0.4 K=0.7 E=0.45-0.05= 0.40"
},
"산식행": [
"측구터파기(토사) 기계100%(굴삭기 0.4m3) / M3",
"▣임도표준품셈 5-1-1 가.토사 적용",
"Q : 시간당 작업량(m3/hr)",
"q : 버킷용량(m3) E : 작업효율",
"f : 토량의 체적 환산계수 K : 버킷계수",
"Cm : 1회 사이클 시간(초)",
"적용토질 : 자갈섞인흙",
"1. 굴삭기(0.4㎥ : 100%)",
"q=0.4 , K=0.7 , E=0.45-0.05= 0.40",
"f= 1/1.175 = 0.85 , Cm=15 sec(90˚)",
"Q=3600*q*K*f*E/Cm= 22.85 ㎥/hr",
"경 비 16,378 / Q = 716.7",
"노무비 55,700 / Q = 2,437.6",
"재료비 15,375 / Q = 672.8"
]
},
{
"원본": "2024년 산불진화임도 실시설계(기번41_현동)_ L=3.46km",
"호표": 21,
"명칭": "구조물터파기(육상토사0~1m) 기계90%+인력10%(굴삭기 0.7M3)",
"규격": "",
"단위": "M3",
"유형": "기계작업_사이클",
"인자": {
"E": "0.45-0.05= 0.40",
"K": "0.9 E=0.45-0.05= 0.40",
"f": "1/1.175 = 0.85 Cm=20 sec(135˚)",
"Cm": "20 sec(135˚)",
"Q": "3600*q*K*f*E/Cm= 38.56 ㎥/hr",
"q": "0.7 K=0.9 E=0.45-0.05= 0.40",
"A": "0.2"
},
"산식행": [
"구조물터파기(육상토사0~1m) 기계90%+인력10%(굴삭기 0.7M3) / M3",
"▣임도표준품셈 5-1-2 가.육상토사(0~1m) 적용",
"집수정, 맨홀 등 배수구조물의 터파기기 적용함.",
"1. 굴삭기(0.7㎥ : 90%)",
"q=0.7 , K=0.9 , E=0.45-0.05= 0.40",
"f= 1/1.175 = 0.85 , Cm=20 sec(135˚)",
"Q=3600*q*K*f*E/Cm= 38.56 ㎥/hr",
"경 비 23,128 / Q * 0.9 = 539.8",
"노무비 55,700 / Q * 0.9 = 1,300.0",
"재료비 18,015 / Q * 0.9 = 420.4",
"2. 보통인부(인력 10%)",
"A=0.2",
"165,545 * A * 0.1 = 3,310.9"
]
},
{
"원본": "2024년 산불진화임도 실시설계(기번41_현동)_ L=3.46km",
"호표": 23,
"명칭": "구조물터파기(육상토사0~1m) 기계100%(굴삭기 0.7M3)",
"규격": "",
"단위": "M3",
"유형": "기계작업_사이클",
"인자": {
"E": "0.45-0.05= 0.40",
"K": "0.7 E=0.45-0.05= 0.40",
"f": "1/1.175 = 0.85 Cm=20 sec(135˚)",
"Cm": "20 sec(135˚)",
"Q": "3600*q*K*f*E/Cm= 29.99 ㎥/hr",
"q": "0.7 K=0.7 E=0.45-0.05= 0.40"
},
"산식행": [
"구조물터파기(육상토사0~1m) 기계100%(굴삭기 0.7M3) / M3",
"▣임도표준품셈 5-1-2 가.육상토사(0~1m) 적용",
"집수정, 맨홀 등 배수구조물의 터파기기 적용함.",
"1. 굴삭기(0.7㎥ : 100%)",
"q=0.7 , K=0.7 , E=0.45-0.05= 0.40",
"f= 1/1.175 = 0.85 , Cm=20 sec(135˚)",
"Q=3600*q*K*f*E/Cm= 29.99 ㎥/hr",
"경 비 23,128 / Q = 771.1",
"노무비 55,700 / Q = 1,857.2",
"재료비 18,015 / Q = 600.7"
]
},
{
"원본": "2024년 산불진화임도 실시설계(기번41_현동)_ L=3.46km",
"호표": 24,
"명칭": "구조물 되메우기 기계100%(굴삭기 0.7M3)",
"규격": "",
"단위": "M3",
"유형": "기계작업_사이클",
"인자": {
"E": "0.5 f= 0.9 / 1.175 = 0.77",
"K": "0.7 E=0.5 f= 0.9 / 1.175 = 0.77",
"f": "0.9 / 1.175 = 0.77",
"Cm": "18 sec(90˚)",
"Q": "3600*q*K*f*E/Cm= 37.73 ㎥/hr",
"q": "0.7 K=0.7 E=0.5 f= 0.9 / 1.175 = 0.77"
},
"산식행": [
"구조물 되메우기 기계100%(굴삭기 0.7M3) / M3",
"▣임도표준품셈 5-1-3 가.되메우기 및 다짐 적용",
"1. 굴삭기(0.7㎥ : 100%)",
"q=0.7 , K=0.7 , E=0.5 , f= 0.9 / 1.175 = 0.77",
"Cm=18 sec(90˚)",
"Q=3600*q*K*f*E/Cm= 37.73 ㎥/hr",
"경 비 23,128 / Q = 612.9",
"노무비 55,700 / Q = 1,476.2",
"재료비 18,015 / Q = 477.4"
]
},
{
"원본": "2024년 산불진화임도 실시설계(기번41_현동)_ L=3.46km",
"호표": 59,
"명칭": "중기운반(현동) L=36.1km",
"규격": "",
"단위": "대",
"유형": "중기운반_사이클",
"인자": {
"L1": "36.1 km(포 장)",
"L2": "0.0 km(비포장)",
"V1": "35 km/hr V2= 35 km/hr",
"V2": "35 km/hr",
"V3": "10 km/hr V4= 15 km/hr",
"V4": "15 km/hr",
"t1": "20 E=0.9",
"t2": "(L1/V1+L1/V2+L2/V3+L2/V4) * 60 = 123.77",
"t3": "20 t4=0.42",
"t4": "0.42",
"E": "0.9",
"Cm": "t1 + t2 + t3 + t4 = 164.19",
"N": "60*E/Cm= 0.33 회"
},
"산식행": [
"중기운반(현동) L=36.1km / 대",
"중 기 운 반 (20톤 트레일러)",
"소재지 ---------------------> 현장",
"L1= 36.1 km(포 장)",
"L2= 0.0 km(비포장)",
"V1= 35 km/hr , V2= 35 km/hr",
"V3= 10 km/hr , V4= 15 km/hr",
"t1=20 , E=0.9",
"t2=(L1/V1+L1/V2+L2/V3+L2/V4) * 60 = 123.77",
"t3=20 , t4=0.42",
"Cm =t1 + t2 + t3 + t4 = 164.19",
"N=60*E/Cm= 0.33 회",
"OH=(Cm-t1-t3)/Cm= 0.76",
"노무비 : 55,700/N*2 = 337,575.7",
"재료비 : 29,196/N*OH*2 = 134,478.5",
"경 비 : 16,353/N*2 = 99,109.0"
]
},
{
"원본": "2024년 산불진화임도 실시설계(기번41_현동)_ L=3.46km",
"호표": 60,
"명칭": "기계운반(현동) L=36.1km",
"규격": "",
"단위": "대",
"유형": "구역화물_운반",
"인자": {},
"산식행": [
"기계운반(현동) L=36.1km / 대",
"기계 운반( 믹서 , 소형 중기류)",
"L=36.1km",
"시·군--------------------> 현장중점",
"○구역화물(8Ton)적용 L = 40km이내 적용",
"128,630 / 1.1 = 116,936.3"
]
},
{
"원본": "2024년 산불진화임도 실시설계(기번41_현동)_ L=3.46km",
"호표": 61,
"명칭": "시멘트운반(현동) L=62.5km",
"규격": "",
"단위": "포",
"유형": "구역화물_운반",
"인자": {},
"산식행": [
"시멘트운반(현동) L=62.5km / 포",
"○L = 37.8km",
"1)자동차운반",
"하치장 -------> 현장중점",
"구역화물(10.5ton적용) L =70km이내적용",
"195,340 / 1.1 / 10.5 / 25 = 676.5",
"2)적상적하비",
"적상 : 0.18 인",
"적하 : 0.13 인",
"(0.18+0.13) * 165,545 / 25 = 2,052.7"
]
},
{
"원본": "2024년 산불진화임도 실시설계(기번41_현동)_ L=3.46km",
"호표": 64,
"명칭": "철근운반(현동) L=62.5km",
"규격": "",
"단위": "TON",
"유형": "구역화물_운반",
"인자": {},
"산식행": [
"철근운반(현동) L=62.5km / TON",
"철근 운반 ( L = 62.5 km)",
"현 장",
"하치장 ---------------------->",
"중 점",
"1).구역화물(10.5ton)적용 L = 70km이내적용",
"195,340 / 1.1 / 10.5 = 16,912.5",
"2).TON당 하차비",
"2 * 11 / 2 / (480-30) * 165,545 = 4,046.6"
]
},
{
"원본": "2024년 간선임도사업(기번3)",
"호표": 21,
"명칭": "중기운반(울진군 울진읍 대흥리) L=19.00km",
"규격": "",
"단위": "대",
"유형": "중기운반_사이클",
"인자": {
"L1": "12 km(포 장)",
"L2": "7 km(비포장)",
"V1": "30 km/hr",
"V2": "35 km/hr",
"V3": "10 km/hr",
"V4": "15 km/hr",
"t4": "154.99",
"E": "0.9",
"Cm": "0.35 회"
},
"산식행": [
"중기운반(울진군 울진읍 대흥리) L=19.00km / 대",
"중 기 운 반 (20톤 트레일러)",
"소재지 ---------------------> 현장",
"건설표준품셈 11-6나(트럭트레일러) 적용",
"중 기 운 반 (20톤 트레일러)",
"L1= 12 km(포 장)",
"L2= 7 km(비포장)",
"V1= 30 km/hr",
"V2= 35 km/hr",
"V3= 10 km/hr",
"V4= 15 km/hr",
"t1 (상차시간(분)) =20 , E=0.9",
"t2 (왕복시간(분)) =(L1/V1+L1/V2+L2/V3+L2/V4) * 60 = 114.57",
"t3 (하차시간(분)) =20",
"t4 (하차준비시간(분)) =0.42",
"Cm (회 사이클 시간(분)) =t1 + t2 + t3 + t4 = 154.99",
"N (간단 운반횟수(회)) =60*E/Cm= 0.35 회",
"OH (상차 10분 초과 시 운반기계의 유류보정) =(cm-t1-t3)/Cm= 0.74",
"노무비 : 55,700/N*2 = 318,285.7",
"재료비 : 29,173/N*OH*2 = 123,360.1",
"경 비 : 16,353/N*2 = 93,445.7"
]
},
{
"원본": "2024년 간선임도사업(기번3)",
"호표": 24,
"명칭": "시멘트운반(울진군 울진읍 대흥리) L=19.00km",
"규격": "",
"단위": "대",
"유형": "구역화물_운반",
"인자": {},
"산식행": [
"시멘트운반(울진군 울진읍 대흥리) L=19.00km / 대",
"○시멘트운반 L = 19.00 km",
"건설표준품셈 1-31-나(품종별 적상.하 기준) 적용",
"1)자동차운반",
"하치장 -------> 현장중점",
"구역화물(5ton적용) L = 20km이내적용",
"61,500 / 1.1 / 5 / 25 = 447.2",
"2)적상적하비",
"적상 : 0.18 인",
"적하 : 0.13 인",
"(0.18+0.13) * 165,545 / 25 = 2,052.7"
]
},
{
"원본": "2024년 간선임도사업(기번3)",
"호표": 31,
"명칭": "기계운반(울진군 울진읍 대흥리) L=19.00km",
"규격": "",
"단위": "대",
"유형": "구역화물_운반",
"인자": {},
"산식행": [
"기계운반(울진군 울진읍 대흥리) L=19.00km / 대",
"기계 운반( 믹서 , 소형 중기류)",
"L=19.00km",
"시군--------------------> 현장중점",
"○구역화물(5Ton)적용 L = 20km이내적용",
"61,500 / 1.1 = 55,909.0"
]
},
{
"원본": "2024년 간선임도사업(기번3)",
"호표": 35,
"명칭": "철근운반(울진군 울진읍 대흥리) L=19.00km",
"규격": "",
"단위": "톤",
"유형": "구역화물_운반",
"인자": {},
"산식행": [
"철근운반(울진군 울진읍 대흥리) L=19.00km / 톤",
"철근 운반 ( L = 19 km)",
"현 장",
"하치장 ------------->",
"중 점",
"1).구역화물(5ton)적용 L = 20km이내적용",
"61,500 / 1.1 / 5 = 11,181.8",
"2).TON당 하차비",
"2 * 11 / 2 / (480-30) * 165,545 = 4,046.6"
]
},
{
"원본": "2024년 간선임도 신설사업(기번3 울진.울진.대흥.산65외2)",
"호표": 31,
"명칭": "중기운반(울진군 울진읍 대흥리) L=19.00km",
"규격": "",
"단위": "대",
"유형": "중기운반_사이클",
"인자": {
"L1": "12.0 km(포 장)",
"L2": "7.00 km(비포장)",
"V1": "30 km/hr",
"V2": "35 km/hr",
"V3": "10 km/hr",
"V4": "15 km/hr",
"t4": "154.99",
"E": "0.9",
"Cm": "0.35 회"
},
"산식행": [
"중기운반(울진군 울진읍 대흥리) L=19.00km / 대",
"중 기 운 반 (20톤 트레일러)",
"소재지 ---------------------> 현장",
"건설표준품셈 11-6나(트럭트레일러) 적용",
"중 기 운 반 (20톤 트레일러)",
"L1= 12.0 km(포 장)",
"L2= 7.00 km(비포장)",
"V1= 30 km/hr",
"V2= 35 km/hr",
"V3= 10 km/hr",
"V4= 15 km/hr",
"t1 (상차시간(분)) =20 , E=0.9",
"t2 (왕복시간(분)) =(L1/V1+L1/V2+L2/V3+L2/V4) * 60 = 114.57",
"t3 (하차시간(분)) =20",
"t4 (하차준비시간(분)) =0.42",
"Cm (회 사이클 시간(분)) =t1 + t2 + t3 + t4 = 154.99",
"N (간단 운반횟수(회)) =60*E/Cm= 0.35 회",
"OH (상차 10분 초과 시 운반기계의 유류보정) =(cm-t1-t3)/Cm= 0.74",
"노무비 : 55,700/N*2 = 318,285.7",
"재료비 : 29,173/N*OH*2 = 123,360.1",
"경 비 : 16,353/N*2 = 93,445.7"
]
},
{
"원본": "2024년 간선임도 신설사업(기번3 울진.울진.대흥.산65외2)",
"호표": 32,
"명칭": "기계운반(울진군 울진읍 대흥리) L=19.00km",
"규격": "",
"단위": "대",
"유형": "구역화물_운반",
"인자": {},
"산식행": [
"기계운반(울진군 울진읍 대흥리) L=19.00km / 대",
"기계 운반( 믹서 , 소형 중기류)",
"L=19.00km",
"시군--------------------> 현장중점",
"○구역화물(5Ton)적용 L = 20km이내적용",
"61,500 / 1.1 = 55,909.0"
]
},
{
"원본": "2024년 간선임도 신설사업(기번3 울진.울진.대흥.산65외2)",
"호표": 36,
"명칭": "시멘트운반(울진군 울진읍 대흥리) L=19.00km",
"규격": "",
"단위": "대",
"유형": "구역화물_운반",
"인자": {},
"산식행": [
"시멘트운반(울진군 울진읍 대흥리) L=19.00km / 대",
"○시멘트운반 L = 19.00 km",
"건설표준품셈 1-31-나(품종별 적상.하 기준) 적용",
"1)자동차운반",
"하치장 -------> 현장중점",
"구역화물(5ton적용) L = 20km이내적용",
"61,500 / 1.1 / 5.0 / 25 = 447.2",
"2)적상적하비",
"적상 : 0.18 인",
"적하 : 0.13 인",
"(0.18+0.13) * 165,545 / 25 = 2,052.7"
]
},
{
"원본": "2024년 간선임도 신설사업(기번3 울진.울진.대흥.산65외2)",
"호표": 47,
"명칭": "철근운반(울진군 울진읍 대흥리) L=19.00km",
"규격": "",
"단위": "톤",
"유형": "구역화물_운반",
"인자": {},
"산식행": [
"철근운반(울진군 울진읍 대흥리) L=19.00km / 톤",
"철근 운반 ( L = 19.00 km)",
"현 장",
"하치장 ------------->",
"중 점",
"1).구역화물(5ton)적용 L = 20km이내적용",
"61,500 / 1.1 / 5 = 11,181.8",
"2).TON당 하차비",
"2 * 11 / 2 / (480-30) * 165,545 = 4,046.6"
]
},
{
"원본": "2025년 계류보전사업(기번1 영덕.병곡.영.산214)(변경)",
"호표": 14,
"명칭": "인력운반 기타",
"규격": "",
"단위": "ton",
"유형": "인력운반_사이클",
"인자": {
"N": "V*T/(120*L+V*T1)= 64.29 회/일",
"Q": "N*0.025 ton = 1.61 ton/일",
"T": "480-30= 450.00",
"T1": "1 T=480-30= 450.00",
"V": "2000 T1=1 T=480-30= 450.00"
},
"산식행": [
"인력운반 기타 / ton",
"인력운반",
"평균 운반거리 L = 100m",
"1). 인력운반(L=100M)",
"L=100, V=2000, T1=1, T=480-30= 450.00",
"N=V*T/(120*L+V*T1)= 64.29 회/일",
"Q=N*0.025 ton = 1.61 ton/일",
"보통인부 : 169,804 / Q = 105,468.3 원/ton"
]
},
{
"원본": "2025년 산불진화임도 신설사업(기번8 울진.금강송.소광.산29외)",
"호표": 9,
"명칭": "떼 하차비",
"규격": "",
"단위": "m2",
"유형": "직접산식",
"인자": {},
"산식행": [
"떼 하차비 / m2",
"◈참고사항",
"500매 = 2500kg ⇒ 5 kg/매",
"1m2 = 11.11매 ⇒ 0.09 m2/매",
"5kg/매 ÷ 0.09m2/매 = 55.55kg/m2 임",
"○ 적하비 (2인 1조)",
"2 인 * 16 /(480-30) * 169,804 * 55/1000 = 664.1"
]
},
{
"원본": "2025년 산불진화임도 신설사업(기번8 울진.금강송.소광.산29외)",
"호표": 10,
"명칭": "평떼붙이기",
"규격": "",
"단위": "m2",
"유형": "일작업량_환산",
"인자": {
"Q": "150 m2/일"
},
"산식행": [
"평떼붙이기 / m2",
"※ 건설표준품셈(공통) : 4-1-1 적용",
"□ 일작업량 : Q = 150 m2/일",
"1. 인부임",
"○ 조경공:",
"224,132 * 1.0 인 / Q = 1,494.2 원/m2",
"○보통인부 :",
"169,804 * 4.0 인 / Q = 4,528.1 원/m2"
]
},
{
"원본": "2025년 산불진화임도 신설사업(기번8 울진.금강송.소광.산29외)",
"호표": 16,
"명칭": "구조물터파기(토사) 굴착기0.6m3",
"규격": "",
"단위": "m3",
"유형": "일작업량_환산",
"인자": {
"Q": "190 m3/일",
"q1": "Q / 8 = 23.75 m3/hr"
},
"산식행": [
"구조물터파기(토사) 굴착기0.6m3 / m3",
"※ 건설표준품셈(공통) : 3-2-4 적용",
"□ 일 시공량 : Q = 190 m3/일",
"※ 작업조건 : Type-Ⅲ (협소)",
"1. 굴착기 (0.6m3)",
"□ 시간당 시공량 : q1 = Q / 8 = 23.75 m3/hr",
"노 무 비 : 57,077 / q1 = 2,403.2",
"재 료 비 : 19,547 / q1 = 823.0",
"경 비 : 26,463 / q1 = 1,114.2"
]
},
{
"원본": "2025년 산불진화임도 신설사업(기번8 울진.금강송.소광.산29외)",
"호표": 19,
"명칭": "제근 밀림(90m3/ha이상)",
"규격": "",
"단위": "m2",
"유형": "직접산식",
"인자": {
"A": "0.05 인/100m2"
},
"산식행": [
"제근 밀림(90m3/ha이상) / m2",
"◇임도품셈 2-3 적용",
"▷ 보통인부 소요인부 : A = 0.05 인/100m2",
"▷ 굴삭기(0.7m3) 소요시간 : B = 0.7 hr/100m2",
"1) 작업인부",
"보통인부 : 169,804 * A/100 = 84.9",
"2) 굴삭기(07",
"노 무 비 : 57,077 * B/100 = 399.5",
"재 료 비 : 19,232 * B/100 = 134.6",
"경 비 : 24,001 * B/100 = 168.0"
]
}
]
@@ -0,0 +1,170 @@
# -*- coding: utf-8 -*-
"""STmate 레시피 350개를 2026년 현행 품셈 원문과 대조한 근거 장부."""
import json
import pathlib
import re
from collections import Counter
from difflib import SequenceMatcher
def repo_root():
here = pathlib.Path(__file__).resolve()
return next(parent for parent in here.parents if (parent / "resources" / "knowledge").is_dir())
ROOT = repo_root()
BASE = ROOT / "resources" / "knowledge" / "original"
ANALYSIS = BASE / "경쟁사 프로그램" / "STmate 분석"
RECIPE = ANALYSIS / "30_원자료" / "일위대가_레시피.json"
OUTPUT = ANALYSIS / "30_원자료" / "일위대가_현행품셈_대조.json"
REPORT = ANALYSIS / "30_원자료" / "일위대가_현행품셈_대조_감사.txt"
SOURCES = {
"산림": BASE
/ "행정규칙"
/ "임도 품셈 적용기준 (현 산림사업 표준품셈)"
/ "첨부"
/ "(산림청고시 제2025-82호) 산림사업 표준품셈.md",
"건설": BASE / "원가계산" / "건설공사_표준품셈" / "2026년_건설공사_표준품셈.md",
}
HEADING = re.compile(r"^#{2,4}\s*(\d+(?:-\d+){1,3})\.?\s*(.*)$", re.MULTILINE)
NUMBER = re.compile(r"(?<![\d.])-?\d+(?:\.\d+)?(?![\d.])")
def sections(path):
text = path.read_text(encoding="utf-8")
hits = list(HEADING.finditer(text))
return [
{
"code": hit.group(1),
"title": hit.group(2).strip(),
"line": text.count("\n", 0, hit.start()) + 1,
"body": text[hit.end() : hits[i + 1].start() if i + 1 < len(hits) else len(text)],
}
for i, hit in enumerate(hits)
]
def compact(value):
return re.sub(r"[^0-9A-Za-z가-힣]", "", value or "").lower()
def words(value):
return {
word
for word in re.findall(r"[가-힣A-Za-z]{2,}", value or "")
if word not in {"적용", "표준품셈"}
}
def source_order(reference):
if "건설" in reference:
return ("건설",)
if "임도" in reference or "산림" in reference:
return ("산림",)
return ("산림", "건설")
def candidate(recipe, indexes):
reference = recipe.get("품셈근거") or ""
code_hit = re.search(r"\b\d+(?:-\d+){1,3}\b", reference)
target_words = words(reference) | words(recipe["명칭"])
ranked = []
for source in source_order(reference):
for section in indexes[source]:
score = 0
title_words = words(section["title"])
score += len(target_words & title_words) * 3
section_title = compact(section["title"])
recipe_title = compact(recipe["명칭"])
if section_title and (section_title in recipe_title or recipe_title in section_title):
score += 7
elif (
section_title and SequenceMatcher(None, section_title, recipe_title).ratio() >= 0.65
):
score += 5
parenthetical = [compact(value) for value in re.findall(r"\(([^)]+)\)", reference)]
if section_title and any(
value and (value in section_title or section_title in value)
for value in parenthetical
):
score += 7
if code_hit and code_hit.group() == section["code"] and score:
score += 2
if score:
ranked.append((score, source, section))
if not ranked:
return None
ranked.sort(key=lambda item: (-item[0], item[1], item[2]["line"]))
return ranked[0]
def observed_quantities(recipe):
values = []
for item in recipe["구성"]:
value = item["수량"]
try:
number = float(str(value).replace(",", ""))
except ValueError:
continue
values.append({"명칭": item["명칭"], "수량": number, "단위": item["단위"]})
return values
def number_present(value, body):
target = float(value)
return any(abs(float(raw) - target) < 1e-9 for raw in NUMBER.findall(body.replace(",", "")))
recipes = json.loads(RECIPE.read_text(encoding="utf-8"))
indexes = {name: sections(path) for name, path in SOURCES.items()}
result = []
for recipe in recipes:
reference = recipe.get("품셈근거") or ""
match = candidate(recipe, indexes)
quantities = observed_quantities(recipe)
row = {
"종류": recipe["종류"],
"명칭": recipe["명칭"],
"규격": recipe["규격"],
"단위": recipe["단위"],
"관측수량": quantities,
"실무_품셈근거": reference or None,
"현행근거": None,
}
if match:
score, source, section = match
found = sum(number_present(item["수량"], section["body"]) for item in quantities)
row["현행근거"] = {
"원문": str(SOURCES[source].relative_to(ROOT)).replace("\\", "/"),
"": section["code"],
"제목": section["title"],
"": section["line"],
"후보점수": score,
"수량문자_확인": found,
"수량개수": len(quantities),
}
if reference and score >= 7 and quantities and found == len(quantities):
row["상태"] = "현행원문_수량문자_전부확인"
elif reference and score >= 7:
row["상태"] = "현행절_확인_수량수동대조필요"
else:
row["상태"] = "현행절_후보_수동대조필요"
elif reference:
row["상태"] = "근거문구_현행위치미확인"
else:
row["상태"] = "근거미기재"
result.append(row)
assert len(result) == 350
OUTPUT.write_text(json.dumps(result, ensure_ascii=False, indent=1), encoding="utf-8")
counts = Counter(row["상태"] for row in result)
lines = ["### STmate 일위대가 350개 × 2026년 현행 품셈 대조", ""]
lines += [f"{name}: {count}" for name, count in sorted(counts.items())]
lines += [
"",
"'수량문자 확인'은 현행 절 본문에 같은 숫자가 존재한다는 1차 검사이며 확정 판정이 아니다.",
"※ 근거가 없거나 조건·규격 해석이 필요한 값은 사용자 협의 없이 갱신하지 않았다.",
]
REPORT.write_text("\n".join(lines) + "\n", encoding="utf-8")
print("\n".join(lines))
@@ -0,0 +1,278 @@
# -*- coding: utf-8 -*-
"""일위대가 레시피 사전 — 전수 추출 + 누락 감사.
시트 (일위대가표·중기사용료·단가산출근거) 호표를 하나도 빠뜨리지 않는다."""
import sys, io, re, pathlib, warnings, collections, json
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
warnings.filterwarnings("ignore")
import openpyxl
def _repo():
p = pathlib.Path(__file__).resolve()
for q in p.parents:
if (q / "resources" / "knowledge").is_dir():
return q
return pathlib.Path(r"D:\aislo-wt\sub")
REPO = _repo()
PRAC = REPO / "resources" / "knowledge" / "original" / "실무문서"
HOPYO = re.compile(r"^제?\s*\d+\s*호표$")
RE_COMP = re.compile(r"(노\s*무\s*비|재\s*료\s*비|경\s*비)\s*:\s*([\d,]+(?:\.\d+)?)\s*/\s*Q")
RE_QF = re.compile(r"\bQ\s*=\s*([^=\n']{3,70})")
RE_DIRECT = re.compile(
r"^\s*([^:*=]{2,40}?)\s*:\s*([\d,]+(?:\.\d+)?)\s*\*\s*([\d,]+(?:\.\d+)?)\s*([^\s/=]{0,8})"
)
RE_PUM = re.compile(r"(?:[▣▶●※]\s*)?((?:임도|건설|산림)?표준품셈.{3,80})")
RE_REV = re.compile(r"^\s*([\d,]+(?:\.\d+)?)\s*\*\s*([\d,]+(?:\.\d+)?)\s*=")
SKIP = {"합계", "", "소계", "총계", "총합계"}
def parse_grid(ws):
"""일위대가표·중기사용료 — A열 제목, 구성이 자원행."""
rows = [
list(r) for r in ws.iter_rows(min_row=1, max_row=ws.max_row, max_col=14, values_only=True)
]
out = []
cur = None
for r in rows:
a = (str(r[0]) if r[0] is not None else "").strip()
k = a.replace(" ", "")
if HOPYO.match(k):
cur = {"no": len(out) + 1, "title": None, "items": [], "q": None, "raw": 0}
out.append(cur)
continue
if cur is None:
continue
if k in SKIP:
continue
cur["raw"] += 1
if cur["title"] is None and a and (r[3] not in (None, "")) and (r[2] in (None, "")):
cur["title"] = (a, str(r[1] or "").strip(), str(r[3] or "").strip())
continue
if a and (r[2] not in (None, "")):
cur["items"].append((a, str(r[1] or "").strip(), str(r[3] or "").strip(), r[2]))
return out
def parse_san(ws):
"""단가산출근거 — B열 제목('명칭 규격 / 단위'), 구성은 산식(중기 시간당단가 / Q)."""
rows = [
list(r) for r in ws.iter_rows(min_row=1, max_row=ws.max_row, max_col=8, values_only=True)
]
out = []
cur = None
for r in rows:
b = (str(r[1]) if r[1] is not None else "").strip()
k = b.replace(" ", "")
if HOPYO.match(k):
amt = sum(x for x in r[2:6] if isinstance(x, (int, float)))
cur = {
"no": len(out) + 1,
"title": None,
"items": [],
"q": None,
"pum": None,
"raw": 0,
"lines": [],
"amt": amt,
}
out.append(cur)
continue
if cur is None:
continue
if k in SKIP:
continue
cur["raw"] += 1
if b:
cur["lines"].append(b)
if cur["title"] is None and b and "/" in b:
nm, _, un = b.rpartition("/")
nm = nm.strip()
un = un.strip()
sz = ""
if " " in nm:
p1, p2 = nm.split(" ", 1)
nm, sz = p1.strip(), p2.strip()
cur["title"] = (nm, sz, un)
continue
if cur.get("pum") is None:
mp = RE_PUM.search(b)
if mp:
cur["pum"] = mp.group(1).strip(" '\"")
m = RE_QF.search(b)
if m and cur["q"] is None:
cur["q"] = re.sub(r"\s+", "", m.group(1))
hit = False
for mm in RE_COMP.finditer(b):
cur["items"].append(
(mm.group(1).replace(" ", ""), "시간당 성분단가", "", mm.group(2))
)
hit = True
if not hit:
md = RE_DIRECT.match(b)
if md:
cur["items"].append((md.group(1).strip(), "", md.group(4).strip(), md.group(3)))
hit = True
if not hit:
mr = RE_REV.match(b)
if mr:
cur["items"].append(
((str(r[0]) if r[0] is not None else "").strip() or "자원", "", "", mr.group(1))
)
hit = True
return out
def special_kind(lines):
text = "\n".join(lines)
if "L1=" in text and "V1=" in text and "Cm" in text:
return "중기운반_사이클"
if "N=V*T" in text.replace(" ", ""):
return "인력운반_사이클"
if "구역화물" in text:
return "구역화물_운반"
if "Q=3600" in text.replace(" ", ""):
return "기계작업_사이클"
if "일작업량" in text or "일 시공량" in text:
return "일작업량_환산"
return "직접산식"
def special_values(lines):
text = "\n".join(lines).replace(",", "")
keys = "L1 L2 V1 V2 V3 V4 t1 t2 t3 t4 E K f Cm N Q q q1 A T T1 V".split()
values = {}
for key in keys:
m = re.search(rf"(?<![A-Za-z0-9_]){key}\s*=\s*([^\n]+)", text)
if m:
values[key] = m.group(1).strip()
return values
SHEETS = {"일위대가표": "일위대가", "중기사용료": "중기", "단가산출근거": "단가산출"}
rec = {}
audit = collections.Counter()
miss = []
specials = []
files = 0
for p in sorted(PRAC.rglob("*.xlsx")):
if p.name.startswith("~$"):
continue
try:
wb = openpyxl.load_workbook(p, data_only=True, read_only=True)
except Exception:
continue
if "공사원가계산서" not in wb.sheetnames:
continue
files += 1
for sh, kind in SHEETS.items():
if sh not in wb.sheetnames:
continue
bs = parse_san(wb[sh]) if sh == "단가산출근거" else parse_grid(wb[sh])
audit[(kind, "호표")] += len(bs)
for b in bs:
if not b["title"]:
audit[(kind, "제목없음")] += 1
miss.append((p.stem[:18], sh, b["no"], "제목 못 잡음", b["raw"]))
continue
if not b["items"]:
if not b.get("amt"):
audit[(kind, "빈호표")] += 1
else:
audit[(kind, "구성없음")] += 1
miss.append((p.stem[:18], sh, b["no"], b["title"][0][:24], b["raw"]))
specials.append(
{
"원본": p.stem,
"호표": b["no"],
"명칭": b["title"][0],
"규격": b["title"][1],
"단위": b["title"][2],
"유형": special_kind(b["lines"]),
"인자": special_values(b["lines"]),
"산식행": b["lines"],
}
)
continue
audit[(kind, "정상")] += 1
key = (kind,) + b["title"]
if key in rec:
audit[(kind, "중복")] += 1
else:
rec[key] = (b["items"], b["q"], b.get("pum"))
print("### 누락 감사 — 출력 엑셀 %d" % files)
print(
"%-10s %7s %7s %7s %8s %8s %8s"
% ("종류", "호표", "정상", "중복", "제목없음", "빈호표", "구성없음")
)
for kind in ("일위대가", "중기", "단가산출"):
print(
"%-10s %7d %7d %7d %8d %8d %8d"
% (
kind,
audit[(kind, "호표")],
audit[(kind, "정상")],
audit[(kind, "중복")],
audit[(kind, "제목없음")],
audit[(kind, "빈호표")],
audit[(kind, "구성없음")],
)
)
tot = sum(audit[(k, "호표")] for k in SHEETS.values())
ok = sum(audit[(k, "정상")] for k in SHEETS.values())
print(
"\n 호표 %d개 중 %d개 처리 (%.1f%%) · 못 잡은 것 %d" % (tot, ok, 100 * ok / tot, tot - ok)
)
for m in miss[:10]:
print(" %-18s %-12s 호표%-4d %-26s%d" % m)
print("\n### 서로 다른 조합 %d" % len(rec))
for a, b2 in sorted(collections.Counter(x[0] for x in rec).items()):
print(" %-10s %3d" % (a, b2))
tot2 = sum(len(v[0]) for v in rec.values())
print(" 구성행 합계 %d (조합당 평균 %.1f)" % (tot2, tot2 / max(len(rec), 1)))
nq = sum(1 for v in rec.values() if v[1])
print(" Q식을 가진 조합 %d" % nq)
out = (
REPO
/ "resources"
/ "knowledge"
/ "original"
/ "경쟁사 프로그램"
/ "STmate 분석"
/ "30_원자료"
/ "일위대가_레시피.json"
)
J = [
{
"종류": k[0],
"명칭": k[1],
"규격": k[2],
"단위": k[3],
"Q식": v[1],
"품셈근거": (v[2] if len(v) > 2 else None),
"구성": [
{
"명칭": a,
"규격": c,
"단위": u,
"수량": (float(q) if isinstance(q, (int, float)) else str(q)),
}
for a, c, u, q in v[0]
],
}
for k, v in sorted(rec.items())
]
out.write_text(json.dumps(J, ensure_ascii=False, indent=1), encoding="utf-8")
print("\n%s %.1f KB" % (out.name, out.stat().st_size / 1024))
special_out = out.with_name("특수_단가산출_21개.json")
assert len(specials) == 21
assert sum(1 for item in specials if item["유형"] == "중기운반_사이클") == 3
special_out.write_text(json.dumps(specials, ensure_ascii=False, indent=1), encoding="utf-8")
print("%s %d" % (special_out.name, len(specials)))
@@ -69,6 +69,7 @@
| 28 | **변환·수량산출·식언어** — 식 변수 대상/저장, 조달청 변환, 직종 106종 |
| 29 | **암호화자료의 뼈대** — 값 없이 구조만, 카탈로그 규모와 분류 깊이 |
| 30 | ⭐ **조달청 호환규정 XSD** — 국가 표준 납품 규격 전문 |
| 31 | ⭐ **일위대가 레시피 사전** — 조합 350개, 누락 감사 |
## 확정된 굵직한 것
@@ -101,6 +102,8 @@
| `jb_shapes.py` · `jb_block_map.py` · `jb_expansion_diff.py` | JB 43개 형태·블록·확장 차이 |
| `rate_grid_defs.py` · `settings_axis_map.py` | 폼에서 그리드·선택축 추출 |
후속 레시피 분석은 `20_분석/31_일위대가_레시피_사전.md``20_분석/32_특수산식과_현행품셈_대조.md`를 본다.
## 다음
`13_통제시험_목록.md` 의 T1~T11. **프로그램을 직접 돌려 값 하나씩 바꿔 보는 일**이라 정적 분석으로는 못 가름. 우선순위 1은 T1(요율 좌표 매핑).