buildrisk-radar

ADR-016 공시 원문 구조화 — 수주 계약 · 채무보증 (기업 × 지역 교차)

왜 재무제표는 분기 단위이고 늦게 나온다 — 실데이터: 유니버스 43개사의 2026 반기보고서는 반기 말(6/30) 뒤 43~49일(중앙값 45일)에 접수됐다. 건설사의 앞으로의 매출(수주)과 우발채무(보증)는 수시공시 원문에 먼저 나온다. 기존에는 보고서 제목으로 ‘수주 공시가 있었다’만 알았다. 원문을 구조화하면 어느 지역에 얼마의 공사가 걸렸는지를 알 수 있고, 이것을 지역 위험(미분양)과 이으면 ‘미분양이 쌓이는 지역에 매출이 몰린 건설사’라는, 재무제표에는 아직 없는 신호가 된다.

대상 유니버스 기업의 단일판매ㆍ공급계약체결(정정·자율공시 포함) · …해지 · 타인에대한채무보증결정 — 최근 1년 1,313건.

수집·보관 filingParseJob 이 document.xml(Zip)을 받아 원문 HTML 을 그대로 저장(dart.filing_doc, SHA-256)하고 파서 버전을 기록한다. 파서를 고치면 버전을 올리기만 하면 되고, 다음 실행의 reparse Step 이 API 호출 없이 원문으로 다시 구조화한다(ADR-003 의 원천 불변 원칙과 같음). Zip 해제는 5MB 상한(zip bomb 방지), XML 파서는 DTD·외부 엔티티를 끈다(XXE).

파싱 공시 서식은 ‘항목명 칸 + 값 칸(xforms_input)’ 표이고 묶음은 rowspan 이다. 유가·코스닥 서식과 정정 공시의 번호·표기가 조금씩 달라 (계약금액(원) ↔ 계약금액 총액(원), 계약상대 ↔ 계약상대방, 번호 7 ↔ 8) 번호·공백·가운뎃점을 뗀 항목 경로(계약내역>최근매출액(원))로 찾는다. 경로 덕분에 계약상대방의 ‘최근 매출액’과 계약내역의 ‘최근매출액’이 섞이지 않는다. 정정 공시 맨 위 ‘정정사항’ 표는 항목명 칸이 없어 자연히 건너뛰고 본문(정정 후 값)을 읽는다. 채무보증 공시 하단의 PF 유형별 보증 표도 읽는다. 실제 원문 5건(유가 체결 · 코스닥 정정 · 해지 · PF 보증 · 보증 정정)으로 FilingParserTest.

‘현재’ 정의 같은 회사·같은 계약명(공백·기호 제거)은 가장 최근 공시만, 정정 공시가 가리키는 원 공시(제출일)는 대체됨, 해지 공시는 계약명 또는 (원 공시일 + 금액)으로 연결. 매 실행 전체를 다시 계산한다(멱등). 한계: 같은 회사가 이름이 같은 다른 계약을 공시하면 하나로 합쳐질 수 있다.

지역 ‘판매ㆍ공급지역’ 자유 서술 주소(“부산광역시 사하구 감천동 94-1번지 일원”)에서 시도 + 뒤이은 시·군·구(시 다음 구는 일반구)만 보고 기존 RegionMatcher 로 찾는다. 시도만 있으면 SIDO, 국가명이 있으면 OVERSEAS, 그 밖은 UNKNOWN — 추정으로 채우지 않고 비중 계산에서 뺀다.

규칙

화면 기업 상세 ‘수주·보증’ 탭 · 지역 상세 ‘이 지역에 걸린 수주’ · 전체 비교 ‘수주·보증 노출’.