수집이 끝났고 ZIP도 정상이라는 보고를 보면 자료가 모두 들어 있다고 받아들이기 쉽다. 오래된 공통 크롤러 작업을 다시 읽으며 나는 완료 숫자를 그대로 옮기기 전에 원장과 파일을 대조했다. 저장 기록 여러 개가 같은 경로를 가리켰고, 압축파일의 손상 검사가 통과한 상태에서도 현재 원장이 가리키는 파일 참조 한 곳은 맞지 않았다. 서로 다른 검사를 하나의 완료로 묶지 않아야 했다.
먼저 숫자가 무엇을 세는지부터 나눴다
전체 기록은 요청과 처리 결과의 행 수다. 정상 저장 행은 그중 오류 없이 저장 경로를 남긴 기록이며, 실제 고유 파일 수는 같은 경로를 한 번씩만 세어야 한다. ZIP 항목에는 이 파일들 외에 보고서와 설명 파일도 들어갈 수 있다. 화면에 나란히 표시된 숫자라도 같은 단위가 아니었다.
원래 소스의 _record는 처리 결과를 목록에 추가하고 저장·실패 카운터를 따로 올린다. 그 저장 카운터가 곧 고유한 파일 경로의 수가 되는 것은 아니다. 이번에는 실제 원장을 읽고 정상 행에서 경로 집합을 다시 만들었다. 고객 사이트 이름과 URL, 실제 수집 파일은 공개하지 않고 아래에는 합성 값만 넣었다.
이어받기 경로는 기존 HTML 파일이 있으면 그 바이트를 읽어 처리에 재사용한다. 따라서 정상 저장 기록을 이번 실행에서 인터넷으로 새로 받은 파일 수라고 부를 수도 없다. 재사용한 자료와 새 응답을 구별해 보고하려면 그 출처를 별도로 기록해야 한다. 이번 원장의 카운터에 없는 의미를 이름만으로 덧붙이지 않았다.
같은 경로가 반복돼도 같은 이유로 반복된 것은 아니었다
선택한 원장에는 같은 최종 URL과 경로가 여러 행에 남은 경우가 있었다. 서로 다른 URL이 하나의 저장 경로를 가리키는 경우도 있었다. 요청 결과의 이력과 현재 파일 하나를 같은 것으로 볼 수 없다는 뜻이다. 반복 행을 발견했다고 모두 불필요한 중복이라고 정리하지 않았다.
크롤러는 최종 응답 주소를 기록하고 URL에서 저장 이름을 만든다. 쿼리를 구별하는 지문과 긴 경로의 해시 저장, 파일과 폴더 이름이 충돌할 때의 우회 경로도 있다. 그렇다고 최종 경로 하나가 그 경로를 참조했던 모든 요청의 바이트를 각각 보존했다는 증거는 아니다. 이번 원장에는 각 요청의 원래 바이트 지문이 없어 그 부분까지 확인했다고 말할 수 없었다.
파일 개수가 같아도 다른 파일이 섞일 수 있다
합성 원장에 저장 성공 네 행이 있고 그중 두 행이 a.bin을 함께 가리킨다고 해 보자. 기대하는 고유 경로는 a.bin, b.bin, c.bin 세 개다. ZIP에는 a.bin, b.bin과 설명 파일 하나가 들어 있다. 기대 경로도 세 개이고 ZIP 파일도 세 개지만 c.bin은 없다. 개수 비교는 맞아도 참조 집합 비교는 실패한다.
그래서 나는 기록 행 수, 고유 경로 수, ZIP 파일 수와 누락된 경로 목록을 별도로 본다. 설명 파일 같은 추가 항목은 그 자체로 오류가 아니지만 빠진 자료를 대신 채운 것으로 세면 안 된다. 아래 표는 이 관계를 정적으로 보여 줄 뿐 실제 수집 대상의 파일 목록이나 원본 내용은 담지 않는다.
압축 무결성과 원장 참조는 다른 질문이었다
이번에 기존 ZIP의 항목을 읽고 CRC·헤더 검사를 실행했으며 손상된 항목은 발견하지 못했다. 이 검사는 압축파일 안에 있는 항목을 대상으로 한다. 외부 원장이 기대한 파일이 모두 들어 있는지는 원장의 경로 집합과 ZIP 항목 집합을 따로 대조해야 한다.
그 대조에서 현재 정상 저장 기록이 가리키는 경로 한 곳을 ZIP 안에서 찾지 못했다. 현재 수집 폴더에서도 해당 경로는 파일이 아니었고, 대소문자나 한글 정규화 차이로 대응되는 ZIP 이름도 없었다. 이 관찰은 현재 참조의 불일치다. 언제 왜 달라졌는지, 당시부터 누락됐는지, 다른 위치에 같은 바이트가 남아 있는지까지 밝혀졌다는 뜻은 아니다.
없는 참조를 닮은 파일로 조용히 고치지 않았다
같은 파일 이름이 다른 폴더에 있다고 원장의 빈자리를 그 파일로 연결하면 원래 출처를 바꿀 수 있다. 이번에도 비슷한 이름의 다른 파일이 있었지만 기대한 바이트 지문이 없는 상태에서 대체하지 않았다. 현재 누락 참조와 원장·ZIP의 지문을 비공개 기록에 남기고 원래 수집본은 그대로 두었다.
추가 복구를 한다면 원래 요청·최종 응답·저장 경로의 관계부터 확인해야 한다. 다른 위치의 자료를 후보로 찾더라도 같은 내용임을 확인할 근거가 필요하다. 단지 상태를 완료로 보이게 하려고 경로를 바꾸거나 오류 행을 지우면 다음 작업자는 무엇을 복구했는지 알 수 없다. 이번 콘텐츠 작업은 원본을 수선하거나 외부 사이트를 다시 수집하는 작업으로 넓히지 않았다.
요청 실패와 포장 실패도 따로 읽었다
원래 HTTP 수집 루프는 실패 기록을 남긴 뒤에도 다른 자료의 수집을 이어간다. 루프가 끝나면 작업 상태는 completed가 될 수 있고 실패 수는 별도로 남는다. 브라우저 보강 가져오기는 그 보강에서 실패가 있으면 partial과 실패 종료 코드로 표현한다. 같은 완료 단어만 보고 서로 다른 단계의 계약을 합치지 않아야 했다.
ZIP 생성은 임시 파일에 먼저 쓰고 결과 파일로 교체하며, 읽을 수 없는 개별 파일은 오류 목록에 모은다. 원본 서버가 응답하지 않은 것과 로컬 파일을 포장하지 못한 것은 다른 실패다. 오류 목록이 비었다고 참조가 모두 존재하거나 모든 웹 자료를 발견했다는 결론이 생기지도 않는다. 기록 파일의 작성 시점과 지금 확인한 ZIP의 범위도 함께 남겨야 한다.
바이트를 확인하는 부품은 기존 구현을 다시 썼다
기존 공유 모듈의 compare_download_bytes는 전달받은 바이트 길이와 SHA-256을 계산하고 완료 선언·수신량·선택적인 기대 해시를 비교한다. 기대 해시가 없으면 실제로 계산한 지문과 원래 내용의 일치 확인을 구별해 보여 준다. 이번처럼 과거 원본 지문이 없는 자료를 읽을 때 특히 중요한 차이다.
그 모듈은 크롤링 범위나 ZIP의 원장 참조를 자동으로 감사하지 않는다. 이번에는 현재 공개 패키지의 바이트와 해시를 대조하고 압축을 푼 뒤 기존 36개 합성 검사를 다시 실행했다. 파일의 바이트 비교라는 책임을 재사용하고, 원장과 실제 항목의 집합 대조는 이번 비공개 감사 기록에 별도로 남겼다. 원래 고객 사이트의 자료를 새 패키지로 묶어 공유하지 않았다.
다음 사람에게는 완료 숫자보다 확인한 관계를 넘긴다
인계에는 무엇을 요청하고 무엇이 실패했는지, 어떤 경로에 저장했다고 기록됐는지, 현재 ZIP에 무엇이 있는지, 기대 지문과 대조했는지를 나눠 적고 싶다. 그래야 다음 사람이 재수집이 필요한지, 원장 정정이 필요한지, 추가 근거를 기다려야 하는지 판단할 수 있다. 일부 자료의 확인이 전체 사이트 수집의 증명이 되지는 않는다.
이번에는 크롤러를 실행하거나 브라우저를 열지 않았다. 선택한 소스와 과거 메타데이터, 기존 ZIP의 읽기 대조와 공유 합성 모듈의 시험만 수행했다. 원래 프로그램의 수집 범위와 접근 경계를 바꾸지 않았다. 자료를 많이 받았다는 이야기보다 어떤 숫자가 어떤 파일과 연결되는지 설명하는 이야기를 남기고 싶었다. 홈페이지를 다시 만들 때도 그 구분이 있어야 다음 수정의 재료를 믿고 찾을 수 있다.