10개 대상에서는 안정적이던 수집 작업도 수천 개로 확대하면 신뢰성이 떨어질 수 있습니다. 실패 분류와 중복 제거, 속도 제한과 동시성, 재개 기능, 네트워크 출구 장애, 데이터 일관성 검증, 핵심 모니터링 지표는 대규모 환경에서 특히 중요해집니다.
데이터 수집 스크립트가 10개 대상에서는 잘 돌아가다가 수천 개로 확대하면 성공률이 떨어지기 시작할 수 있습니다. 재시도를 추가하고 프록시를 바꾸고 동시성을 조정해도 문제가 반복됩니다. 더 깊이 살펴보면 병목은 파싱 로직이 아니라 구축되지 않은 여러 엔지니어링 계층에 있는 경우가 많습니다. 소규모에서는 이런 문제가 아예 드러나지 않을 수 있습니다.
재시도가 의미 있으려면 먼저 실패를 분류해야 한다
데이터 수집에는 실패가 반드시 생깁니다. 핵심은 유형을 나누는 것입니다. 네트워크 흔들림과 연결 재설정은 즉시 재시도할 수 있습니다. 일시적인 속도 제한은 backoff 후 재시도해야 합니다. 페이지 구조 변경으로 파싱 결과가 비어 버렸다면 1만 번 재시도해도 해결되지 않으므로 기록하고 알림을 보내야 합니다. 대상 자체가 존재하지 않으면 완료로 표시하면 됩니다. 환경이나 네트워크 출구가 시작되지 않으면 다른 것으로 전환한 뒤 다시 시도합니다.
무조건 재시도하는 것은 가장 흔한 실수 중 하나입니다. 사람의 개입이 필요한 문제를 반복문 속에 숨기고, 동시에 할당량과 출구 자원을 낭비합니다. Backoff도 필요합니다. 재시도 간격을 점점 늘리지 않으면 한 묶음의 작업이 같은 시간대에 다시 몰려가 속도 제한을 더 심하게 만들 수 있습니다.
재시도는 곧 중복 제거 문제로 이어집니다. 한 작업이 재시도로 여러 번 실행될 수 있으므로 각 작업에는 안정적인 고유 식별자가 필요합니다. 예를 들어 URL 정규화 후의 값을 사용할 수 있으며, 데이터베이스 쓰기는 이 식별자를 기준으로 멱등하게 처리해야 합니다. 그렇지 않으면 재시도가 많아질수록 오염된 데이터도 늘어납니다.
속도 제한과 동시성은 서로 다른 문제다
동시성을 높인다고 처리량이 반드시 증가하는 것은 아닙니다. 동시에 세 가지 제약이 작동합니다. 대상 사이트가 속도 제한을 걸기 전까지 견딜 수 있는 부하, 로컬 머신의 메모리와 CPU, 그리고 하나의 환경이나 세션이 여러 작업을 동시에 실행할 수 있는지 여부입니다.
안정적인 방법은 낮은 동시성에서 시작해 부하를 점진적으로 올리면서 성공률과 응답 시간을 함께 관찰해 성능이 뚜렷하게 나빠지는 지점을 찾는 것입니다. 속도 제한은 별개의 문제입니다. 동일한 대상에 접근하는 속도를 제어하는 것이며 전역 동시성과 같은 개념이 아닙니다. 한 작업 묶음이 여러 사이트에 분산된다면 각 사이트별로 별도의 접근 속도를 정해야 합니다.
중단 후 재개는 상태 영속화에 달려 있다
몇 시간 동안 실행되는 작업이 한 번 중단되는 것은 흔한 일이며, 처음부터 다시 시작하는 비용은 너무 클 수 있습니다. 따라서 상태를 저장해야 합니다. 대기, 처리 중, 완료뿐 아니라 재시도 횟수, 다음 실행 가능 시각, 오류 유형도 함께 영속화해야 합니다. 프로세스가 시작될 때는 메모리에서 다시 만드는 대신 저장소에서 큐를 복원해야 합니다.
큐를 메모리에만 유지하는 것은 겉보기에는 잘 동작하는 흔한 구현입니다. 프로세스가 종료되면 대기 중인 작업이 모두 사라지고 수치도 더 이상 맞지 않습니다.
프록시와 네트워크 출구 장애는 별도로 처리해야 한다
대상이 특정 출구를 차단하거나, 프록시가 끊기거나, 지역 노드 상태가 흔들리는 일은 대규모 환경에서 계속 발생합니다. 예외가 아니라 일상적인 조건입니다. 출구를 교체 가능한 자원으로 다뤄야 합니다. 작업이 실패하면 먼저 대상의 속도 제한인지 출구 사용 불가인지 판단하고, 전자라면 backoff를 적용하고 후자라면 출구를 바꾼 뒤 재시도합니다. 각 출구의 실패율도 기록해 성능이 뚜렷하게 나빠진 그룹은 제외합니다.
반대로 모든 작업이 하나의 출구를 공유하면 한 작업이 경로를 망가뜨려 뒤의 모든 작업에 영향을 줄 수 있습니다. 문제를 조사할 때는 로그를 거꾸로 추적해 어떤 작업이 원인이었는지 찾아야 합니다.
데이터 일관성 검증
작업이 끝났다고 데이터가 올바른 것은 아닙니다. 저장 후에는 몇 가지 질문에 답할 수 있어야 합니다. 완료 작업 수와 저장된 행 수가 맞는가, 파싱 결과가 빈 비율은 얼마인가, 핵심 필드의 누락률이 비정상적으로 올라갔는가, 중복 행은 몇 개인가?
이 검증은 복잡할 필요가 없습니다. 배치별 샘플링만 해도 충분하지만 누군가는 결과를 확인해야 합니다. 규모가 커질수록 잘못된 데이터는 데이터가 없는 것보다 더 큰 문제가 될 수 있습니다.
어떤 지표를 모니터링해야 하나
지표를 너무 많이 볼 필요는 없습니다. 시스템 상태를 보여 주는 몇 가지면 충분합니다.
- 성공률과 실패 유형 분포: 어떤 오류가 늘고 있는지 확인
- 작업 큐 길이와 평균 대기 시간: backlog가 계속 늘면 유입량과 처리 능력이 맞지 않는다는 뜻
- 활성 환경 수와 관련 프로세스 수: 오랫동안 한 방향으로 계속 증가하면 자원 회수 누수를 의심
- 단위 시간당 산출량: 속도 제한이 처리량을 눌러 버리는지 판단
- 네트워크 출구 실패율: 노드 묶음을 교체할지 결정
이 지표들 중 하나라도 오랫동안 한 방향으로 계속 변한다면 먼저 자원 회수와 재시도 로직을 확인해야 합니다.
환경 계층을 독립시켜야 한다
이 문제들을 함께 보면 같은 결론에 도달합니다. 환경 계층은 스크립트와 독립적으로 관리해야 합니다. 환경 풀링을 하려면 각 스크립트에 흩어 두는 대신 중앙에서 환경을 스케줄링할 수 있어야 합니다. 자원 회수를 위해서는 스크립트 자체의 예외 처리에 기대지 않고 상태를 조회할 수 있어야 합니다. 다른 환경이나 다른 출구로 바꿔 재시도하는 것도 환경을 독립적으로 스케줄링할 수 있을 때만 가능합니다.
스크립트는 로직을 담당하고 환경 계층은 자원과 ID를 담당합니다. PurpleMark는 이런 아키텍처에서 바로 이 계층을 맡아, 일괄 생성할 수 있고 독립적인 네트워크 출구에 연결할 수 있으며 상태를 조회할 수 있는 환경 자원을 제공합니다.
컴플라이언스 경계
규모를 키울 수 있다고 해서 데이터를 임의로 수집해도 된다는 뜻은 아닙니다. 대상 사이트의 robots 규칙과 서비스 약관을 지키고, 개인정보를 수집하지 말고, 기술적 보호 조치를 우회하지 말며, 상대 서비스의 정상 운영에 영향을 주지 않도록 요청 빈도를 제어해야 합니다. 안정성은 기술적 문제이고, 수집이 허용되는지는 또 다른 문제입니다. 두 조건을 모두 충족해야 합니다.


