PNCAD (포인캐드 · 구 poincad2) — 작업 규칙
규칙만 둔다. 상태·이력은
handoff.md, 완료이력은archive.md.
🆕 신규 (2026-08-19 밤 — 몸통규격표 출력 화면 spec.html)
- 🔴 몸통규격표 본표는 리스트가 아니라 매트릭스다 — RDL
Tablix2의 행 그룹 =POSSTR(아티클) · 열 그룹 =NAME1(부재명), 한 칸에 (높이*길이, 수량) 두 열이 붙는다. 행 머리 3칸(번호·제품명·수량)의 머리글은 2줄로 쪼갠 글자다(번/호 · 제품명/W*D*H · 수/량) — 별개 컬럼이 아니다. 열 머리 밑줄 축 라벨은Textbox50식이 정본: 측판=깊이*높이 · 뒤판=길이*높이 · 서랍재/뒤판각목=높이*길이 · 그 외=깊이*길이. - 🔴 A4 가로 한 장에 부재명 8열(행머리 4.39cm + 8×2.88cm). 그 이상이면 장을 나눈다 — imos 도 같은 폭이다.
종이 규격 =
29.7×21cm· 여백상하 5mm / 좌 11mm / 우 2mm(RDLPage). - 🔴 규격표 셀 수정본도 「사람이 고친 것만 DB」 규약을 그대로 쓴다. 원본은 저장하지 않는다 —
엔진(
schema.js)이 늘 다시 만드니까 되돌리기 = 그 키를 지우는 것이다. 저장소 =pncad_scene.spec_override(작업본당 JSON 한 칸,PUT /api/pncad/scene/<no>/spec). ⚠ 씬 저장(update)은 이 컬럼을 안 건드린다 — 배치를 다시 저장해도 규격표 수정본이 살아야 한다. - ⚠ JS 객체 리터럴에
걸레받이/문틀재같은 전각 기호(/)를 키로 쓰면 문법 오류다(식별자 문자가 아니다). 섹션 이름은 배열 하나로 두고Object.fromEntries로 만든다. - 출력용 화면은 앱과 분리한다(
spec.html,/production/pncad/spec.html) — 인쇄 CSS 가 3D 앱 레이아웃과 싸운다. 진입 = app.html 규격표 창의 「🖨 출력용 규격표」, 인자?scenes=1,2또는?field=<현장명>. - 테스트 DB 에는 PNCAD 작업본이 0건이다 — 화면 E2E 는 임시 작업본을 직접 만들고(POST) 끝나면 지운다(
tools/_e2e_spec.py).
🆕 신규 (2026-08-19 밤 — qahelper 사이드바 뒤 3D 뷰 폭 안 돌아오던 버그)
- 🔴 PFS qahelper 는
body { padding-right: 380px }를 넣었다 뺀다 — 창 크기는 안 바뀌므로window resize가 안 뜬다. 캔버스/차트처럼 px 로 크기를 박는 요소는addEventListener('resize', …)로는 못 따라간다 → 컨테이너를ResizeObserver로 직접 본다. PNCAD 증상 = 패널 열린 동안 카탈로그 토글 등으로resize()가 한 번 돌면 캔버스가 380px 줄어든 채 굳고, 패널을 접어도#view의 배경 그라디언트만 남아 "배경색은 같은데 화면만 좁아진 것" 처럼 보인다(원인 추적이 어렵다). 수정 =app.htmlnew ResizeObserver(resize).observe(view)(editor.js 그래픽 호스트가 이미 쓰던 방식과 통일). 회귀체크 =cd C:\dev\pncad && PYTHONUTF8=1 python tools/check_view_resize.py(자체 http 서버 + Playwright, 열림 -380 / 닫힘 복구 assert). - PNCAD 는 PFS 사이드바가 없는
target=_blank단독 화면이라, 우측에 뭔가 뜨면 그게 곧 본문 폭을 먹는다. 우측 오버레이를 새로 붙일 땐 폭 복구를 같이 확인한다.
🆕 신규 (2026-08-19 밤 — imos DWG 실좌표 대조기 verify3 · 배치 규칙 근본수정)
- 🔴 부재 좌표의 정본 = 오더 DWG 다. DB 에는 없다.
IDBGPL(86컬럼)·IDBINFO어디에도 부재 XYZ 가 없고,IDBGEOM(19,435행)은 아티클 삽입점이다(IDBINFO.ID와 조인,IDBGPL과는 0건). 실좌표는poinimos01:D:\ProgramData\imos AG\Factory\Imorder\<오더>\*.DWG를 받아 AutoCAD 2024 COM 으로 뜯는다(tools/dwg_parts.py→tools/mk_dwgjob.py→verify3.mjs). - 🔴 좌표 규약(실측 확정) — 아티클 로컬 원점 = 앞·아래·왼쪽.
+x오른쪽 ·+y앞→뒤 ·+z위. 월드 =IDBGEOM.IP + 로컬(OR_ = 0). PNCADpart.box와 축·원점이 완전히 같다* — 변환 없이 1:1. - 🔴 imos ELEMID = 면 주소이고 앞0 → 우1 → 뒤2 → 좌3 시계방향이다.
convert.mjs FACE_KEY가 좌우 뒤집혀 있었다. 대칭 장에선 치수가 같아 안 보이고 코너 기둥목처럼 좌우가 다른 부재에서만 드러난다. ⚠ 라벨을 바꾸면 트림 결합표schema.js PLAN_JOIN도 같이 미러링해야 한다 — 한쪽만 고치면 치수가 조용히 틀어진다(58.3%→57.9%, 미러링 후 복귀). - 🔴
SIZREF 'O'(Outer)는 앞판 평면뿐 아니라 몸통 존 자체를 장 외곽에 재기준한다. 종전엔fbase만 돌려 덧방은 맞고 기둥목만 왼쪽 끝에 박혔다(Δx 최대 +570). ※ imos 에 깊이 분할축은 없다 —DIVDIR= V/H/I/A 뿐이고LINDIVZ는 10,604행 전부 빈값. - 🔴 LINDIV 혼합칸 —
1+($CH높이)MM= 「66mm 고정」이 아니라 비율 1 + 65mm 가산이다. 단, 단위 없는 항이 맨숫자 리터럴일 때만 비율이다 —$밑장깊이-170mm처럼 변수면 그건 치수라 식 전체가 고정칸이다(안 가르면 도어 폭이 통째로 틀어진다). 종전엔 단위가 하나라도 붙으면 통째 고정으로 뭉개서1-30MM이 −29mm 짜리 칸이 됐고, 자체검사가 그 오답을 정답으로 박제하고 있었다. - 🔴 「밴드는 두께+1 을 먹는다」는 증상보정이었다. 진짜 −1 은 그 부재 자신의 TOPOFFS/BOTOFFS(−0.5×2)다. 존을 깎으면 같은 존을 쓰는 이동선반까지 같이 틀어진다. 존은 두께만 먹고, 세로 부재(L/R·앞면 구조재)가 자기 오프셋만큼 짧아지고 그만큼 떠 있게 한다.
- 🔴
anglelem.INSET= 그 면의 바깥쪽 법선 방향 밀어내기. 앞면이면 −y, 뒷면이면 +y. 도어 −20 · 코너 덧방 −4.5 · 뒤판각목 −18/−19/−20. 카탈로그에 이미 있었는데 좌표엔 안 쓰고 있었다. - 🔴 천/지판(CABIN 02·03)의 깊이 기준은 「전면」이다(페페 질문의 답).
UEBERST_V(ueb.v) 하나가 정한다 — v=0 이면 앞면 플러시, v<0(Rv 리빌)이면 그만큼 뒤로.ueb.h(HINTEN)는 크기만 줄이고 위치엔 무관. full depth 면 종전 뒤기준 식과 대수적으로 같아 하부장은 무변화다. - 🔴 밴드 깊이는 오더변수
$밴드폭이 CPfixW보다 우선한다(60/56). 이름에 mm 박힌 CP(_F_40mm)만 예외. - 🔴 밴드가 눕었나 세웠나 =
TRAVERSE.LAGE(0 눕힘 · 2 세움 · 1 VV · 3 mixed). 이름의_V는 관례일 뿐이다. 세운 밴드는 깊이↔높이가 뒤집히고(깊이=두께·높이=밴드폭), 깊이 기준이 장 외곽 깊이다(뒤판 뒤쪽을 채우므로). 뒤판이 깊이를 먹은 장은 뒤끝에서 1mm 띄운다. - 🔴 구조재 앞면은 오버레이가 아니다 — 존 앞면에서 안쪽으로 채운다(inset 0). 도어·덧방만 장 앞으로 나온다.
- ⚠ E2E 픽스처에 면 라벨(L/R)을 박지 마라 — ELEMID 매핑이 바뀌면 같은 부재가 L↔R 로 바뀐다.
전개된 부재에서
zone·side를 읽어 써라(_e2e_editor2가 그렇게 깨졌다). - ⚠ PFS 배포 후 「↻ imos 기준선 갱신」을 안 돌리면 화면은 안 바뀐다 — 화면 카탈로그는 DB 시딩판이다.
CLI =
PYTHONUTF8=1 python tools/_reseed.py [test|prod] --apply(미리보기는--apply없이). - ⚠ pfs
deploy-live는 전체 머지 금지 — 「선별 포워드」가 이 리포의 방식이다. master 를 통째 머지하면 PNCAD 와 무관한print-agent·orderpaper-sync까지 운영에 들어간다 (실측:agent.py−588줄).git checkout master -- <바꿀 파일만>으로 가져와 커밋한다.
🆕 신규 (2026-08-19 — CP 오버레이 · 부재목록 편집 · imos 기준선 갱신)
- 🔴 정본 규약 = 「imos 생성물은 파일 · 사람이 고친 것만 DB」. CP·아티클·배리어블 셋 다 같은 패턴이다.
DB 로 통째 옮기면
mkcp2/convert.mjs를 다시 돌릴 때마다 충돌하고, 파일만 두면 화면에서 못 고친다. CP =data/cp.json+pncad_cp_override(필드 단위) →schema.applyCpOverride()로 병합. 화면과 node 대조기가 같은 함수를 쓴다 — node 쪽은tools/pull_overrides.py로 오버레이를 받아 둔다. 🔑 필드 단위인 이유 = 사람은thk만, imos 는ueb만 바뀌는 경우가 실재한다. 레코드째 덮으면 그런 CP 는 imos 갱신을 영영 못 받는다. - 🔴
applyCpOverride의 타입 기준은 반드시 imos 원값(base)이다. DB 는 값을 문자열로 준다 — 이미 덮인 값을 기준 삼으면 한 번 문자열이 된 숫자칸이 영영 문자열로 굳어 계산이 조용히 틀어진다.node schema.js「오버레이 3」에 「두 번 덮어도 숫자」 케이스로 박아 뒀다. - 🔴
cp.json을 그 자리에서 덮으면 imos 원값이 사라진다 — 화면의 「imos 원값 병기」가 병합본을 가리킨다.app.html이CPBASE = structuredClone(CPM)로 사본을 먼저 뜬다(구현 중 발견·수정). - 🔴
_seed_if_empty()는pncad_article이 비었을 때만 돈다 — convert.mjs 를 고쳐도 PFS 화면은 최초 시딩판 그대로다(단독 8799 는 파일을 읽어 새 값이 나오니 로컬만 보면 못 잡는다). → 「↻ imos 기준선 갱신」(/api/pncad/reseed). 손 안 댔는지 판정 = 가장 최근kind='seed'백업판과의 정규형 대조다.origin='imos'는 편집해도 안 바뀌어(컨트롤러 규약) 판정에 못 쓴다. - 🔴 재시딩에 지우는 동작을 넣지 마라. 손 안 댄 행만 UPDATE · 없던 것만 INSERT · imos 에서 사라진
아티클은 보고만 한다. 통짜
DELETE하는replace_all()은 백업 복원 전용이다(재시딩이 그걸 쓰면 사용자 제작 아티클이 통째로 날아간다). - ⚠
thk오버레이는verify2를 안 움직인다 — 오더변수바디자재_주방가구/일반가구(ctx.bodyThk)가 CP 두께보다 우선한다. 오버레이가 대조기까지 먹히는지 확인하려면 밴드fixW같이 ctx 가 안 덮는 칸으로 해라 (60→80 주입 시 58.3%→58.0%). - ⚠ E2E 의 PFS 경로는
/production/pncad/다(/pncad/는 단독 8799 전용). 로그인도 폼 채우기가 아니라/api/auth/loginfetch 로 한다 — 로그인 페이지 마크업에 안 묶인다. - imos 보링 체인 실측(다음 단계 재료) — 2026 가공 123,175행인데 워크그룹 27종·(WGNAME,직경,깊이,
방향,가공구분) 조합 57개뿐이고 상위 3종이 77%다.
IDBWG는(ORDERID, ID)로IDBGPL과 부재 단위로 맞물려IP_X/IP_Y/IP_Z(부재 자체 좌표계)·OR_*·DIA·DE·CNT를 준다 → verify2 와 같은 좌표 대조기가 가능하다.IDBWG.NAME1은 부재명이 아니라 PRODCODE 다(IDBGPL.NAME1 과 뜻이 다르다). 페페 결정 = 9단 체인 이식이 아니라 실측 역공학 우선.
🆕 신규 (2026-08-18 심야 — imos 아티클 생성 구조 완전복사 1~3단계)
- 🔴 트림코드(
anglelemSTARTTRIM/ENDTRIM/TOPTRIM/BOTTRIM)가 부재쌍 결합의 정본이다. 평면 윤곽은 F→L→B→R 순환이고 결합쌍은(F.END,L.START) (L.END,B.START) (B.END,R.START) (R.END,F.START).L=관통(이웃을 덮고 지나간다) ·S=끼움 ·P=CP 파라미터가 결정(뒤판 RGRDE) ·D=관통변형 ·M=중앙. 🔑 깎는 조건은 「내가 S」가 아니라 「내가 S 이고 이웃이 L」이다 — 양쪽 다S면 관통재가 없다. 앞면 도어가 정확히 그 케이스(도어SSSS↔ 측판SLLL/LSLL)라, 이 가드가 없으면 풀오버레이 도어가 측판 두께만큼 좁아진다. 여기에 같은 변의STARTOFFS/ENDOFFS/TOPOFFS/BOTOFFS(mm)를 항상 더한다. 실측 정본 =일반가구_2단서랍_날개측판 =D − 날개두께18 − 2(STARTOFFS)→ imos 458/458. 전량 대조 56.8% → 58.3%(측판 깊이 + 앞판 폭 + 뒤판 좌우여유). 자체검사 =node schema.js트림 3건. - 오프셋 수식(
STAOFFFOR등)은 안 파싱한다 — 2,270행 중 2,257행이 숫자 리터럴이고 숫자 컬럼이 그 캐시값이다. 비숫자 수식은68-550·88-$밑장깊이류 18행뿐 → 캐시된 숫자가 이미 답이다. - ⚠ 편집기에서 부재정의를 바꾸면 트림·오프셋이 조용히 날아갔다(
bindZone이parseCP로 CP 를 통째로 새로 만들었다). 면 CP 를 재조립하는 코드는ref·inset·trim·offs를 반드시 물려줘야 한다. - 🔴 아래 3개는 imos 컬럼이 있어도 규칙으로 먹이면 안 된다(전량 대조가 내려간다 — 근거는 코드 주석에 남겼다):
DIVTYPE='S'(부자재)로 선형 판정 → 58.3%→54.5%.S에 pc11(속대·보조목·누적자재)처럼 깊이를 쓰는 게 섞여 있다. PRODCODE 12·13 이 정본.SIZREF5슬롯의 첫·끝을 양 끝 앵커로 분리 적용 → 58.3%→58.1%, 회귀는 전부 우수 코너장(BC_R_001계). 슬롯 방향이 좌우 대칭에서 뒤집히는 듯 → 방향 규칙을 실측으로 확정하기 전엔 불리언outer가 정본.- 가상면(
FUNCT='V')INSET을 내경에 반영 → 부호를 어느 쪽으로 줘도 58.2%. 카탈로그 165종에 46존뿐. → 셋 다 카탈로그에는 원문 보존(divtype·sizref·vinset). 편집기 표시·후속 조사용이다. - 🔴
anglzone.LEFTHINGE/RIGHTHINGE는 힌지 방향이 아니다.RIGHTHINGE는906126224같은 초기화 안 된 정수가 들어 있고, 둘 다 켜진 존이 10,604 중 8,796인데 도어 면을 가진 존은 614뿐이다(상관 없음).anglzone.DOORHISIDE는 도어존 614개가 전부 0. 도어 방향은 CP 이름이 쥔다(하부도어_SDO_R·_DDO·_FDO). → 「힌지 10,344건 확보」는 이 쓰레기값을 센 것이었다. 힌지 기능은 CP 이름 파싱으로 다시 설계할 것. - 🔴 이동선반(
DIVTYPE='A') 수량은 아티클에서 안 나온다 — 오더에서 사람이 정한다.신발장_DD124인스턴스에서$Q_AS01/$Q_AS02가 전부5{1}(=8매)인데 imos 실부재는 0·3·4·5·6·7·8·10 으로 갈리고 같은 H(1970)에서도 3·4·7·8 이 공존한다. 전량 대조 「잉여 이동선반 1,085매」의 정체가 이것 → 규칙으로 못 맞춘다. 대조 상한선을 볼 때 이 몫(≈4%p)을 빼고 읽어라. 재현 =tools/_probe_shelf_qty.mjs. BASECP(받침)은 카탈로그 165종 전부BA_filler_1000_H080mm인데BAHEIGHT/BAINSET/BAINSERT/BATOEKICK이 전부 0 = 받침이 꺼져 있다. 「87.7% 채워짐」은 기본 CP 이름일 뿐 부재가 안 나온다.- imos DB 조회는
tools/imosdb.py공용 헬퍼로 한다(q()·dump()).pull_*.py마다 dbquery 배선을 복사하지 마라. 받아둔 것 =cp_rest_full.json(DOOR 232·KMS 490) ·drawer_full.json(DR_MAIN 926·ZONE 626·SING 806·SIZE 16,480) ·pd_full.json(kmsprof 2,062 + MPP 4종) ·hardware_rules.json(CONNSELATTR 26,251). 전부reference/imos-full/. - 몸통 프리셋 = imos
KORP_VOR(Korpus-Vorgabe). 포인이 쓰는 줄은Poin_kitchen·Poin_furniture·Poin_오픈장3종뿐(나머지는 imos 기본Corpus_*·STANDARD). 컬럼은 독일어 약어 —KORP=측판OB_B=천판UN_B=지판RUEC=뒤판MITS=칸막이KO_B=고정선반EL_B=이동선반SOCK=받침. 생성 =tools/mkpresets.py→data/presets.json. - 배리어블 분류는
IMOS.FAMILY(찬넬·서랍·도어·01몸통부자재 … 72종, 424개 중 421개 채워짐).CATEGORY는 206개뿐이라 보조. 생성 =tools/mkvars2.py→data/vars-meta.json. 🔑 PFSpncad_var에 컬럼을 안 늘렸다 — 값은 DB 가, 분류는 imos 마스터가 쥐는 읽기전용 참고열이라 파일로 나른다. - 🔴
convert.mjs를 고쳐도 PFS 화면은 안 바뀐다 —_seed_if_empty()는pncad_article이 비었을 때만 돈다. 단독(8799)은data/catalog.json을 직접 읽어 바로 반영되지만, PFS 안에서는 DB 에 든 옛 판이 계속 나온다. 재시딩 경로가 아예 없다(있는 건 백업/복원뿐,replace_all은 사용자 제작 아티클까지 지운다). → 필요한 것 = origin='imos' 행만 갱신하는 머지 시딩. 착수 전 그릴링 대상(파괴 범위·트리거 주체).
🆕 신규 (2026-08-18 밤 — 아티클 편집기 2차 · 기둥목 겹판 · 부재목록)
- 🔴 imos 는 부재 좌표를 저장하지 않는다 — anglzone/anglelem/IDBGPL/IDBGEOM 어디에도 좌표 컬럼이 없고 매 렌더 때 존트리로 재계산한다(실측 2026-08-18). 배치가 어긋난 버그는 DB 로 정답을 못 찾는다 → 실물 구법(가구전문가·KB)·imos 화면 캡처가 정본이다.
- 🔴 코너 기둥목 2본 = 15T 면대면 30T 겹판(경첩 인발하중 보강 — 실물 구법, KB 「코너대기둥목 제작깊이 68mm」·DISTFRONT=37=System32 첫홀 정합). 존트리는 68mm 포켓 좌·우 벽에 1본씩 기록해 그대로 그리면 38mm 뜬다.
schema.js snapPostPairs()= 같은 CP 각목 2본·같은 y·z·간격 60mm 이내 → 장 모서리쪽 앵커에 붙임(치수·수량 불변, 3D만). ⚠기둥목폭은 상수 아님 — 68=$기둥목폭02(BC/WC_SD 001계) · 60=$기둥목폭(DD계), 오더별 45~117 산발. ⚠예외 = BC_DD_R_001 3본 · BC*_CH_001_TK 의주방장_기둥목_0000_SCR별도 2본. - 🔴 밴드(TRAVERSE) 전후면 =
VORNE/HINTEN플래그가 정본(이름_F/_B는 관례) →cp.json bandPos. 앞밴드만 앞면 정렬(horizY분기), 뒤밴드는 종전 후면(장 외곽 깊이 기준). 깊이 폴백사슬 = fixW(실부재 최빈) → bandDepth(위치 플래그쪽 TIEFE 리터럴) → CABIN식 — fixW 없는 저빈도 밴드(_F_68mm류)가 CABIN 폴백으로 새면 장 전체깊이 판이 된다. mkcp2 의TIEFE_VORN or TIEFE_HINT병합은 HINTEN 밴드에서 오값이라 플래그 분기로 교체. ⚠LAGE=2(수직_V)·3(mixed) 밴드는 미구현 — 다룰 때 별도 규칙. - 부재목록(
▤ bElems) =schema.cpMaster()읽기전용 조회(imos EM 역할). CP 정본이 convert 생성물이라 편집은 서버 테이블 신설 후(phase2 — 필드 목록은 session-2026-08-18-article-editor2-imos-full.md §4). - ⚠ 편집기 E2E 존 경로 — BC_R_001 기둥목존은
0.0.0.0.0.0(6단). 한 단계 얕게 짚으면 빈 면이 잡혀 에러 없이 성공처럼 보인다(설치면 이동은 빈 면 가드). - ⚠ 공간 투명표시는 아티클 편집기 전용(페페 지시 26.8.18) — 메인 3D
paintZones()는 존 색을 안 입힌다(픽킹만 유지). 메인에 존 강조를 되살리지 마라. - imos 원본 전컬럼 =
reference/imos-full/zone_full.json(anglprim/anglzone/anglelem 17,015행, 재실행tools/pull_zone_full.py). 트림코드 4변 100%·힌지 10,344건 확보 — convert.mjs 소비가 다음 본작업.
🆕 신규 (2026-08-18 오후 — 좌대·멍박스·멍장 근본수정)
- 🔴
#이름LINDIV = DESCRIPTOR 조건분할이다 — 분할식이DESCRIPTORVALUES(NAME, NODENUM, CONDITIONID, LINDIV)에 있고, NODENUM 순으로 훑어 처음 참이 되는 조건의 LINDIV 을 쓴다(CONDITIONID=0= DEFAULT). 조건은 전량X <op> 숫자의 AND 이고 X = 나누는 축의 길이(실측 135조건 ·LEFTVALUE전량'X'·OPERATIONTYPE전량 0). 정본 =data/descriptors.json(54종·186분기, 생성tools/pull_descriptors.py). 종전엔 카탈로그 자식 수만큼 균등분할로 폴백해 칸 수가 스냅샷에 굳어 있었다(좌대 전량 잉여의 정체). ⚠ 소비자마다useDescriptors()를 따로 물려야 한다(app.html·verify2.mjs·verify.mjs·convert.mjs) —useCPMaster와 같은 구조. - 🔴 박스형 부자재(좌대·멍박스)는 구성원칙이 반대다 — 긴변(F/B)이 존 폭을 통으로 가로지르고 짧은변(L/R·분할재)이 그 사이에 끼운다. imos 정본 =
anglelem.STARTTRIM/ENDTRIM의'L'(장) /'S'(단). 실측(2026 인스턴스 199건 전량 · 편차 0) = 긴변W − max(inset, 0.5) 양쪽· 짧은변D − insetF − 앞뒤 두께· 길이는 넷 다 존 높이. 판정 = 네 면 CP 가 전부 CSIDE PRODCODE 12. 15T 판으로 바뀌면 짧은변도 같이 줄어 맞는다(18T_좌대 15T 2건 확인).schema.js walkBox(). - 🔑
max(inset, 0.5)가 핵심 — 18T_좌대(inset 1)는W−2, 15T_좌대(inset 0)는W−1이다. inset 이 없어도 양쪽 0.5씩 물러난다(CABINUEBERST −0.5와 같은 관행). - 🔴 세로 박스형(멍장류) = 좌·우가 박스형 CP 이고 위·아래 경계 부재가 있을 때 — 천/지판이 존 폭을 통으로(
W − max(inset,0.5)×2), 측판이 그 사이에(H − 천지판 두께 2장 − 1). 종전엔 천지판이내경 − 1(= W−37) 이라 전량 어긋났다. - 🔴 박스형·선형 부자재(PRODCODE 12·13)는 몸통 두께 스위치를 안 받는다 — 이름이 곧 자재 지정이다.
18T_문틀재실부재 486매 중 15T 는 6매뿐인데alt이력이 있다는 이유로 주방 오더에서 전량 15T 로 바뀌어15T_문틀재_VA가 0% 였다.alt유무만으로 스위치를 걸면 안 된다(뒤판각목 사고와 같은 계열). - ⚠ 박스형 부자재(12)는 F면에서도 오버레이가 아니다 — 앞판 평면(
fb)이 아니라 그 존 박스를 딱 채운다(갭 0).struct판정에서 12를 빼야 멍장 칸막이 F/B 가 존 폭 그대로 나온다. - ⚠
convert.mjs의 「분할폴백」은 LINDIV 을 통째로 버린다 — 남는 자식에 내용이 있으면 균등분할로 떨어지는데, 15T_멍장이 그 케이스라 80mm 고정칸이 78mm 균등칸으로 뭉개진다(imos 실측은 LINDIV 이 맞다). 폴백 목록은data/convert-report.json의분할폴백7건. - ⏳ 남은 원인(다음 세션) = ①텐덤 서랍 — 부재는 후판 1 + 바닥판 1(측판 없음, 금속 박스가 측판 역할). 오프셋이 박스 종류별로 갈린다(같은 W600 에
Wz−89와Wz−43이 공존) →텐덤_<브랜드>_<모델>_D<>_H<>CP 이름 파싱이 선행. 누락 459+469매. ②좌대 분할 수는 디스크립터로 76% 까지만 맞는다 — 나머지는 오더에서 손으로 고친 것(같은 W1500 에 2칸·3칸이 공존). ③멍장 칸막이 개수도 같은 성격(멍장측판개수디스크립터가 따로 있으나 루트 LINDIV 은 리터럴).
🆕 신규 (2026-08-18 낮 — 편집기를 imos Article Designer 구조로)
- 🔴 아티클 편집기는 imos Article Designer 를 그대로 베낀다(페페 지시 — "그래야 적응하고 수정 지시를 낼 수 있다"). 화면 구성 정본 =
catalog-article-center.md:341의 뷰 이름 —imosCADArticleSizeSnapInView(상단 치수 바) ·imosCADArticleDesignerSnapInTreeView(좌측 정의 트리) ·imosArticleDesignerGraphicView(중앙 그래픽). 뷰 프리셋 3D/정면/평면/측면 = imos 명령 103 1/2/3. - 🔴 트리 노드 타입 =
Ind.dll TreeEnums.TypeE(article-variables.md§12.2) —Root · Dimensions(200) · Zone(400) · Division(500~, +570 식·571 유형·572 분할재두께) · Part(600~611 Left/Right/Bottom/Top/Front/Back) · Parameters(800)/Parameter(801). 파트를 오른쪽 패널이 아니라 트리 안에 노드로 둔다 — 이게 종전 편집기가 안 읽히던 이유다. - 어휘도 imos 것을 쓴다 — 분할 유형
Division+TypeE(우리 dir V=DIV_Z 높이분할 · H=DIV_X 폭분할 · I=DIV_W 독립) · 공간 유형Zone+ZoneTypeE(NORMAL/FRONT/CARCASE/BACKPANEL/COLUMN) · 파트 유형PartDefinition+PartTypeE. - 🔴 3D 에서 부재를 집으면 트리가 따라온다 — 그러려면 부재가 자기 자리를 알아야 해서
schema.js emit(out, part, {zone, side})이 표식을 찍는다(계산에는 안 쓰인다). ⚠ 분할재(divider)는 부모가 아니라 그 뒤 자식 존에 찍는다 — emit 이 자식 walk 뒤에 불려서 부모 경로를 쓰면 어긋난다. - ⚠ 레이캐스트는 앞쪽 부재가 이긴다 — 측판을 겨눠도 도어가 앞이면 도어가 잡힌다. E2E 기대값은 「겨눈 부재」가 아니라 그 지점에서 실제로 맞는 앞쪽 부재로 잡아야 한다(안 그러면 멀쩡한 코드가 FAIL 로 뜬다).
- 🔴 선택 강조는 색을 갈아치우지 말고 발광(emissive)만 얹는다. 통째로 주황을 칠하면 공간을 골랐을 때 그 밑이 한 덩어리가 돼 형태가 사라진다(첫 판 캡처에서 드러남).
- 편집기 3D 는 도어를 기본으로 끈다 — 앞판이 덮으면 선반·칸 구성이 안 보인다(미리보기와 같은 이유). 「투시」 토글은 반투명.
- ⚠ 오버레이 헤더는 우상단 ~44px 을 비운다 — qahelper 토글이 그 자리를 덮어 「닫기」가 가려진다(devplan 전역 함정과 같은 것).
- 편집기 3D 는 자체 three 컨텍스트를 한 번만 만들어 재사용한다(열 때마다 만들면 컨텍스트가 샌다). 마우스 규약은 본 화면과 같다.
🆕 신규 (2026-08-18 오전 — 아티클 카탈로그 편집기 · 배리어블 · 묶음 백업)
- 🔴 용어 = 「배리어블」(variable). 페페 지시 2026-08-18 — 앞으로 문서·UI·코드 주석에서 변수를 배리어블로 지칭한다(상단바 버튼도
ƒ 배리어블). 「현장 변수」처럼 이미 굳은 화면 문구는 그대로 두고, 새로 쓰는 곳부터 적용한다. - 🔴 카탈로그 정본은 PFS DB 다 —
data/catalog.json은convert.mjs생성물이라 제작 아티클을 담을 수 없다(다음 재변환 때 조용히 사라진다). 테이블 3개 =pncad_article(최신본만·초안is_draft) ·pncad_var(배리어블 마스터) ·pncad_backup(묶음 판). 전부 멱등ensure_table이라 SSM DDL 스크립트가 없다(pncad_scene선례). 앱은/api/pncad/catalog를 먼저 받고 실패하면 파일로 떨어진다(단독 8799 = 읽기전용). - 🔴 배리어블 이름 PK 는 대소문자를 가려야 한다(
utf8mb4_bin). imos 실데이터에TC_R_Door_001과TC_R_DOOR_001이 둘 다 있는데 MySQL 기본 콜레이션(utf8mb4_general_ci)은 둘을 같은 키로 봐서 시딩 트랜잭션이 Duplicate entry 로 통째로 죽고/api/pncad/catalog가 전량 502 였다. 한글·영문 식별자를 PK 로 쓰는 새 테이블은 전부 이 검사를 먼저 한다. - 🔴 시딩·부트스트랩 코드에서 예외가 새면 uWSGI 워커가 죽어 nginx 가 502 를 준다(Python 500 이 아니다). 로그가
upstream prematurely closed connection뿐이라 원인이 안 보인다 → 부트스트랩은try/except로 감싸고 실패를 응답 필드(seed_error)로 실어 눈에 보이게 남긴다. 서버 로그 실물 =/home/ubuntu/pfs-log/pfs.log(EC2,python scripts/ssm_run.py로 읽는다). - 🔴
setSegCount()는 세그먼트 목록(1:$기둥목폭 MM:1) 전용이다. 분할식이 반복 템플릿(3*{1}·6{1})이면 lindiv 를 안 건드리고 자식만 늘려서, 전개(LINDIV 이 정본)가 원래 개수로 되돌린다 = 칸 수 편집이 조용히 안 먹는다. 반복 템플릿은normVar(String(n), lindiv)로 개수만 갈아끼운다(editor.js setZoneCount). 잎 존을 처음 나눌 때는 자식과 분할식을 같이 세운다(자식만 만들면 나눌 근거가 없다). - 면(부재정의)은 CP 이름 문자열 하나로 전부 파생된다 —
parseCP(name)이 role·line·rv·thk·variant 를 뽑는다. 그래서 편집 UI 는 6칸이 아니라 이름 1칸 +$변수스위치 + 인셋 셋이면 된다. - 미리보기 렌더러는 카드 썸네일과 같은 것을 쓴다(
previewURL(item, opt)). 편집기용 three 렌더러를 따로 만들면 GPU 컨텍스트가 둘이 된다. - 백업은 카탈로그 전체 한 묶음 단위(페페 확정 — 아티클별 이력·수시저장 이력은 안 쌓는다). 자동 판은 최근 50개, 이름 붙인 수동 판과
seed판은 안 지운다. 배리어블을 연달아 고칠 때 550KB 판이 수십 개로 불지 않게 5분 안의 자동 판은 갱신한다. 복원은 직전 상태를 자동 판으로 먼저 뜬 뒤 통째 교체한다. - imos 기준선(
origin='imos') 아티클은 삭제 금지(서버가 거부). 재추출 판과 비교할 기준이 무너진다 → 고치려면 복제해서 쓴다. - 🔴 마우스 규약(페페 지시) = AutoCAD 와 같다 — 가운데 휠 드래그 = 이동 · Shift+가운데 = 커서 중심 회전 · 좌클릭은 카메라를 안 건드린다(선택·장 이동 전용) · 관성 금지(
enableDamping=false). OrbitControls 는 target 만 축으로 돌기 때문에 커서 중심 회전은 컨트롤을 끄고 카메라를 직접 궤도 이동시킨다. 🔑 손 뗄 때 target 은 커서점이 아니라 「시선축 위로 투영한 점」으로 되돌린다 — 커서점을 그대로 넣으면camera.lookAt(target)이 걸려 화면이 튄다. - 자석 붙임 거리 = 90mm(45 → 2배, 페페 지시).
- ⚠
rayAt(e)는 Raycaster 를 돌려준다 —.ray는THREE.Ray라intersectObjects가 없다(intersectPlane만 있다). 객체 교차는rayAt(e).intersectObjects(...). - ⚠
place()는 배치 즉시 W/D/H 모달을 연다 → 마우스·픽킹 E2E 는 모달을 먼저 닫아야 한다(#iClose). 안 닫으면 오버레이가 캔버스를 덮어 모든 마우스 시험이 조용히 0 으로 나온다.
🆕 신규 (2026-08-18 새벽 — 코너장 근본수정 · imos 전량 대조)
- 🔴 대조는 오더 1건이 아니라 imos 2026 전량으로 한다 —
verify2.mjs+reference/imos-2026/instances.json(인스턴스 2,726 · 아티클 165 · 부재 28,709, 생성 =tools/pull_imos_2026.py). 오더변수(IDBVARVARCAT 0)와 인스턴스변수(VARCAT 1)를 안 물리면 대조가 무의미하다 — 구verify.mjs는 변수를 안 넣어 찬넬높이·기둥목폭이 카탈로그 기본값으로 굳어 있었다. 전량 기준 33.5% → 50.4%. - 🔴 앞면 부재는 내경이 아니라 「앞판 평면」 기준이다. 도어·덧방은 풀오버레이라 측판·천지판을 덮고 장 외곽까지 나온다.
schema.js walk()가 몸통 박스(box)와 앞판 평면(fb)을 나란히 내려보낸다 — 면·천지판 두께는fb를 안 깎고, 분할만 양쪽에 같이 먹인다. 실측 = 코너 덧방 폭(장W − 도어) − 213/13 편차 0, 높이(장H − 찬넬높이) − 2. - 🔴 어떤 분할이 「외곽 기준」인지는 imos
anglzone.SIZREF*가 알려준다 —SIZREFOUT1/EDG1/MID/EDG2/OUT2중 하나라도'O'(Outer)면 그 분할은 장 외곽을 다시 기준 삼는다(convert.mjs가outer:true로 옮긴다). 포인 도어 분할이 전부 이것이고, 일반 중간기둥(1:$기둥목폭 MM:1)은 전부'I'다. 이걸 모르면 코너장에서$기둥목폭02 mm:1(68) 안에 도어 400mm 가 들어가는 구조를 영영 못 푼다. ⚠ 재설정은 나누는 축에만 건다 — 다른 축까지 되돌리면 찬넬로 잘린 높이가 살아나 덧방이 찬넬만큼 길어진다. - 🔴 코너 기둥목은 존 폭이 아니라
$기둥목폭02(68) × 2본이다. 실측 = 2026 코너 인스턴스 2,971건 전량이 2본·폭 68(6,318/6,730행).TC_L은 370·600 존인데 부재는 68이라 존 폭일 수 없다. 종전post → box.w규칙이 맞아 보였던 건 우연이고, 두 번째 기둥은 0폭 존에 갇혀 부재가 통째로 사라졌다(페페 지적 「한쪽만 있고 한쪽은 안 보인다」의 정체). - 🔴 CP 파라미터 값이
$변수수식이면 숫자로 못 깎인다고 버리지 마라 — 원문을 보존해 전개 때 평가한다(schema.js pv(),mkcp2.py num()).주방가구_AS_Corner.UEBERST_V=-3-($기둥목폭02)= −71.null로 버리는 바람에 그 CP 부재가 통째로 71mm 어긋나 있었다. - 🔴 몸통 두께 스위치는 그 CP에 실측 두께 이력(
alt)이 있을 때만 먹인다. 계열이 '일반'이라고 무조건 18T 로 바꾸면뒤판각목_15T가 18T 로 부풀어 2,235매가 통째로 어긋난다(대조 원인표 1위였다). 도어(18T MDF)도 스위치 대상이 아니다. - 🔴 뒤판 높이·천지판 깊이는
RGRDE부호가 정한다 — 부호 > 0 = 홈 물림(그루브) → 그 판 두께를 뺀 채 두고, ≤ 0 = 평면 리빌 → 판 두께를 되돌려 더한다. 천/지판 깊이도 같은 부호로 갈린다(≥0 = 장 깊이, <0 = 뒤판 앞까지). 이름(_하부홈)으로 가르면 벽장·상하홈 변형에서 틀어진다. - ⚠ 밴드(TRAVERSE)는 두께 + 1mm 를 먹는다(아래 존이 그만큼 낮다). 천판(CABIN) 아래는 안 그렇다. 앞판 평면에는 안 먹인다.
- ⚠ 선형부재(CSIDE PRODCODE 12 좌대류·13 걸레받이/문틀재)는 「각목류」가 아니다 — 구성원칙이 반대(긴변이 F/B)다.
post판정에서 빼고, 칸막이로 쓰일 땐 깊이가 아니라 그 존의 폭이 부재 폭이다(18T_문틀재_VA0% → 75%). - ⚠ 기둥목 같은 CSIDE 구조재의 F면은 앞판 평면이 아니라 그 존 박스 기준이고 갭이 0이다. 오버레이인 도어·덧방만
fb+ 갭을 쓴다. - 🔑 변수 입력은 「모양 복원」으로 푼다(페페 지시 2026-08-18) — 원래 값의 모양을 기억했다가 사용자가 맨숫자를 넣으면 그 모양으로 되돌린다(
6{1}+3 →3{1}·100mm+450 →450mm). 콜론·중괄호·$가 섞이면 손대지 않고 그대로 저장한다. 정본 =lindiv.js의varKind/normVar/varHint(node lindiv.js자체검증). 이게 없으면 도어 사이즈 칸에450을 친 순간"450"= 비율 450:1 로 풀려 도어가 장 전체 폭이 된다. - 🔑 imos DB 는 미니PC에서 바로 붙는다 —
powershell -File C:/dev/nesting/dbquery.ps1 -SqlFile <utf8.sql> -OutFile <json>. ⚠ SQL 안에 한글 리터럴 금지(에러 없이 0행).anglzone은 77컬럼이고 우리가 쓰던 덤프는 13컬럼 발췌였다 — 필요하면 원본 스키마를 다시 본다(SIZREF*·LINDIVZ·DIVANGLE이 거기 있었다). - ⏳ 미해결(다음 세션 1순위): ①좌대·멍장·멍박스 =
#좌대_SCR같은 DESCRIPTOR 조건분할 미구현(DESCRIPTORVALUES에1/1:1/1:1:1/1:1:1:1+CONDITIONID로 들어 있다) ②텐덤 서랍 산출식(브랜드별 gap/margin/depth 실측표는 확보) ③커스텀 높이 오더의 선반 매수 — imos 가IDBVAR밖에서 정한다.
🆕 신규 (2026-08-17 밤 — 계산 버그 3종 근본원인)
- 🔴 imos 변수값은 단위째로 보관한다.
imos-vars.json의WERT가"100mm"인데vars-default.json이 숫자100으로 깎아 넣으면, LINDIV$BC_R_Door_001:1이 고정 100mm 가 아니라 비율 100:1 로 풀린다(도어 100 → 960).lindiv.js는 문자열"100mm"를 solo 변수 재파싱으로 이미 옳게 처리하므로 데이터만 옳으면 된다. 화면에서 변수를 고칠 때도 단위를 되붙여야 한다(putVar). 실측 = 단위 깎인 변수 25개. - 🔴 수량이 바뀌면 자식을 LINDIV 에 맞춘다 — 던지지 마라.
$W_선반수{1}·$문틀재Q01{1}같은 반복수 변수를 바꾸면 세그먼트 수가 카탈로그 children 과 달라진다. 예외를 던지면 그 장이 갱신을 통째로 잃어 「최초 수량에서만 정상」이 된다. SPEC-SCHEMA §5(LINDIV 이 정본)대로 부족하면 마지막 자식 복제, 남으면 자른다. - 🔴 칸 수를 바꿀 때 LINDIV 을 통째로 다시 쓰지 마라.
'1:1:1'로 덮으면 찬넬·밴드 같은 고정칸(mm)이 비율칸이 된다 →setSegCount()(lindiv.js) 로 대상 칸만 늘리고 줄이고 고정칸 원문은 건드리지 않는다. - 🔴 각목류(CSIDE)는 장 깊이가 아니라 그 존의 폭이 부재 폭이다. 기둥목 = imos 실부재
670×68(68 =$기둥목폭02로 갈린 존 폭). PRODCODE 01(측판)만 깊이를 쓴다. 안 그러면 기둥목이 측판처럼 길게 나온다. - ⚠ 존은 음수가 될 수 없다. 고정 세그먼트 합이 부모를 넘으면(실데이터에 있다) 앞에서부터 채우고 나머지 칸은 0. 부재도
l·w·t > 0이 아니면 안 만든다 — 안 막으면-47짜리 부재가 규격표·네스팅까지 나간다. - 🔑 존 편집 UI 는 「선택된 잎」과 「그 부모」를 같이 봐야 한다. 픽킹 대상은 잎 존뿐인데 칸 수·서랍 단수는 자식을 가진 부모의 값이라, 부모를 안 물리면 그 UI 가 영영 안 뜬다(2026-08-17 E2E 로 발견 — 코드는 처음부터 있었는데 도달 불가였다).
- ⚠ 치수를 치수선으로 그리면 라벨의 W×D×H 는 빼라 — 숫자가 두 벌로 겹쳐 읽을 수가 없다.
🆕 신규 (2026-08-17 밤 — 네스팅 편입 · 배포 확인)
- 🔴 PFS 배포 대기를 「HTTP 200 + 본문에 특정 문자열」로 판정하지 마라 — 비로그인은 200 + 로그인 HTML 이다. 26.8.17 네스팅 배포에서 폴링 루프 10회가 전부 「대기」로 나왔지만 그 시점에 이미 배포 완료였다. 판정은 로그인(
POST /api/auth/login={user_id, password}) 후 본문 대조 — 로컬 파일과 길이·해시 비교가 확실하다(서버는 LF, 로컬 체크아웃은 CRLF 라 바이트 수는 다르다 → 텍스트로 디코드해 비교). - ⚠
gh run list로 pfs Actions 를 못 본다 — PAT 가 프라이빗 리포 Actions 권한이 없어404. 배포 확인은 GH 가 아니라 서버 직접 조회로 한다. - 🔴 PFS 화면을 새로 붙이면 「라우트」와 「사이드메뉴 행」이 별개 작업이다 — 라우트만 배포하면 URL 직접입력으로만 열린다.
menu_catalogINSERT 는 SSM 스크립트를 직접 돌려야 반영된다(코드 배포에 안 딸려간다). 스크립트만 커밋하고 끝내면 조용히 누락된다. - 🔑 네스팅 부재 합산은 저장 시점
nest_rows를 서버가 더한다 — 네스팅 페이지가 씬 전개 엔진(schema.js·cp.json·three.js)을 다시 들이지 않아도 된다. 대신 PNCAD 에서 저장을 안 한 작업본은 부재가 0 이라skipped로 빠진다.
🆕 신규 (2026-08-17 밤 — 작업본 서버 저장)
- 🔴 작업본(씬) 저장소 = PFS DB
pncad_scene1테이블(페페 택1 2026-08-17). 저장 단위 = 현장 + 작업명 여러 벌(한 현장에 타입별로 나란히). API =/api/pncad/scene(목록·새저장) ·/api/pncad/scene/<no>(단건·덮어쓰기·소프트삭제). 목록 SELECT 에서data를 반드시 뺀다 — 씬 JSON 통짜라 목록에 실으면 응답이 수 MB 된다. - 🔑 PFS 새 테이블은 SSM DDL 스크립트 없이 만든다 —
company_vacation이 쓰는 멱등ensure_table()패턴(dao 안CREATE TABLE IF NOT EXISTS+ 전역 latch)을 그대로 따르면 앱서버가 첫 요청에 만든다. 운영 배포 시 DB 작업이 코드 배포 하나로 끝난다. - 🔴
MySQLDatabase는 쿼리 1건마다 새 커넥션을 연다 — insert 후SELECT LAST_INSERT_ID()를 따로 부르면 다른 세션이라 0 이 나온다. 신규 PK 가 필요하면 반드시MySQLUtil.execute_id()(같은 커서의lastrowid). - ⚠
MySQLUtil.fetchall(sql, params)는 params 가 있으면%이스케이프를 탄다 — SQL 안DATE_FORMAT(..., '%Y-%m-%d')는%%Y-%%m-%%d로 써야 한다. params 가 빈 리스트면cursor.execute(sql)라%하나로 둔다(같은 파일 안에서 규칙이 갈린다). - 🔴
loadScene같은 "불러온 뒤 clean 표시"는 재렌더 뒤에 찍어라 —rebuild()안에서save()가 불려 dirty 로 되돌린다. E2E ⑤b 로 실제로 잡혔다(테스트 1차 FAIL). - 저장 상태 표시는 색으로만 구분(저장됨 초록 · 변경됨 주황 · 미저장 회색). 페페 UI 취향 = 굵기로 강조하지 않는다.
- localStorage 는 크래시 복구용 자동저장으로만 남긴다(
poincad2.scene= 씬 ·pncad.sto= 현재 작업본 번호·이름·dirty). 진짜 저장은 서버. JSON 파일 저장·열기는 백업·외부 전달용으로 유지(페페 지시) — 파일에서 연 씬은STO.no=null로 끊어 다음 「저장」이 새 작업본을 만들게 한다. - PFS RestResponse
fail()은 반환값이 없다 —RestResponse().fail(e).result로 체이닝하면NoneType이다.response.fail(e)후response.result를 따로 낸다.
🆕 신규 (2026-08-17 밤 — PFS 편입 · 운영배포 완료)
- 🔴 PNCAD 운영 실행 경로 = PFS
생산 > PNCAD(첫 항목, 새 탭) →https://pfs.poin.co.kr/production/pncad/. devplan 8799 는 개발본이다. URL 끝 슬래시가 규칙 —app.html이./schema.js·data/*.json을 상대경로로 부르므로/production/pncad(슬래시 없음)면/production/schema.js로 새어 404 난다. 라우트도/production/pncad/로 걸어 Flask 가 자동 리다이렉트하게 둔다. - 🔴 PNCAD 자산은
pfs/web/pncad/에 둔다 —web/static/밑은 금지. Flask 정적 폴더는 로그인 없이 열려서 현장목록 1,988건·카탈로그·CP 마스터가 그대로 샌다. 서빙 =send_from_directory+@login_required(자산 라우트도 같이 건다). 검증 = 비로그인curl로 페이지·schema.js·data/fields.json셋 다 로그인 페이지가 나오고 데이터 0줄인지 본다. ⚠ PFSlogin_required는 302 가 아니라 200 으로 로그인 HTML 을 반환한다 — 상태코드만 보고 "인증이 안 걸렸다"고 오진하지 마라(이번에 한 번 오진했다). 본문을 봐야 한다. - 🔴 원본은
C:\dev\pncad, PFS 는 실행 사본이다. 동기화 =PYTHONUTF8=1 python tools/sync_to_pfs.py(어긋남 검사 =--check). S7 대조(verify.mjs)·변환(convert.mjs)이schema.js를 import 해서 pncad 리포에서 계속 도니까 리포를 합치지 않았다. PNCAD 를 고치면 sync → pfs 커밋까지가 한 작업이다. - PFS 안에서는 현장목록을
/api/field/라이브로 쓴다(같은 오리진 + 세션 쿠키 그대로). 단독 실행(8799)이면 401/404 라 스냅샷data/fields.json으로 자동 폴백. 이걸로 "스냅샷 갱신 잊어버림" 문제가 사라졌다 —tools/fetch_fields.py는 이제 단독 실행용 백업일 뿐이다. - app.html 은 Jinja 를 태우지 않는다 —
render_template이 아니라 파일 그대로 내보낸다(JS 템플릿 문법 보호). menu_catalog새 메뉴에 권한행을 만들 필요 없다. 자사(company_type='1')는 명시 row 가 없으면 신규 메뉴 자동 허용이고, 업체 계정은 기본 차단이다. OMC 처럼 특정 업체에 열어줄 때만company_permission행을 넣는다.- 생산 그룹 순서:
production(450) >production_pncad445 >production_sticker(451). 첫 항목으로 넣을 땐 기존 행을 재번호하지 말고 더 작은 값을 쓴다. - 사이드바 새 탭은
base.html한 줄 조건({% if child.url_path == '/production/pncad/' %} target="_blank"{% elif … %}).menu_catalog에 target 컬럼이 없어서 그렇다.startswith를 쓰면url_path가 None 인 행에서 터지니 정확일치로 건다. - EC2 에서 운영 DB 를 만질 땐
python scripts/ssm_run.py <스크립트>를 쓴다(base64 → /tmp → python3).scripts/_*는 gitignore 라 git 으로 안 올라간다 — 그래서 SSM 러너가 표준 경로다. - deploy-live 선별 포워드 전에
git diff origin/deploy-live origin/master -- <파일들>로 실제 딸려갈 내용을 본다. 커밋 목록(log deploy-live..master)은 과거 선별 포워드분이 영원히 "미머지"로 남아 부풀려 보인다 — 판정 근거는 diff다(이번엔 딸려간 것 0 확인).
🆕 신규 (2026-08-17 밤 — 리포 이전 · PFS 편입 착수)
- 🔴 리포 =
C:\dev\pncad(구C:\dev\poincad2, 2026-08-17 이전). URL도/pncad/app.html로 바뀌었다. devplanapp.pyMOUNTS를 고치면 서버 재시작이 필요하다(pythonw 상주라 terminate 후 재기동). 이전 검증 =find . -type f | sort스냅샷 393개 전후 대조 일치. - 폴더 이전 시
mv: Device or resource busy는 코디 자신의 셸이 범인이다 — Bash 도구 세션 CWD가 그 폴더면 잠긴다.psutil로pr.cwd()스캔해 확인하고, 셸을cd /c/dev로 빼고 다시 시도하면 풀린다(별 조치 필요 없음). - 🔴 현장 선택 모달 = PFS 견적서 팝업(
web/templates/popup/field_popup.html) 양식이 정본이다(페페 지시 2026-08-17): 상차일 from~to 범위 + 검색 → 현장명·유형·상차일 3컬럼 표(상차일 내림차순) → 행 클릭 = 선택표시 · 「선택」 = 확정 · 「취소」. 앞으로 현장명 선택창은 전부 이 컨셉으로 만든다. - ⚠ GitHub 보존은 막혀 있다 — 로컬 커밋은 됐지만(
164ce8a·7c8240b) 새 리포 생성이 안 된다: SSH 인증(git@github.com)은 통하는데 wincred에 저장된 PAT이 만료(401 Bad credentials)라 API로 리포를 못 만든다. 경로 = ①페페가 GitHub에서delix0731/pncad빈 리포 생성 후git remote add origin git@github.com:delix0731/pncad.git && git push -u origin master②또는 PAT 재발급(로그인 자격증명 작업 = 텔레그램 승인 항목). - PFS 사이드바는 DB 주도다(
sidebar_menuscontext_processor +menu_catalog테이블) — 코드만 배포해도 메뉴가 안 생긴다. 메뉴 추가 =menu_catalog행 + 권한 행 insert가 필요하고, RDS는 사무실/미니PC에서 직접 안 열린다 → 리포 관례대로scripts/_*_prod.py를 만들어 EC2에서 앱 컨텍스트로 실행해야 한다(permission_dao.company_field_menu_key계열이 선례).
🆕 신규 (2026-08-17 저녁 — 앱 UI 2차 · PFS 현장 연동)
- 🔴 PFS 현장목록은 스냅샷으로 쓴다 —
python tools/fetch_fields.py→data/fields.json(1,988건). 라이브 호출은 3중으로 막힌다: ①/api/field/가login_required(401) ②브라우저는 CORS ③RDSpoin-dbinstance...:3306은 사무실/미니PC에서 타임아웃(SG 미개방). EC2 SSM으로 들어가도 앱이MYSQL_*를 프로세스 env에 안 갖고 있어 자격증명 경로가 없다. 결론 = 코디가 PFS에 로그인(C:\dev\.secrets\pfs_login.env)해 GET으로 떠서 정적 JSON으로 둔다. - PFS
RestResponse는 성공 payload를message키에 담는다(data가 아니다).{"code":"0000","message":[...행...]}— 파서를data로만 짜면 "형식 모르겠다"로 죽는다. - 현장 선택 시
field_no를 씬에 같이 박는다(scene.fieldNo) — imosPROADMIN.ARTICLENO= PFSfield.field_no가 유일한 다리(SPEC-BOM §4). 이름만 저장하면 나중에 오더를 못 붙인다. - 🔴 벽이 기준면이다 — 가구 뒤판이 벽(three z=0)에 붙고 앞으로 나온다. 인스턴스마다 깊이가 달라서 three z 오프셋 = 그 인스턴스의 D. 앞면 정렬(z=0에 앞판)로 두면 깊이 다른 장들이 벽에서 들쭉날쭉해진다.
- 드래그 이동은 기본 가로(x)만이다. 원근 평면에서 화면 가로로 밀면 평면상 y(높이)도 딸려 올라가 장이 공중에 뜬다 → 기본은 x만 반영하고 Shift일 때만 z까지 옮긴다. 자석 = 이웃 장의 좌/우 모서리·상/하면 45mm 안쪽이면 정확히 붙임.
- ⚠ E2E에서 3D 픽킹/드래그를 합성 이벤트로 테스트할 땐 카메라가 돌아간다 — 드래그가 안 잡히면 OrbitControls가 직전 합성 이벤트로 카메라를 회전시켜 좌표가 어긋난 것이다(앱 버그로 오진했다). 매 시행마다
rebuild()→fit()→ 좌표 재계산 → 이벤트 순서를 지키고,PC2.hitAt(x,y)로 그 점이 정말 그 장을 맞는지 먼저 확인한다. - 카메라 fit 거리는 화각에서 역산한다 —
d = r / sin(min(fovV, fovH)/2). 고정 배율(maxSize × 2.6)로 잡으면 입면에서 벽(4200×2400)이 잘린다. 썸네일도 같은 공식.
🆕 신규 (2026-08-17 — S3~S6 앱 구축 + S7 대조)
- 🔴 앱 본체 =
C:\dev\poincad2\app.html(스키마 v2 ·schema.jsimport).mockup.html은 스키마 v1 시안이라 손대지 않고 보존한다. 실행 = http://100.108.234.45:8799/poincad2/app.html (devplanMOUNTS가 리포를 그대로 서빙 — 사본 만들지 말 것). 허브 카드site도 이걸로 교체했다. - 🔴 부재의
box(3D 위치)는size에서 만든다. S2까지box가 존 박스 복사본이라 선반 n매가 전부 바닥에 겹치고 서랍 5매가 한 점에 뭉쳤다. 렌더러를 고치지 말고schema.js에서 고친다(= 위치의 단일 출처). 헬퍼 =frontBox(도어·프론트) ·horizY(수평재는 뒤쪽 기준 정렬, 천/지판만 장 깊이 기준). - 🔴 뒤판 높이는 지판만 빼고 천판은 안 뺀다(실측): 벽장 H600 →
588 = (600−15)+5−2· 하부장 H750 →738 = 735+5−2· B2D H750 → 738. 내경(천·지 둘 다 뺀 값)으로 계산하면 상부장에서 15mm씩 짧아진다. - 🔴
rgrde(홈 물림)는 CABBACK 클래스에만 쓴다. 뒤판각목처럼 B면에 붙은 CSIDE/CABIN 부재는 수평재 규칙(내경 −1) 이다 — 벽장 W500 뒤판각목 469 = 470−1(실측 17매). 클래스를 안 보면 1mm씩 계속 틀린다. - 🔴 imos 오더변수는 CP 교체 스위치다. 존트리 CP가
ref:"$B_밴드_F"를 쥐고 있으면 스코프 변수값(CP 이름)으로 갈아끼워야 한다. 카탈로그 스냅샷 CP(밴드_0000_SCR_Front)를 그대로 쓰면 밴드가 폭 60이 아니라 풀깊이 539로 나온다. - 🔴 CP 이름에
_0000_이 있고 실부재 0건이면 부재를 안 뽑는다(imos 기본값 placeholder).밴드_0000_SCR_Front/Back이 카탈로그에서 30번 참조되는데 imos는 부재를 만든 적이 없다(두께·자재 공란). 조용히 버리지 말고out.skipped에 남긴다. - 몸통 두께 스위치는 계열별로 따로 먹는다 —
bodyThk.주방/bodyThk.일반. CP 이름에 박힌 두께(_15T)도 스위치를 받되 15/18(PB 계열)만 바꾼다. 3T 뒤판·4.5T·19T 도어마감까지 바꾸면 안 된다. imos도 오더변수바디자재_주방가구/_일반가구로 계열별로 건다. - imos는 서랍 측판·전후판을
15T 서랍재(PRODCODE 08)로 부르고, 서랍 바닥판은뒤판(06, 3T HDF)으로 부른다. CP 마스터 키서랍바닥판의role이뒤판인 게 오류로 보이지만 실부재 517건 기준이라 그게 맞다. node verify.mjs는 치수만 비교한다(이름·CP 무관). 원인 추적은ALL=1 node verify.mjs— 부재별우리 ↔ imos이름을 붙여 원인표를 찍는다. 짝짓기는 최근접 치수라 라벨이 엇갈릴 수 있다(천판↔지판으로 붙는다) → 원인 판정은 반드시gold-parts.json원본을 같이 본다.- 박스형 부자재(멍장·좌대)는 구성원칙이 반대다 — imos는 천/지판 풀폭(W1000 → 998) + 측판이 사이에 끼임(H700 → 669). 우리는 "측판 풀높이"로 고정돼 있어 이 군은 전부 틀린다. CP
UEBERST로는 판별이 안 된다(같은 −0.5) → 존별 구성원칙 옵션이 필요하다. - three.js 좌표 변환 = schema(
x폭 ·y깊이 앞→뒤 ·z높이) → three(y상):mesh.position = (x + w/2, z + h/2, −(y + d/2)). 앞면이 +z라 도어가 카메라 쪽에 선다. - 썸네일 카메라는 바운딩 스피어에서 역산한다 —
d = r / sin(fov/2) × 1.08. 최대 변 기준으로 잡으면 키큰장·상부장이 잘린다(첫 시도에서 잘렸다). - 존 픽킹은 앞면을 맞춘다 — 존 중심을 향해 레이를 쏘면 앞에 겹친 다른 존(서랍 사이 30mm 칸 등)이 먼저 맞는다. E2E에서 존을 고를 땐
mesh.position.z + depth/2 − 2(앞면)로 좌표를 잡아야 의도한 존이 선택된다.
🆕 신규 (2026-08-17 — S2 변환 + 몸통규격표 RDL 해독)
- ✅ 찬넬(CH) 실물 치수 확정(페페) = 깊이 37 × 높이 70 의 빈 공간을 만들고 그 공간을
19mm 도어마감 동일 자재로 메운다. 즉 찬넬은 구매 프로파일이 아니라 부재를 뽑는다 — 자재가 도어 계열(PRODCODE=21)이라 몸통규격표에서 빠져 imos 덤프에 부재 이력이 없었던 것. 변수$CH깊이=37 ·$CH높이=70과 정확히 일치. 현장별 전역변수 수정이 잦다. - ✅ 기둥목 폭(페페) = 일반 더블도어 장 45 기본(현장 사양에 따라 56·60 변동 = 변수
$기둥목폭) / 코너장 68 × 수직 2개(경첩 고정 기둥, 힘 받음 =$기둥목폭02). 가구 KB의 45와 imos 실측 68이 충돌해 보였으나 둘 다 맞다 — 장 종류가 다르다. -
가구 KB는 imos CP 필드 수준 값을 갖고 있지 않다(KB는 업계 교과서·카톡마이닝 실무값 위주). 치수 산출은 imos CP 실측이 1차 소스, KB는 교차검증·상충 발견용으로 쓴다(가구전문가 에이전트 판정 2026-08-17). KB-07의 일반값(뒤판 6~9mm PB/MDF, 홈파기 9mm)은 포인 실제(3T HDF, 물림 4.5×2)와 축 자체가 달라 그대로 쓰면 안 된다.
-
🔴 부재 치수를 추측하지 마라 — imos CP 테이블이 전부 갖고 있다(페페 지시 2026-08-17: "치수 추측에서 시간을 많이 뺏기고 있다").
CABIN.UEBERST_R/L/V/H(수평재 4방향 돌출) ·CABBACK.RGRDEL/R/T/B(뒤판 4방향 여유) +DISTLR(뒤판이 장 뒤에서 들어온 거리) ·CSIDE.DISTFRONT/BACK·TRAVERSE(밴드). 실측 검증 = 지판 569=570−1(UEB −0.5씩) · 이동선반 567=570−3($AS_LR여백=−1.5) · 뒤판 폭 579=570+4.5×2 · 뒤판 높이 738=735+5−2 · 밴드 폭 60(변수밴드폭). 이 이식만으로 실오더 대조 일치율이 16.5%→35.0%로 뛰었다. - CP 파라미터 값도
$변수다 —$AS_LR여백(−1.5)$찬넬깊이(−($CH깊이+1))$기둥목폭02(68)$밴드폭(60). 그래서 변수표는 전역 마스터 486행 전량을 쓴다. 아티클 존트리가 참조하는 71개만 뽑으면 CP 쪽이 빈다(첫 시도에서 그랬다). - CP 마스터는 CP 테이블 전량(1,672종) 기준으로 만든다. 2026 실부재(IDBGPL)에서 뽑은 322종만으론 부족하다 — 존트리가 2026 미사용 CP(
주방가구_AS_Rv160)도 참조한다. 자재·두께·PRODCODE는 실부재에서, 구성원칙 파라미터는 CP 테이블에서 합친다. THK가 빈CABIN행은 껍데기다. 밴드(주방가구_밴드_15T_F)가 CABIN에도 있지만 THK·UEBERST가 전부 비어 있고 진짜 주인은TRAVERSE다. 같은 이름이 양쪽에 있으면 TRAVERSE 우선.- 🔴 부재↔아티클 조인 =
IDBGRPS.HIGHARTID→IDBINFO.ID(몸통규격표 RDL 정본).PARTPOSSTR문자열 앞자리 파싱은 임시방편이다. - 🔴 imos↔PFS 실연동 다리 =
PROADMIN.ARTICLENO= PFSfield.field_no(몸통규격표 RDL의i_orderInfo가OPENQUERY(pfs, …)로 실사용 중). 1차 조사가 지목한PROADMIN.pfs_field_no는 2026년 사용 0건이 맞고, 실제로 도는 건ARTICLENO다. PRODCODE코드표(2026 실오더 40건 실측) =01측판02천판03지판04고정선반05이동선반06뒤판07뒤판각목08서랍재 /11속대류12좌대류13걸레받이·문틀재14경판·몸통재도어 /21도어33.1EP(제외). 메인 규격표 =CUT_FLAG=1 AND PRODCODE≤10 AND POSSTR≠999, 11~14는 별도 섹션. 상세 =poincad2/SPEC-BOM.md.- 규격표 치수 칸은
FWIDTH*FLENG(폭 먼저)이고,IDBGPL.ID_TEXT='PART_V'인 부재만 뒤집어FLENG*FWIDTH. 소수는FLOOR버림(반올림 아님). PART_V = 세로 부재(측판·뒤판각목 등 48종). BINDATA의 RDL blob은 base64로 뽑는다 —CAST(BINCONTENT AS VARCHAR(MAX))로 받으면 한글이 깨진다(cp949 변환).SELECT (SELECT BINCONTENT FROM … FOR XML PATH(''), BINARY BASE64)후<BINCONTENT>태그 안쪽만 디코드.- imos는 재단(CLENG)이 완성(FLENG)보다 크다(실측 420→428.8). "재단 = 완성 − 엣지"가 아니다 → 나중에
cut()을 켤 때 방향을 반대로 잡지 마라. - 고아 존은 "빈 존"일 때만 자른다. 검증 결과 잘라낸 76개 중 70개(92%)가 완전히 빈 존이었지만 6건은 내용(면·서랍)이 있었다 → 내용 있는 자식이 남으면 자르지 말고 lindiv을
_lindiv_raw로 보존한 뒤 균등분할 폴백(convert.mjs 실장, 7건 발생).
🆕 신규 (2026-08-17 — S1 스키마 v2 확정)
- 🔴 imos
anglzone에는 고아 존 행이 남는다 — LINDIV이 정본이고 children은 그 결과다. 실측: 자식 있는 존 3,185개 중 세그먼트 수와 자식 수가 정확히 일치 1,430 / 불일치 198(12.2%), 그리고 불일치는 항상 자식이 더 많다(diff ≥ 0,top/bot/divider개수와 무상관).1:1:1→1:1로 고쳤을 때 세 번째 존 행이 안 지워진 흔적이다. → 존 자식 수를 imos 행 수로 믿지 마라. S2 변환기는 세그먼트 수만큼만 가져오고 나머지는 버리고 로그에 남긴다.schema.js:validate()가 불일치를 에러로 잡는다.LINDIV1이 빈 문자열인데 자식이 있는 존 184건 = 자식 수만큼 균등분할로 읽는다. - CP는 마스터 목록이 아니라 (파트 × 자재 × Rv) 3축 조합이다(페페 확인 2026-08-17: "이동선반·고정선반은 계산식이 단순한데 imos에서 세부지정이 오래 걸려 필요한 수량만큼 일일이 만들었다"). 실측 뒷받침 = 덤프 CP 756종 중 Rv 붙은 270종이 Rv를 숫자 파라미터로 빼면 124종으로 접힌다(
일반가구_AS_Rv#24 ·주방가구_AS_Rv#21 ·일반가구_칸막이_Rv#17 ·일반가구_FS_Rv#16). → CP를 그대로 베끼지 말고 3축으로 풀되_imos원문은 항상 보존(S7 대조·역변환).CP_*로 시작하는 imos 순수 CP만raw:true로 통째 보존. - 도어 판정은 CP 원문명으로 한다 — role만 보면 놓친다.
밑장_SDO_noHandle은 3축 분해하면role="밑장"이라/도어|SDO|DDO|FDO/에 안 걸린다(찬넬 밴드 도어를 통째로 놓쳤던 실패). 판정식 =_imos ?? role. 도어 두께는 몸통 스위치와 무관하게 18T MDF 고정(imos 자재명 안 믿음, 기존 규칙과 동일). DIVDIR축 해석 = V 높이축(선반) · H 폭축(칸막이) · I 레이어 · A 서브아티클. 근거 =B_SD루트1:($찬넬높이)mm(찬넬 = 높이 방향)가DIVDIR='V'+(V,이동선반)418 ·(V,고정선반)252 ·(H,칸막이)231 ·(H,측판)62. ⚠(H,이동선반)124 ·(H,고정선반)75 = 199건은 이 해석과 어긋난다 → S2에서 실부재 치수로 확정하기 전엔 단정 금지.DIVDIR='A'(25행) = 서브아티클 삽입이다.DIVIDER컬럼에 존 분할부재가 아니라 아티클명이 들어간다(BC_1DR_1SD,$DM_SUBARTICLE,Plant_05). 대부분 imos 데모라 1차 구현 대상 아님 —dir:"A"자리만 두고 넘어간다.LINDIV2는 5행뿐이라 모델링 안 한다(원문만 보존).- 부재 치수는 자재 두께로만 계산한다 — 엣지는 치수에 안 닿는다(페페 2026-08-17). 부재는
size한 세트만 갖고, 재단치수는 필드가 아니라cut(part)함수로 만든다(현재 항등). 엣지는 변 4개 레코드로 따로 붙여 밴딩 길이 산출·도면 표기에만 쓴다. 보정을 켤 일이 생기면cut()한 군데만 고친다.
🆕 신규 (2026-08-17 — 카탈로그 이식 실측)
- 🔴
DIVDIR='I'= 겹침 레이어 복제 (2차 조사 "의미 미확정" 해소). 존 공간을 N개 독립 레이어로 복제하고 각 레이어가 따로 재분할된다. 순차 분할이 아니다. 실증:신발장_DD루트I 3*{1}→0.0 뒤판각목/0.1 선반/0.2 도어= 깊이 방향으로 겹친 3레이어.W_SD→기둥목/몸통보조목/선반. LINDIV가2*{1}(313)·3*{1}(136) 두 값뿐인 것도 이것으로 설명됨(레이어 2개 또는 3개). ⚠ 현mockup.html존 모델은 V/H 순차분할만 지원 → I를 쓰는 아티클 93종·1,472배치(53%)를 못 읽는다. 존 모델에 레이어 개념 추가가 이식 선결조건이다. - 면 개수를 셀 땐
anglelem.FUNCT='V'(가상면 931행)를 반드시 뺀다. 가상면은 경계만 정의하고 부재를 안 만든다 → 안 빼면 측판·도어 개수가 부풀려져 골격 판정이 틀어진다(코디 1차 시그니처가 그랬음). - 코드값 의미는 라이브 DB가 아니라 2차 조사본이 정본(라이브 수치는 100% 일치 확인).
DIVTYPEA=이동선반·F=고정선반·P=칸막이·S=측판·C=뒤판·B=지판·T=천판·V=없음(잎존 포함) →element-manager.md§DIVTYPE 9종.PARTTYPES=측판(CSIDE)·H=수평재(CABIN)·B=뒤판(CABBACK)·D=도어(DOOR),ELEMID0=front/1=left/2=back/3=right.anglprim.CONSTRUCTIONTYPE은 1,040행 전부 0 = 미사용. -
라이브 덤프는
C:\dev\poincad2\reference\catalog-2026-08-17\에 보존(zones 10,604 / elems 5,371 / prims 1,040 / catalog 233 + 시그니처·타입분해 스크립트). 같은 쿼리 재실행 금지 — 파일에서 읽는다. -
도어 자재 = MDF가 정답. imos
IDBGPL.MATNAME에18T MDF/하부도어자재가 섞여 있는 건 페페가 imos 기본값을 안 고치고 쓴 흔적일 뿐이다(2026-08-17 페페 확인). poincad2는 도어=MDF로 판정하고 imos 자재명을 그대로 믿지 않는다. - 도어 갭(상하좌우 빼기)은
DOOR테이블 68컬럼에 이미 파라미터로 있다 —DISTUP/DISTDN(상/하)DISTC(양문 중앙)DISTJ(조인트)FRONTDIST/FRONTDISTR(좌/우)NOD(도어 매수)DOORTYPE(1=오버레이·2=인셋)LR_SYM/LRSAMESIZE. 포인 도어 CP 232행. 값은 음수=빼기(포인 표준: 상하좌우 −3, 전면 좌우 −2, 플랩상부도어_FDO만 중앙 +16·상하 0). - 도어 갭의 기준면은 아티클이 아니라 "도어가 덮는 존"이다. 실부재 역산에서
하부도어_SDO_서랍마이다가 아티클H 대비 +394/+414로 튀는 건 그 도어가 장 전체가 아니라 서랍칸 존을 덮기 때문. 갭 계산을 아티클 W/H 기준으로 짜면 서랍·분할 도어에서 전부 틀어진다. -
장 종류별 도어 높이 규칙이 실제로 다르다(실측 2026) — 하부장
도어L = 장H − 34(지배값) / 상부장장H + 17(도어가 더 길다) / 키큰장−37과+3양분(462 vs 430건) / 플랩−37. 폭은 단문장W − 4, 양문(장W − 8) / 2로 일관. 페페 지적대로 상하부장·키큰장·배치별로 변수가 갈린다 — 단일 상수로 못 박는다. -
도어/몸통 경계 =
IDBGPL.PRODCODE.21= 도어라인(2026 부재 3,008 중 3,004가 도어 CP, 자재18T MDF·하부도어자재),01~20= 몸통·부자재. 두께로 MDF/PB를 판정하면 안 된다 — 도어는 18T라PB18로 오분류된다.C:\dev\nesting몸통재 탭이PRODCODE>=N'01' AND PRODCODE<=N'20'로 이미 도어를 걸러내고 있다(그래서 두께 CASE가 안전한 것). poincad2 산출도 같은 경계를 코드로 들고 간다. - 아티클 배치 인스턴스 =
IDBINFO.TYPE=2, 아티클명은CPID,WIDTH/DEPTH/HEIGHT가 실배치 치수.GROUPNAME은Article Designer Group같은 고정 라벨이라 아티클명이 아니다.IDBINFO.ORDERID는 int가 아니라 오더NAME(nvarchar30) —PROADMIN.ID로 조인하면 형변환 에러. dbquery.ps1SQL 입력에 한글 리터럴 금지(재확인).IN (N'붙박이장_DD_001', …)은 에러 없이 0행을 조용히 반환한다. 한글은 출력에만 쓰고 필터는 ASCII 키 또는 클라이언트측에서 건다.KT_FOLDER폴더경로는 재귀 CTE로 뽑는다(PARENT_ID=0앵커 →DIR_ID조인). 리프(TYPE=46)의NAME이 곧 아티클명이라articles와 바로 붙는다.
🆕 신규 (2026-07-27 — imos 실사조사에서 확정)
- imos 운영 DB(
poin)는 읽기 전용으로 다룬다. 조사·추출은 SELECT만. 쓰기가 필요하면 스냅샷 사본을 떠서 오프라인으로 한다(운영 부하·잠금 회피). GetActiveObject('AutoCAD.Application')금지. 이 PC에선 페페가 실작업 중인 AutoCAD 2016에 붙는다. iX CAD를 잡으려는 의도였다면 100% 오작동이다. iX CAD는 COM Application을 아예 노출하지 않으므로 CAD 자동화 시도 자체를 하지 않는다 — imos 연동은 SQL 읽기가 유일한 실용 경로.- iX CAD를 띄워 조사할 땐 ①새 인스턴스 ②초기화 완료(MainWindowTitle 생성) 확인 후 ③
SetWindowPos(-32000,-32000)로 파킹 ④PrintWindow(hwnd, dc, 2)캡처 순서를 지킨다. 초기화 전에ShowWindow(SW_HIDE)를 걸면 기동이 멈춘다(실측). 페페의 기존 인스턴스(PID)는 절대 건드리지 않는다. - imos 기능 조사는 스크린샷보다
imostemplate.cuix+imos.msg파싱이 우선. CUIX가 리본 전체(탭·패널·버튼·명령·설명)를 담고 있어 더 완전하다. WPF 리본은 UI Automation에 TabItem을 노출하지 않고 PostMessage 합성클릭도 무시하므로 탭 전환 캡처에 시간 쓰지 말 것. - .NET 어셈블리 조사는
ReflectionOnlyLoadFrom+ReflectionOnlyAssemblyResolve핸들러를 반드시 같이 건다. 핸들러 없이GetTypes()를 부르면 의존성 해석 실패로 타입 0개가 조용히 반환돼 "공개 API 없음"으로 오판한다(실측: 핸들러 추가 후imos.Data.dll0 → 1,216 public 타입). - 규격표 집계키 =
(부재명, CP, 자재, 완성치수 3축). imosIDBGPL.CHECKSUM1/2는 포인 환경에서 비어 있으므로 의존하지 않는다. - 치수는 완성/재단을 처음부터 분리해서 들고 간다. 한 필드로 뭉치면 엣지 두께 보정을 매번 재계산해야 하고 재단 오류가 난다. 엣지도 부재 속성이 아니라 변(edge) 4개 레코드로 둔다.
- 원격 도달성은 "포트 열림"이 아니라 기능검증(실제 쿼리·실제 파일읽기)까지 해야 판정된다 (2026-07-27 미니PC 실측: 사무실PC 445는 TCP 열림인데 SMB
Test-Path는 인증실패로 False,net view는 Tailscale 경유에서 오류 1702). 전역 규칙·머신 인벤토리·비침습 철칙 =rules/remote.md. - devplan 페이지 빌드는 이 사무실PC에서
build_hub.py를 직접 못 돌린다 —BASE/DEV가C:\dev하드코딩(미니PC 기준)인데 여기선 dev가 UNC(\\10.73.241.2\dev)다. 리포 파일을 고치지 말고 상수만 치환해 exec하는 래퍼로 실행한다. 치환 시re.sub대체문자열에 백슬래시가 있으므로 반드시 lambda repl을 쓴다(안 그러면bad escape \d). 검증된 래퍼 =scratchpad/build_hub_unc.py(치환 건수 1/1 아니면 abort).
🆕 신규 (2026-07-27 — 2차 심층조사에서 확정)
- imos DB 조회에
sqlcmd를 쓰지 마라 — 한글이 전부 깨진다(cp949 실측). 표준 = PowerShellSystem.Data.SqlClient직결(Server=(local)\IMOSSQL2012;Database=poin;Integrated Security=True), 출력은 UTF-8 TSV 파일로. 검증된 헬퍼 =scratchpad/imosq.ps1(SELECT 전용 정규식 가드 내장). 한글 리터럴은N'...'. - 사무실PC에서 Bash의
python은 깨진 venv(pfs/.venv)를 가리킨다. 반드시 절대경로C:\Users\poinpc\AppData\Local\Python\pythoncore-3.14-64\python.exe+PYTHONUTF8=1. - imos 테이블/컬럼의 정체를 "이름만 보고" 판정하지 마라 — 2차 조사에서만 오독 6건이 나왔다. 판정 근거는 항상 ①UNIQUE 인덱스 정의 ②
imos.Data.dll의 정본 enum(EnumShare.FolderItemType등) ③값이 어느 마스터 테이블에 조인되는지 셋 중 하나 이상이어야 한다. 실패 사례 =KMS(하드웨어세트 아니라 PD) ·Rv##(리비전 아니라 Reveal) ·poin_handle(손잡이 아니라 엔티티핸들) ·anglelem(존의 4변 아님) ·FAVORITES(즐겨찾기 아니라 MRU 캐시) ·TRAVERSE(iX PLAN 부속 아니라 포인 핵심 밴드 CP). - "파일이 디스크에 없다 = 기능 미도입"으로 단정하지 마라. imos는 RDL 리포트를 DB
BINDATA테이블에 varbinary로 저장한다. 1차 조사가 이걸 몰라 "리포트 미도입"으로 오판했고, 그 안에pfs링크드 서버 조인이 있었다(= imos↔PFS "단절" 판정도 무효). 자산 부재를 주장하려면 DB blob 테이블까지 훑어라. - 0행 테이블은 "포인 미사용"과 "기능 미도입"을 구분해 판정한다. 근거 =
IMOS.INI스위치(포인이 260키 중 실제로 바꾼 건 9개, 그 OFF들이 대부분의 0행 원인) + 설치 자산 유무 + 타 연도 데이터. - "2026년" 모집단을 폴더 기준으로 잡지 마라 — 사람이 오더를 옮기므로 재현 불가능하다. poincad2 회귀테스트 정본 =
PROADMIN.DATECREATE연도 기준 오더 223 / 부재 27,481 / 가공 117,185. (폴더 기준 153/17,369와 다르다. 도메인 3곳이 서로 다른 수를 쓰던 것을 이걸로 종결.) - 여러 에이전트가 같은 데이터를 조사하면 결론 충돌을 반드시 명시적으로 판정하라. 2차 조사에서 A파트 7건 · 감사 5건이 나왔고, enum 첫 글자 매칭보다 "값이 어느 테이블에 조인되는지"가 항상 강한 방법이었다. 충돌을 감추면 틀린 쪽이 설계에 들어간다.
- poincad2 스키마 철칙 3개(2차 조사에서 imos 실패를 보고 확정): ①면 주소를 존 경로에 이어붙이지 않는다(imos
anglelem "0.3"은 존인지 변인지 구분 불가 — 1차 조사 오독의 직접 원인) ②식별자에 사람이 읽는 이름을 섞지 않는다(imos는 오더명 문자열로 조인) ③단가에 유효일·통화를 넣고, 단가 0이면 조용히 더하지 말고incomplete로 표시한다(imos는 없어서 2009년 € 값이 2026년 한국 오더에 그대로 쓰였다).
🆕 신규 (2026-07-27 저녁 — iX CAD 실제 UI 구동 테스트에서 확정)
- iX CAD는 UI 자동화가 가능하다 — 단 "새 인스턴스 + Session 1 + SendInput" 조합일 때만. COM·LISP은 여전히 막혀 있지만 실사용자 입력 시뮬레이션(SendInput)은 통한다(실측: 아티클 삽입→복수선택→뷰 생성→자동치수까지 전 구간 구동). 조사 도구 =
scratchpad/ui.ps1(입력·창제어) ·shot.ps1/shotgrid.ps1(캡처·좌표그리드) ·dlg.ps1(모달 자동처리). - UI 구동 전 반드시
imos.exe·acad.exe실행 여부를 확인한다. 페페 작업 프로세스가 살아 있으면 구동하지 않는다(입력이 실작업 도면으로 샌다). 없을 때만 새 인스턴스를 띄운다. - 입력 가드는 hwnd 일치가 아니라 "같은 프로세스"로 잡는다. hwnd 완전일치로 하면 AutoCAD 자동완성 팝업·모달 대화상자에서 오탐으로 멈춘다(실측). 프로세스 단위여도 타 앱 유출은 막힌다.
- PowerShell에서
SendInput구조체를 조립하지 마라.$i.u.mi.dx = n은 중첩 값형식의 복사본에 써서 조용히 무시된다(실측: 커서가 전혀 안 움직임). 조립·전송을Add-TypeC# 메서드 안에서 한다. - PowerShell 5.1 함정 3종:
[ushort]없음 →[uint16]· 별칭이 함수보다 우선하므로 한두 글자 함수명 금지(r=Invoke-History,h=Get-History,gc=Get-Content에 가려져 엉뚱한 에러) ·-Pid파라미터명은 자동변수$PID와 충돌 →-ProcId. - 모달 대화상자는 픽셀 추측으로 누르지 말고
EnumChildWindows로 컨트롤 텍스트를 읽어 버튼 rect 중심을 클릭한다. 메시지 원문도 같이 수집돼 조사 근거가 된다. iX CAD 오류창은 연속 재발하므로Check1(다시 표시 안 함) 체크박스를 먼저 누르고 OK를 눌러야 끊긴다. - 뷰/치수 조사 시 오더는 저장하지 않는다. DB 기록은 오더 저장 시점에만 일어난다(실측: 미저장 테스트 후
PROADMIN신규 0건,SECTIONREFERENCE/MULTIARTICLECHECKSUM여전히 0행). 저장 안 하면 운영 DB 무흔적이다. -
🔴
SECTIONREFERENCE0행을 "POIN이 뷰를 안 만든다"의 근거로 쓰지 마라. imos 뷰는 2세대 공존이다 — 구(섹션 BREF,imosr24.arx, DB 기록 O, POIN 11오더) / 신(뷰셋 Viewset,ImosVisPart24.dbx, DWG 전용·DB 테이블 자체가 없음, POIN 85오더). POIN 주 경로는 신 계열이라 DB에 안 남는 게 정상이다. -
LINDIV파서는 공용 컴포넌트 1개로 만든다. 존분할·부재분할·커넥터배치·DESCRIPTOR·CSIDE·nut_erb·TRAVERSE·mppcpnts가 전부 같은 문법이다. 문법서 =survey2-2026-07-27/drangl-contour-objectdesigner.md.$(계산 시 치환)와#(지연 치환)는 모델에서 구분하고 원문식·평가결과를 둘 다 저장한다.