500은 오류가 아니라 종료 신호였다

26년 08월 16일

관문을 넘었다

법원경매 권리분석 프로젝트에서 가장 어려웠던 관문 하나를 넘었다.

서류 원문 확보다.

법원경매 데이터는 목록만으로는 권리분석이 안 된다.

현황조사보고서와 매각물건명세서 원문에 점유 관계, 임차인 주소, 회차별 최저가 같은 핵심 실태가 들어 있다.

등기부 없이는 절대 검증할 수 없던 정보들이다.

그런데 이 서류 원문을 내려받는 공식 API는 존재하지 않는다.

포털의 뷰어 프로토콜을 직접 해독해서 우회하는 수밖에 없었다.

서류를 내려받는 5개의 함정

결론부터 말하면 해독에 성공했다.

현황조사보고서는 기존 크롤러 세션을 재사용하는 요청 하나로, 매각물건명세서는 열람등록부터 시작하는 4홉 체인으로 각각 원문을 확보했다.

현황조사보고서는 구조화된 JSON이고 매각물건명세서는 페이지별 전문 텍스트로 들어온다.

OCR 같은 후처리가 필요 없다는 뜻이다.

실제 매물 2건에서 원문 획득까지 검증했다.

그 과정에서 함정이 다섯 개 있었다.

열람등록 요청에는 커스텀 헤더 세 개가 필수인데 하나라도 빠지면 500이 돌아온다.

PDF 조회 요청은 부분 전송이 안 되고 전체 필드 복제가 필요하다.

인증 헤더는 Bearer 접두사 없이 토큰 값만 넣어야 한다.

마지막 페이지 이후에는 요청이 500을 반환하는데 이건 오류가 아니라 정상 종료 신호다.

공개도 기일 1주 전부터만 가능해서 그 전에는 아무리 잘 짜도 원문이 없다.

가장 인상적인 건 네 번째 함정이다.

500을 보고 실패로 판정하면 마지막 페이지 근처에서 항상 크롤이 망가진다.

상태 코드만으로 성공과 실패를 가르는 습관이 얼마나 위험한지 다시 체감했다.

과거에 200 응답 안에 차단 메시지가 박혀 있던 사례와 정확히 같은 계열이다.

응답 본문을 읽지 않으면 진실이 안 보인다.

서류는 게시기간이 지나면 소멸한다

원문을 확보했으니 저장 전략이 필요했다.

법원 포털의 서류는 게시기간이 지나면 소멸한다.

크롤 시점에 잡지 않으면 다시 받을 방법이 없다.

그래서 GCS에 원문 PDF를 아카이브하고 서비스에는 구조화 데이터만 노출하는 하이브리드로 결정했다.

원문 아카이브와 노출 데이터를 분리하면 나중에 파싱 로직이 바뀌어도 원문에서 다시 뽑을 수 있다.

수집 대상은 매각물건명세서와 현황조사보고서 두 종으로 좁혔다.

감정평가서는 이미 요약 데이터가 있어서 원문의 한계 효용이 낮다고 판단해서 뺐다.

서류 수집은 별도 스윕이 아니라 매물 파이프라인 트랜잭션에 인라인으로 넣었다.

결정 기준이 흥미로웠는데 타이밍이나 코드 우아함이 아니라 크래시 시나리오의 데이터 내구성이었다.

매물은 저장됐는데 서류 수집이 뒤로 밀려난 상태에서 프로세스가 죽으면, 게시기간이 지난 서류는 영원히 사라진다.

같은 트랜잭션에 넣으면 둘 다 저장되거나 둘 다 롤백된다.

사라지면 안 되는 데이터는 사라질 수 없는 경로로 흘려보내라는 원칙의 적용이었다.

차단 기준은 물건 수가 아니라 누적 요청 수였다

같은 기간에 크롤러 안정성 실측도 진행했다.

과거 실행 기록을 대조하니 차단 시점이 제각각이었다.

800건 넘게 처리하고 차단된 실행도 있고 500건 만에 차단된 실행도 있다.

물건 수로 설명이 안 됐다.

로그를 파고들니 기준은 세션 시작 후 누적 요청 수였다.

쿨다운 뒤 새 실행은 카운터가 리셋된 상태로 시작한다.

500개마다 차단 같은 고정 임계는 없다.

그래서 세션 회전 주기를 300~600건 사이 무작위로 돌렸다.

N건마다 정확히 회전하는 것 자체가 또 패턴이 되기 때문이다.

회전 로직에서 버그도 하나 잡았다.

세션 회전 GET이 요청 간격 대기를 우회해서 즉시 나가고 있었다.

회전 직전 요청이 4MB짜리 상세를 받느라 3초 걸렸다면, 회전 GET은 사실상 3초 간격으로 발사된 셈이다.

등간격 단타 요청은 없애려던 봇 시그널 그 자체인데 완화 로직이 시그널을 만들고 있었다.

HTTP 실패 분류도 정리했다.

영구적인 500은 재시도하지 않고 스킵이 정답이고 일시적인 503은 재시도할 가치가 있다.

둘을 같은 재시도 큐에 넣으면 안 된다.

0%의 진짜 원인은 키 오타였다

데이터 품질 쪽에서 오결론 하나를 교정했다.

경매 공고의 최선순위 필드가 한동안 0%로 집계되고 있었다.

API가 값을 안 준다는 결론이 내려져 있었는데 알고 보니 매핑 테이블의 키 오타였다.

Hypothc와 Hypthc, 철자 한 글자 차이다.

키를 고치니 100% 충전됐다.

데이터가 비는 지점을 발견하면 API 탓으로 결론 내리기 전에 내 매핑을 먼저 의심해야 한다.

마무리

하루 종일 법원경매 프로젝트에 매달렸다.

서류 원문 확보로 등기부 없이는 불가능하던 점유와 권리 실태 검증의 원천이 열렸다.

그리고 서류 원문은 게시기간이 지나면 소멸한다는 제약이 인라인 트랜잭션 같은 아키텍처 결정을 몰고 왔다.

기술 선택의 기준을 타이밍이 아니라 데이터 내구성에 두는 관점이 이번에 가장 큰 수확이었던 것 같다.