공무원 영수증 151만 건을 파싱해 맛집 지도를 만들었습니다
1시간 전
그냥여기를 만든 사람입니다. Sidedock 운영자분이 메일로 세 가지를 물어보셨습니다 — 왜 이런 데이터를 보게 됐는지, 수많은 문서 형식을 어떻게 정리했는지, 만들면서 가장 예상 밖이었던 게 무엇인지. 순서대로 적습니다.
왜 하필 업무추진비였나
맛집을 검색하면 광고를 걸러내는 데 시간을 다 씁니다. 별점도 리뷰도 돈이 드나드는 자리라 믿기 어렵습니다. 그래서 아예 리뷰를 쓰지 않는 쪽을 골랐습니다.
지자체는 업무추진비 집행내역을 스스로 공개합니다. 감사를 전제로 올리는 자료라 상호·날짜·금액이 이미 한 번 검증을 거친 셈이고, 무엇보다 누가 썼는지가 부서 단위로 적혀 있습니다.
여기서 규칙 하나가 나왔습니다.
맛을 평가하지 않는다. 몇 개 부서가 갔는지만 센다.
한 부서가 스무 번 간 집은 그 부서 단골이고, 열 개 부서가 한 번씩 간 집은 그 동네에서 「손님 데려갈 만한 집」으로 통한다는 뜻입니다. 행 수는 「얼마나 썼나」이고 부서 수는 「누가 갔나」입니다. 순위는 뒤쪽으로만 셉니다.
문서 형식 — 여섯 갈래를 직접 읽습니다
게시판은 기관마다 제각각이고, 붙임 파일은 더합니다.
| 형식 | 어떻게 |
|---|---|
| HWP | OLE 복합문서를 열어 레코드를 직접 훑습니다 |
| HWPX | zip 안의 XML |
| 텍스트 조각의 좌표로 표를 다시 세웁니다 (제일 긴 파서, 1,100줄) | |
| XLS / XLSX | 시트 여러 장 중 어느 것이 본표인지 고릅니다 |
| HTML | 게시글 본문의 표 |
형식보다 어려웠던 건 어느 표가 본표인지 고르는 일이었습니다. 붙임 하나에 표가 여럿이고, 어떤 건 결재란이고 어떤 건 빈 서식입니다. 열 이름도 「집행일자 / 일시 / 사용일」처럼 기관마다 다릅니다.
지금 소스는 215개(도는 것 203개), 형식 파서만 2,300여 줄, 회귀 시험 2,307개입니다. 원장은 163만 행이고요.
예상 밖이었던 것 셋
1. 지표가 「좋아지는」 쪽으로 튀면 그게 결함이다
구리시 파서를 고치고 「부서 자리에 지역명이 들어온 행」 비율을 다시 쟀더니 33.6%에서 0%가 됐습니다. 기뻐할 일이 아니었습니다.
못 읽은 243행이 앞줄 부서를 그대로 물려받은 것이었고, 지역명이 사라진 게 아니라 그 행 자체가 안 읽힌 것이었습니다. 결함이 지표를 고쳐준 셈입니다.
그 뒤로 규칙을 하나 더 뒀습니다 — 「0건」이 나오면 없는 것인지 못 센 것인지부터 가른다. 검사기에 일부러 결함본을 물려서 실제로 빨개지는지 보고요.
2. 「SSL이라 못 걷는다」는 대개 서버가 한 장을 빠뜨린 것
강릉이 두 판(두 분기) 넘게 스킵으로 남아 있었습니다. 못 걷은 것 중 제일 큰 21만 행이었고요.
curl 200
node fetch UNABLE_TO_VERIFY_LEAF_SIGNATURE
TLS 서버는 자기 인증서와 중간 인증서까지 보내야 하는데 중간을 빠뜨리는 서버가 있습니다. 브라우저와 curl은 인증서의 AIA 항목을 따라가 그 한 장을 스스로 받아오지만, Node는 따라가지 않습니다. 그러니 「자료가 없다」가 아니라 「우리 도구가 못 읽는다」였습니다.
검증을 끄는 대신 빠진 한 장을 받아 번들에 넣었습니다. 뿌리는 Node가 이미 갖고 있어서, 신뢰를 하나도 넓히지 않고 풀렸습니다.
3. 파서를 고쳤더니 922행이 「사라질」 뻔했다
목록에서 칸을 하나 더 읽게 되면 글 주소의 조각(#dept=…)이 바뀝니다. 그런데 HTTP 캐시 열쇠가 주소째라, 같은 글이 통째로 캐시에 없는 글이 됩니다.
고치기 전후를 견주니 그 글의 행이 전부 없어진 것처럼 보였습니다. 파서 탓으로 읽고 원장에서 지웠으면 922행을 날릴 뻔했습니다. 지금은 앞뒤 판의 「건너뛴 글」 수를 같이 견줘서, 갈린 소스를 합계에서 빼고 이름을 찍습니다.
「맛집」이라는 말에 대해
메일에서 짚어주신 대로입니다. 업무추진비에는 기관과의 거리, 단체석 유무, 회식 편의가 함께 묻어 있습니다. 세종에서 가족 외식보다 점심·회식 자리가 많이 보이는 건 데이터 성격 그대로고요.
그래서 낱장마다 「부서 수로 셉니다」를 적고, 세는 규칙을 따로 한 쪽에 뒀습니다. 「직장인들이 실제로 자주 간 곳」쪽으로 더 뾰족하게 가져가라는 말씀은 그대로 받아 적어 뒀습니다.
그 밖에 정해 둔 것들
-
부서명은 화면에 올리지 않습니다. 1인 직위는 사실상 개인이라서요. 집계에만 씁니다.
-
좌표는 기기 밖으로 나가지 않습니다. 서버로 보내지도, 광고에 넘기지도 않습니다.
-
분기마다 자동으로 다시 걷습니다. 폐업·상호 변경이 그때 반영됩니다.
-
6,426장을 미리 그려 올리는 정적 사이트라, 배포는 푸시 하나로 끝납니다.
좋은 메일 감사합니다. 덕분에 정리하면서 저도 다시 봤습니다.

