13.5 KB · 수정 2026-09-23 10:37
목차

2026-09-23(수) 세션 — 자재관리 동기화 20시간 먹통(SSM 97KB 한도) · 파싱 규칙 정본

운영배포 93d7ce1 완료 · devplan pfs-orderpaper-sync-ssm-limit.html · [코디]

지시

[페페] 「pfs 자재관리 항목에 나진 상가 등 새로 발주낸 내용을 엑셀파일 수정해서 담았는데 반영이 안된거 같다. 파싱 규칙 파악해서 조치해줘. 기존 파일에 새탭이 생성되거나 엑셀파일 기존 pfs에 데이터 올라간것도 원본 엑셀파일에서 내용이 수정되면 반영이 되는것을 기본으로 해야한다.」 + 「운영배포 사항이다」(채팅 승인).

결론 — 파싱 규칙 문제가 아니었다

운반이 끊겨 있었다. NAS 파일은 제대로 고쳐져 있었고(나진 9.22 16:51 저장, 새 탭 「코파산업」 포함), 그걸 운영 RDS 로 실어나르는 SSM 경로가 2026-09-22 13:39 부터 20시간 전량 실패하고 있었다.

An error occurred (MaxDocumentSizeExceeded) when calling the SendCommand operation:
The total size of your parameter(s) and document exceeds the 97KB limit.

고친 것 (파싱 규칙은 한 줄도 안 건드렸다)

무엇 어떻게 파일
청크 전송 60,000자 초과 시 여러 send-command 로 /tmp 에 이어붙인 뒤 디코드·실행 (S3 버킷 추가 0) scripts/ssm_run.py
원격 파일명 고유화 /tmp/_op_remote.<ms>_<pid>.py 〃
크기 대조 가드 b64 길이·디코드본 크기를 원본과 비교, 어긋나면 실행 거부 〃
실패 알림 동기화 실패 시 텔레그램(notify.py done) orderpaper-sync/orderpaper_sync.py

🔴고유화가 없으면 깨진다 — 첫 시도에서 실측했다. 고정 이름(/tmp/_op_remote.py)으로 청크를 붙였더니 디코드본이 74,276 → 103,552바이트(청크 1개가 중복 append)가 되며 SyntaxError. 예약작업(09:38)과 내 수동 실행(09:41)이 겹친 결과다. 그래서 크기 대조 가드를 같이 넣었다 — 반만 디코드된 파이썬이 돌면 RDS 를 어중간하게 건드린 채 죽는다.

검증

파싱 규칙 — 정본 요약 (orderpaper-sync/orderpaper_parse.py)

단계 규칙
대상 \\192.168.0.101\공유폴더\2026현장\원부자재발주서\MM월\*.xlsx — 05월 이상 월폴더만 · ~$ 제외
파일명 첫 토큰=가구업체 / 나머지=현장. 「원부자재발주서」 제거 · 선행 (마커)·추가·복사본 은 발주처로 안 본다 · 넥서스→한샘 정규화
탭 탭 1개 = 발주처 1개. 제외 = 업체·발주서·설정·al마감사이즈·al업체·유리업체·poin_info. 그 밖 새 탭은 전부 새 발주처로 수집
섹션 A열/C열 「포인 발주서」 제목마다 분리(한 탭에 여러 발주 가능)
날짜 「수신」 행 = 발주처(C열)+발주일 · 「참조」 행 = 입고일
표 「NO」 행이 머리. 머리에 「원자재」 있으면 R(두께·수종·전면·후면·발주량), 없으면 S(품명·규격·발주량·입고일·비고)
엣지 품명·규격에 「엣지」 있으면 E 블록으로 자동분리(「엣지바」는 AL 이라 제외)
입고처 A열 「입고처」 행 = 그 위쪽 항목들의 입고처(여러 개면 각자 자기 위 묶음을 끊는다)
안 읽음 NO칸이 글자인 라벨행 · 품명 0 · 품명·규격·발주량 전부 빈칸 · 원자재 템플릿 자리행 · 품명칸 병합이 규격칸까지 덮은 「시트」 메모행
현장연결 파일명 현장 ↔ field.name 토큰 2개 이상 일치면 자동, 수동 연결은 보존

반영 원리 — 항목 신분증 = (파일명 · 탭 · 섹션번호 · 자재구분 · 표 안 순번) 해시. 같으면 UPDATE(값 전부 최신 엑셀로), 없으면 INSERT, 이번 라운드에 안 나오면 remove_yn='1'. 보존(덮어쓰지 않음) = 진행상태(대기/진행/완료) · 사용자가 바꾼 자재구분·입고일 · 재고(stock).

반영이 안 되는(어긋나는) 경우 — 지금도 유효

  1. 🔴날짜칸을 비우면 화면에서 사라진다 — 자재관리는 입고일‖발주일 기준 3개월 창만 본다. 비면 양식 기본값(1월)이 들어가 창 밖으로 밀린다. 현재 숨김 유발 23블록 · 날짜 미기입 32블록(동기화 로그 끝 경고 목록).
  2. 🔴표 중간에 행을 끼우면 아래 항목 순번이 밀려 신분증이 바뀐다 → 값은 맞지만 진행상태·입고 체크가 한 칸씩 옮겨붙는다. 추가는 맨 뒤가 안전.
  3. 🔴파일명을 바꾸면 전부 새 항목 + 옛 항목 삭제표시(진행상태 초기화).

