knowledge(단가산출): 이름 식 계산 없이 만들기 분석 — 만약 겹침·로직 비목 미정·멈춤 로직 실수 확인

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PAdA5ThqmtVSsbzjusk1cJ
This commit is contained in:
2026-09-22 18:34:00 +09:00
co-authored by Claude Sonnet 5
parent fc7af43cf4
commit 1ecb5f02a4
@@ -0,0 +1,143 @@
# 분석 — 텍스트 수식의 이름 식, 계산 없이도 만들기
검토만 · 코드 안 고침. `master_text.py`·`master_formula.py`·`master_copy.py` 읽기와
실제 데이터(`resources/master_data/로직_*.json`) 전수 확인으로 뒷받침.
## 0. 사용자 안
- 지금: 만약() 조건을 실제로 셈해 걸린 가지만 이름 식에 적음(`named()``만약` 분기).
- 사용자 안: 계산이 안 되면 양쪽 가지를 다 보이고, 계산이 되면 지금처럼 걸린 가지만 보임.
## ① 이름 식 만드는 자리 — 만약() 말고 계산이 필요한 곳 전수 확인
코드를 따라가 보면 `named()`(이름 꼴을 만드는 함수) 안에서 실제 값 계산(`mf.evaluate`)을
부르는 자리는 딱 하나 — **만약() 조건 판정** 뿐임. 나머지는 계산 없이도 그대로 됨:
- 요소 이름(석공 노임 같은 것) — 표의 `이름` 칸을 그냥 읽음. 계산 아님.
- 별칭(표 찾기·로직 부르기 이름, QF000421 → 메쌓기인력 같은 것) — `aliases()`가 식 글자를
그대로 훑어 별칭을 짓는 것도 계산 아님. 실행 전에 이미 다 지을 수 있음.
- 중간 값·덧줄 이름(증가율·노무비 같은 것) — 그 이름 글자를 그대로 적을 뿐, 값을 몰라도 됨.
- 끝수 — 대상·자리·방법을 적는 한 마디일 뿐, 계산이 아니라 규칙을 읽는 것.
**그런데 전수로 데이터를 훑어 보니, 만약() 말고 계산이 정말 필요한 자리가 두 곳 더 있음**
브레인이 짚은 것보다 큼:
**(가) 원문번호에 `{이름}` 홀이 낀 요소 참조.** 예 — `EQ:{장비}`, `MT:{합판}`, `LB:{용접직종}`,
`MO:휘발유:{지역}`. 이런 자리는 표의 어느 줄을 가리키는지부터 그 홀 값(대개 입력, 가끔 중간
값)을 알아야 정해짐 — 모르면 이름 대신 `LB000123:{용접직종}` 같은 날 것 키만 보임(지금
`_elem_name` 이 실패하면 원문 그대로 돌려주는 자리가 이미 있음 — 보기 좋은 이름을 못 만들
뿐 죽지는 않음). **14개 파일·13가지 홀 이름**(장비·합판·각재·용접직종·지역·직종 등)에서 씀.
확인해 보니 예를 들어 `EQ:{장비}``장비`**중간 값**(GC000138 등)이라 홀 하나를 풀려 해도
그 중간 식을 실제로 셈해야 함 — 입력만 있다고 되는 게 아니라 중간 단계 계산이 필요할 수 있음.
**(나) 로직을 부르는 호표 줄의 비목(노무비/재료비/경비) 갈래.** 전체 로직 1,354개의 호표 항목
4,920개 가운데 **990개가 「로직」 종류**임(나머지는 인력 3,255·재료 675). 인력·재료·기계 종류는
비목이 식 정의에 그대로 박혀 있어(예: "노무비") 계산 없이도 어느 표에 넣을지 바로 앎.
**로직 종류 990개는 예외 없이(990/990) 비목을 안 밝힘** — 불러온 로직 자체의 노무비·재료비·
경비 몫을 실제로 계산해야 그 줄이 세 표 가운데 어디에 얼마씩 들어가는지 정해짐. 이것은
「알약 + 사칙연산」 검토([[분석-알약-상세줄]])가 상정한 「세 묶음 표」 얼개 자체와 부딪히는
자리임 — 계산 없이 보이는 이름 식 모드에서는 이 990개 줄을 애초에 세 표로 못 나눔.
## ② 사용자 안을 넣으면 값 식과 줄이 짝으로 안 맞는 자리
`텍스트 수식` 화면(`M01_MasterData_UI_LogicLab_Formula.ts`)을 다시 보면, 「짝」 번호는
이름글 칸과 값 칸을 나란히 세우는 것이 아니라 — **한 줄(line) 객체 하나가 이름글·글 두 칸을
이미 같이 담고 있고**, 짝은 그 줄을 가리키는 안정된 이름(마우스오버·클릭으로 같은 줄을
밝히는 용도)일 뿐임. 그래서 「양쪽 칸이 서로 다른 배열이라 자리가 밀린다」 같은 문제는
원래 없음.
진짜 안 맞는 자리는 두 가지임:
1. **①-(나)의 990개 로직 호출 줄** — 계산이 없으면 그 줄이 노무비 표에 들어갈지 재료비 표에
들어갈지 경비 표에 들어갈지부터 못 정함. 지금 코드(`_shares()`)는 계산된 금액이 0이 아닌
비목만 걸러 그 표에 넣는 방식이라, 계산 없는 모드에서는 이 필터 자체가 작동을 못 함.
→ 걸음 제안: 계산 없는 모드에서는 이런 줄을 세 표에 억지로 나누지 않고, **호표에 적힌
원래 차례 그대로 「로직 부르기」 한 줄로**(비목 안 나눔 표시) 보이는 수밖에 없음.
2. **만약() 양쪽 가지를 다 보이는 줄** — 값 칸(글)은 실제 수가 있어야 하므로 계산이 안 되면
그 칸 자체가 비게 됨. 한 줄 안에서 이름글만 길어지고 값 칸은 빈칸(또는 「계산 안 됨」
한 마디)이 되는 것뿐, 줄 개수·짝은 안 흔들림 — 이 자리는 걱정한 것만큼 문제는 아님.
## ③ 양쪽 가지를 다 보일 때 글이 얼마나 길어지는지 — 실수로 셈
전체 로직 1,354개를 실제로 파싱해 만약() 겹침을 셈:
| 겹침(한 로직 안 가장 깊은 자리) | 로직 수 |
|---|---|
| 1 | 291 |
| 2 | 74 |
| 3 | 26 |
| 4 | 8 |
| 5 | 1 |
| 6 | 7 |
| 7 | 3 |
| 8 | 2 |
| 14 | 6 |
| 19 | 2 |
| **28(가장 깊음)** | **1** |
- 만약()을 **하나라도 쓰는 로직 417개**(전체의 31%) · 만약() 등장 총 **3,352번**.
- 대부분(291+74=365개, 88%)은 겹침 1~2로 짧게 끝남 — 조건 하나짜리라 양쪽 다 보여도
줄 하나가 두 배쯤 길어지는 정도.
- 꼬리에 겹침이 아주 깊은 것들이 있음 — 가장 깊은 것은 **GC000887(이중보온관 기계부설)**의
「부설크레인」 중간 값으로, `만약(관경=='ø20(90)', ..., 만약(관경=='25(90)', ..., 만약(...`
처럼 **관경 종류마다 다른 장비를 고르는 28겹 사다리꼴** 식임(사실상 switch 문). 지금은
계산이 되면 한 줄(예: "부설크레인 = 2104-0015")로 끝나지만, 양쪽 가지를 다 펼치면 이런
자리는 최대 **29가지 선택지가 한 줄에 줄줄이 나열**됨 — 화면에 그대로 펼치면 안 읽힘.
→ 걸음 제안: 겹침이 일정 수(예: 3~4) 넘는 사다리꼴은 "다 보이기"를 문자 그대로 펼치지
말고 "표에서 고를 값(관경별로 다름)" 한 마디로 뭉뚱그리는 예외 규칙이 필요함.
## ④ 계산이 멈추는 로직 — 이름 식만이라도 보이는지
실제로 전체 로직을 시험 입력으로 다시 돌려 확인 — **현재 멈추는 로직은 60개**
(브레인이 든 63개와 조금 다름 — 데이터가 그새 바뀐 듯, 최신 값으로 60개 씀).
**중요한 발견 — 멈추는 까닭이 거의 다 만약()과 무관함.** 60개를 다 훑어 보면 예외 없이
"「재료 고르기 … MT0306xx」 값 없음 — 관리자가 채울 값" 또는 "재료 고르기 후보 없음" 꼴 —
**재료 단가를 아직 안 채워서** 멈춤. 만약() 분기 문제가 아니라 데이터 공백 문제.
실제로 GF000180(집수정)으로 확인 — 호표 줄 **9개** 가운데 재료 하나("MT030712 집수정
뚜껑")의 값이 없어서 `POST /text` 가 **통째로 실패**함(`ok: false`, 다른 8줄은 전혀
안 보임 — 지금 화면은 "계산 멈춤" 한 마디만 뜸). 나머지 8줄은 그 재료와 아무 상관 없는데도
같이 막힘 — 지금 `lines()``mf.run()`(전체 계산)을 맨 먼저 통째로 부르고, 그게 하나라도
실패하면 이름 식이든 값 식이든 아무것도 못 만드는 구조이기 때문.
→ **이름 식만이라도 보이려면, 60개 대부분은 만약() 처리와 상관없이 그냥 될 일**임 — 막힌
재료가 든 그 한 줄만 "값 없음" 표시로 두고, 나머지 8줄은 이름 식(과 계산 가능한 값 식)을
그대로 보이면 됨. 필요한 것은 `mf.run()`을 맨 앞에서 통째로 부르는 지금 구조를 풀어,
줄마다 따로 계산하고 실패한 줄만 표시를 달리하는 것.
## ⑤ 2장(알약) 검토와 부딪히는지
부딪힘. [[분석-알약-상세줄]]의 알약 목록(별칭 `aliases()`)은 계산 없이 식 글자만 훑어
짓는 것이라 원래도 계산이 필요 없었음 — 그 점은 그대로 안전함. 하지만:
- ①-(가) 홀 낀 요소 참조 — 알약 이름도 `_elem_name`/`_base`를 그대로 쓰므로, 계산이
안 되면 알약 이름도 똑같이 날 것 키(`LB000123:{용접직종}`)로 떨어짐. 이름 식과 같은 문제.
- ①-(나) 로직 호출 990개의 비목 미정 — 알약 검토가 상정한 "세 묶음(노무비·재료비·경비)
표를 폭 맞춰 나란히" 얼개 자체가, 계산 없는 이름 식 모드에서는 이 990개 줄을 그 세 표
가운데 놓을 자리를 못 정하는 것과 똑같이 부딪힘.
두 문제 다 하나만 고치면 됨 — 이번 검토의 ⑥ 걸음이 그대로 알약 쪽에도 적용됨.
## ⑥ 넣을 자리와 걸음 (짧게)
- `master_text.py``lines()``mf.run()`을 맨 앞에서 전체 통으로 부르는 구조부터 풂 —
줄마다 따로 계산 시도하고, 실패한 줄만 "계산 안 됨" 표시.
- `named()`의 만약 분기 — 조건 계산이 실패하면(예: 필요한 입력이 없음) 예외로 안 죽고
두 가지(참일 때/거짓일 때)를 "(조건) 이면 A 아니면 B" 꼴로 이어 붙이는 길 추가.
단, ③에서 본 것처럼 겹침이 깊은 사다리꼴(3~4겹 넘음)은 예외로 뭉뚱그림.
- `_elem_name`/`_base` — 홀이 안 풀리면 지금처럼 날 것 키로 물러나는 대신, "무엇을 고르는
자리인지"(홀 이름)만이라도 보이는 문구로 다듬음(예: "장비별 손료").
- 로직 호출 990개 줄 — 계산 없는 모드에서는 세 표로 안 나누고 원래 호표 차례 그대로
한 줄로 보임(비목 표시는 "계산 후 확인").
- 순서 제안: (a) `lines()` 계산-분리 → (b) 만약 두 가지 표시 → (c) 알약 쪽에도 같은
뼈대 재사용(⑤). 알약 검토의 세 표 폭 맞춤 작업과 겹치므로 같은 걸음에서 함께 다루는
편이 나음.
## 결론
만약()은 문제의 일부일 뿐임. 실제로는 **로직 호출 990개 줄의 비목 미정**(전체의 100%)이
계산 없는 이름 식을 막는 더 큰 자리이고, **60개 멈춤 로직**은 만약과 거의 무관하게 재료
단가 공백 때문이며 `lines()`의 "전체 계산 먼저" 구조만 풀면 대부분 저절로 풀림. 만약()
자체는 417개 로직(31%)에서 쓰이고 대개 짧지만(88%가 겹침 1~2), 드물게 28겹까지 가는
사다리꼴 식이 있어 "양쪽 다 보이기"는 깊이 제한 예외가 필요함.