4편에서
네이버 크롤링 물량을 field=1로 88% 줄였다. 그런데 이 시점에 이미 알고
있던 사실이 하나 더 있었다 — 직접 IP가 막혀 있는 순간에도 프록시
IP는 바로 뚫린다는 것. 두 IP가 서로 독립된 요청 예산을 갖는
것처럼 보였다. 지금까지는 그중 하나(직접, 막히면 프록시로 폴백)만
순차로 썼는데, “둘 다 동시에 쓰면 최소 두 배는 빨라지지 않을까”
라는 제안이 나왔다.
청크를 두 IP로 나눠 동시에 — Python 기본 인자값 캡처 함정(monkeypatch 무력화)
날짜구간(청크) 목록을 홀수/짝수로 나눠서, 짝수 구간은 직접 IP를 우선으로 쓰고 막히면 프록시로 폴백하고, 홀수 구간은 반대로 프록시를 우선으로 쓰고 막히면 직접으로 폴백하게 만들었다. 두 그룹을 스레드 두 개로 동시에 돌리는 구조다. 각 구간 안의 페이지네이션, 딜레이, 재시도 로직은 그대로 재사용했다 — 코드 중복 없이, 어느 쪽이 “우선”인지만 뒤집을 수 있게 만든 것이다.
구현하다가 함정을 하나 밟았다. 처음엔 “어느 함수를 먼저 쓸지”를
함수 인자의 기본값으로 직접 넣었는데, 그러면 기존 테스트들이
가짜 함수로 바꿔치기(monkeypatch)해도 반영이 안 됐다. 파이썬의
기본 인자값은 함수가 정의되는 시점에 딱 한 번 평가돼서 그 값이
고정돼버리기 때문이다 — 그 뒤에 모듈 전역을 바꿔치기해도 이미
캡처된 참조는 그대로다. 기본값을 None으로 두고, 함수 본문
안에서 그때그때 이름을 다시 찾아 쓰는 방식으로 고쳤다.
진단 규모 검증 — 완벽했다
실제로 돌려봤다. 2개월치 기간(날짜구간 2개 → 레인 2개에 정확히
하나씩), field=1이 적용된 상태로. 431건, 86.7초, 에러도
재시도도 0건. 흠잡을 데 없는 결과였다.
실전 스케일로 옮기니, 문제가 훨씬 커져서 돌아왔다
이 개선(딜레이+백오프, 프록시 폴백, field=1, 두 IP 병렬화)을
전부 확인한 뒤, 진단용 2개 타깃이 아니라 실제 운영 가이드(21개
타깃, 203개 키워드) 전체를 돌려봤다. 메일 발송만 뺀 채로, 사실상
진짜 리포트를 만드는 시도였다.
16분 33초 만에 “직접+프록시 둘 다 실패” 764건, 폴백 성공은 62건뿐이었다. 진단 규모에서는 거의 발동조차 안 하던 케이스가 급증했다. 더 진행해봐야 데이터 손실만 쌓일 것 같아 멈췄다.
동시성을 늘렸는데, 왜 다시 막혔나 — 네이버 크롤링 IP 차단 재발
파이프라인은 타깃을 10개씩 동시에 처리한다. 그런데 각 타깃 내부에서도 이번에 만든 병렬화가 청크를 2개 레인으로 나눠 동시 실행한다. 즉 실전 스케일에서는 최대 10개 타깃 × 레인 2개 = 최대 20개의 동시 요청이 단 두 개의 IP(직접, 프록시)에 몰릴 수 있는 구조였다. 진단(타깃 2개, 최대 4개 동시)에서 검증한 재시도 예산(2회, 5초/10초 백오프)이, 이 정도 규모의 동시 부하에서는 버티기에 부족했던 것이다.
“둘 다 실패”가 뜨면 그 페이지의 나머지 페이지네이션이 통째로 끊긴다 — 남은 결과가 조용히 유실된다는 뜻이다. 이대로 완주했다면 데이터 누락이 상당했을 것이다.
남는 생각
진단 규모에서 완벽했던 해법이, 그대로 실전 규모로 옮기니 통하지 않았다. 둘의 차이는 코드가 아니라 순전히 “몇 개가 동시에 도는가” 였다. 작은 표본에서 완벽하게 통과한 테스트는, 그 표본의 크기 자체가 결론의 일부라는 걸 이번에 다시 확인했다. 두 배 빨라졌다는 결론은 여전히 맞았지만, 그 결론이 성립하는 조건(“최대 4개 동시”) 을 실전 조건(“최대 20개 동시”)까지 그대로 늘려 적용할 수는 없었다.