2부 — 날짜 고침 패널 + 엑셀 되쓰기 (같은 날 후속, 테스트서버만 반영)

지시

[페페] 「자재관리 탭, 발주서 정리 탭에 상단에 별도 블록으로 만들어서 날짜 없는 내용들을 올려주고 경고 표시해줘. 그리고 거기서 날짜수정하면 엑셀파일에도 지정된 날짜 입력하고 저장해줘. 엑셀이 읽기모드인 경우에는 닫힘 신호 기다렸다가 그 후에 작업 진행해줘. 1시간 이상 대기시 파일 이름을 나에게 텔레그램으로 알려줘라.」 + 「테스트서버 데이터 운영데이터로 최신화 하고 테스트서버 반영만 먼저 반영해」.

그릴링 1문 [페페] 결정 = 입고일 칸의 「긴급」 63칸은 경고에 올리고, 날짜를 넣으면 덮어쓴다 (화면에 현재값을 같이 보여준 뒤 교체 — 납기가 정해졌다는 뜻이니 「긴급」은 역할을 다한 값이다).

먼저 한 것 — 테스트 데이터 최신화

운영 orderpaper(2,758행)·orderpaper_site_map(60행)을 테스트 RDS 로 복제(_op_copy_prod_to_test.py, 운영은 읽기만). 그 뒤 테스트 동기화 1회 → alive 2,158행 / 47현장으로 운영과 같아졌다. ⚠단 field_no 는 환경마다 다르다(테스트 field 테이블 기준으로 다시 매핑된다 — 「용산 나진 상가」는 테스트에 현장이 없어 미연결).

설계를 바꾼 발견

🔴블록의 엑셀 위치가 DB 에 없었다. block_key 는 sha1(source_file|sheet|section|material_type) 이라 되돌릴 수 없고, sheet·section 은 해시 입력으로만 쓰이고 버려졌다 — 「이 블록이 어느 탭 몇 번째 발주인가」를 알 길이 없으니 엑셀을 고쳐 쓸 수가 없다. 파서가 이미 아는 값을 안 저장해서 생긴 구멍이다. → 컬럼 4개 추가: sheet_name · section_idx + order_date_raw · receive_date_raw(날짜칸의 비날짜 원문 = 「긴급」).

만든 것

층 파일 내용
파서 orderpaper-sync/orderpaper_parse.py 날짜칸(I열) 원문 보존 — raw_date_cell()
동기화 orderpaper-sync/orderpaper_sync.py 컬럼 4개 적재(DDL·INSERT·UPDATE)
테이블 orderpaper-sync/orderpaper_date_fix_migrate.py orderpaper_date_fix 큐(13컬럼)
API api/pfs/orderpaper/{dao,service,controller} date-issues · date-fix · date-fixes · date-fix/claim · date-fix/finish
게이트 web/__init__.py 에이전트 화이트리스트 2줄
엑셀 쓰기 orderpaper-sync/orderpaper_date_write.py zip XML 치환 + 백업 + 검증 + 잠김 판정
사이드카 orderpaper-sync/orderpaper_{date_fix_relay,sync_server}.py 20초 폴링 · 잠김 대기 · 1시간 텔레그램
화면 web/templates/basic/field/field_orderpaper.html #opDateFixPanel

🔴 함정 3개 (전부 실측으로 드러났다)

  1. openpyxl 로 열었다 저장하면 안 된다. 나진 발주서 실측 — printerSettings1~3.bin · sharedStrings.xml · calcChain.xml · sheet rels 3개가 사라지고 126,679 → 102,971바이트. 발주서는 인쇄해 팩스로 나가는 문서라 프린터 설정 손실이 곧 사고다. ⛒zip 안 대상 시트 XML 한 장만 치환 → 실측 파트 손실 0.
  2. openpyxl 의 cell.style_id 는 XML 의 s= 와 다른 체계다. 그대로 쓰면 저장된 파일이 IndexError 로 안 열린다. → 스타일도 XML 에서 직접 읽는다(_xml_style_of). 「긴급」이 있던 칸은 날짜 서식이 아니라, 같은 열의 날짜 셀 서식을 베껴야 46285 로 안 보인다.
  3. ssm_run.py 의 입력 JSON 파일명이 고정이라 «남의 스크립트가 실행됐다». 사이드카가 20초마다 같은 경로를 쓴다 — 내 E2E 스크립트를 보냈는데 RELAY_CLAIM none 이 돌아왔다. 청크 파일명만 고유화하고 입력 JSON 을 놓쳤던 것. → _ssm_input_<ms>_<pid>.json + 실행 후 삭제.

화면 (web-designer 처방 반영)

동작 규칙

검증

남은 것