데이터 팀에게 웹 수집 비용을 물으면 기가바이트, 요청 수, 크레딧, 성공률 이야기가 나온다. 재무팀에게 알고 싶은 것을 물으면 대답은 더 단순하다. 사용 가능한 레코드 하나의 비용은 얼마이고, 그것이 오르고 있는가 내리고 있는가. 이 두 대화는 좀처럼 만나지 않는데, 엔지니어가 추적하는 숫자는 파이프라인을 설명할 뿐 그것이 만들어내는 결과물을 설명하지 않기 때문이다.
깨끗한 레코드당 비용(cost per clean record)은 그 간극을 메운다. 이는 수집 작업의 총비용을 검증을 통과한 레코드 수로 나눈 값으로, 실제로 보고서, 모델, 또는 고객 대상 제품에 넣을 만한 레코드를 말한다. 이 가이드는 이 지표를 제대로 정의하고, 실제 비용이 어디로 가는지 보여주고, 예시를 통해 계산 과정을 살펴보고, 이를 움직이는 레버들의 순위를 매긴다.
핵심 요약
- 깨끗한 레코드당 비용을 측정하라. 총비용을 요청 수나 HTTP 성공 수가 아니라 콘텐츠 검증을 통과한 레코드 수로 나눈 값이다.
- 모니터링 작업의 경우 유효 변경당 비용(cost per useful change)을 추가하라. 대부분의 재요청은 아무것도 변하지 않았음을 확인할 뿐이므로, 변경 하나의 비용이 레코드 하나의 비용보다 훨씬 클 수 있다.
- 과금 방식이 어떤 낭비가 뼈아픈지를 결정한다. 대역폭 과금은 무거운 페이지에 불리하고, 성공 건당 과금은 불필요한 페치에 불리하다.
- 중간값 홈페이지의 무게는 약 2.5MB이며, 그중 HTML은 22KB에 불과하다. 파싱하는 부분만 페치하는 것은 대역폭 과금 수집에서 흔히 가장 큰 단일 절감 요인이다.
- 이 지표를 매달, 작업별로, 구성 요소와 함께 보고하라. 재무팀이 추적할 수 있는 숫자라야 예산을 받는 숫자가 된다.
지표를 신중하게 정의하기
세 가지 정의가 대부분의 역할을 한다.
**총비용(Total cost)**은 작업이 소비한 모든 것이다. 프록시 대역폭 또는 API 크레딧, 컴퓨팅, 스토리지, 그리고 솔직히 말하면 이를 계속 운영하는 데 든 엔지니어링 시간까지 포함된다.
**깨끗한 레코드(A clean record)**는 검증을 통과한 것이다. 필수 필드가 존재하고, 값이 타당한 범위 안에 있으며, 차단 페이지나 챌린지, 빈 껍데기가 아니라 실제로 요청한 페이지여야 한다. HTTP 200 응답은 이런 검사를 통과하기 전까지는 깨끗한 레코드가 아니다. 이 둘 사이의 간극은 침묵하는 실패율(the silent failure rate)에서 다룬 주제다.
**유효 변경(A useful change)**은 모니터링에서 중요하다. 가격을 매일 재페치하는데 한 달에 두 번만 변한다면, 나머지 28일 동안 지불한 레코드는 새로운 것을 아무것도 확인해주지 않은 셈이다. 유효 변경당 비용은 모니터링이 실제로 무엇을 위한 것인지 포착한다.
어떻게 과금되는지 알기
같은 파이프라인이라도 과금 방식에 따라 비싸질 수도 저렴해질 수도 있는데, 각 방식이 서로 다른 것을 계산에 넣기 때문이다.
| 과금 방식 | 지불 대상 | 비용을 높이는 요인 |
|---|---|---|
| 레지덴셜 프록시 대역폭 | 게이트웨이를 통과하는 바이트 | 무거운 페이지, 렌더링, 데이터를 전송하는 재시도 |
| 스크래핑 API 크레딧 | 성공한 응답 | 불필요했던 페치 |
Shifter의 레지덴셜 게이트웨이에서는 헤더를 포함해 송수신되는 모든 바이트가 계산되며, 오류 전에 바이트가 전송되었다면 실패한 요청도 계산에 포함된다. Web Scraping API에서는 JavaScript 렌더링 활성화 여부와 무관하게 성공한 요청은 크레딧 1개가 소요되고, 실패한 요청과 대상 측 오류는 비용이 들지 않으며, 한 번의 호출 안에서 이루어지는 자동 재시도는 그 단일 호출로 계산된다. 따라서 대역폭 과금에서는 모든 이미지와 스크립트를 불러오는 렌더링된 페이지가 HTML보다 훨씬 비쌀 수 있지만, 성공 건당 과금에서는 비용이 동일하다.
용량 격차는 크다. HTTP Archive의 2025 Web Almanac에 따르면 중간값 홈페이지의 무게는 데스크톱에서 2.86MB, 모바일에서 2.56MB였고, 중간값 HTML은 두 경우 모두 22KB였다. HTML만, 혹은 그 뒤의 JSON 엔드포인트만 파싱한다면 완전히 렌더링된 페이지 바이트의 대부분은 아무 소용이 없다.
계산 예시
한 달 동안의 모니터링 작업을 생각해보자. 아래 숫자는 계산 방식을 보여주기 위한 예시이며, GB당 $3.00라는 요율도 예시를 위한 어림값일 뿐 견적이 아니다.
| 입력 | 값 |
|---|---|
| 전송된 요청 수(재시도 포함) | 120,000 |
| 요청당 평균 바이트 | 450 KB |
| HTTP 수준 성공 | 108,000 |
| 검증을 통과한 레코드 | 97,000 |
| 이전 복사본과 다른 유효 레코드 | 6,800 |
| 대역폭 요율 | GB당 $3.00 |
| 컴퓨팅 | $40 |
아래 계산기를 돌리면 다음 결과가 나온다.
| 지표 | 값 |
|---|---|
| 총비용 | $202.00 |
| 요청당 비용 | $0.0017 |
| 깨끗한 레코드당 비용 | $0.0021 |
| 유효 변경당 비용 | $0.0297 |
| 침묵하는 실패율 | 10.2% |
| 깨끗한 레코드당 바이트 | 약 557 KB |
두 가지가 두드러진다. 유효 변경 하나의 비용이 깨끗한 레코드 하나의 비용보다 약 14배 높은데, 대부분의 페치가 아무것도 움직이지 않았음을 확인할 뿐이기 때문이다. 그리고 HTTP 성공 열 건 중 하나는 아무 쓸모없는 결과를 만들었다.
이제 한 가지만 바꿔보자. 전체 페이지 렌더링을 멈추고 파서가 필요로 하는 HTML이나 JSON만 페치하여, 요청당 평균을 450KB에서 60KB로 줄인다. 나머지는 모두 동일하다. 총비용은 $61.60으로 떨어지고, 깨끗한 레코드당 비용은 $0.0006으로, 유효 변경당 비용은 $0.0091로 떨어진다. 페이지를 페치하는 방식 하나만 바꿔서 3분의 2 이상 절감된 것이다.
계산기는 몇 줄이면 충분하다.
from dataclasses import dataclass
@dataclass
class Run:
requests: int # every request sent, including retries
bytes_transferred: int # everything through the proxy, failures included
responses_ok: int # HTTP-level successes
records_valid: int # records that passed content validation
records_changed: int # valid records that differed from the last copy
price_per_gb: float # your plan's rate
compute_cost: float = 0.0
people_cost: float = 0.0 # engineering time spent on this job, if you count it
def unit_economics(run):
bandwidth = run.bytes_transferred / 1e9 * run.price_per_gb
total = bandwidth + run.compute_cost + run.people_cost
return {
"total_cost": round(total, 2),
"cost_per_request": round(total / max(1, run.requests), 5),
"cost_per_clean_record": round(total / max(1, run.records_valid), 4),
"cost_per_useful_change": round(total / max(1, run.records_changed), 4),
"silent_failure_rate": round(1 - run.records_valid / max(1, run.responses_ok), 3),
"bytes_per_clean_record": int(run.bytes_transferred / max(1, run.records_valid)),
}
크레딧 과금 작업의 경우, 대역폭 항목을 성공한 응답 수에 크레딧당 가격을 곱한 값으로 바꾸면 된다. 나머지 계산은 동일하다.
레버들, 영향력 순서대로
1. 변하지 않은 것을 다시 페치하지 말라. 모니터링에서 변하지 않은 재페치는 대개 가장 큰 비용 항목이다. 각 페이지가 실제로 얼마나 자주 변하는지에 따라 방문 일정을 짜고, 사이트가 지원하는 곳에서는 조건부 요청을 활용하면 변경 사항을 놓치지 않으면서도 페치를 줄일 수 있다. 이 방법론은 비용 인식 크롤 스케줄링(cost-aware crawl scheduling)에 정리되어 있으며, 실제 변경과 노이즈를 구분하는 방법은 대규모 변경 감지(change detection at scale)에 있다.
2. 페이지당 더 적게 페치하라. 대역폭 과금에서는 데이터가 필요로 하지 않는 한 렌더링을 피하고, 렌더링이 불가피할 때는 이미지, 폰트, 미디어를 차단하며, JSON 엔드포인트를 선호하고 압축을 유지하라. 전체 목록은 프록시 대역폭 비용 절감하기(cutting proxy bandwidth costs)에 있다.
3. 침묵하는 실패를 고쳐라. 성공으로 통과되는 모든 차단 페이지, 챌린지, 빈 껍데기는 좋은 레코드와 동일한 비용이 들면서 아무것도 만들어내지 못한다. 콘텐츠를 검증하고, 대상별로 실패를 집계하며, 그런 실패를 유발하는 대상을 고치거나 속도를 늦춰라.
4. 안정적인 소스에서 추출하라. 유지보수는 실제 비용이다. 재설계될 때마다 깨지는 선택자는 엔지니어링 시간을 소모하며, 그래서 구조화된 데이터는 단발성으로 볼 때보다 1년 단위로 보면 더 저렴하다. 자세한 내용은 HTML 파싱을 그만하라(stop parsing HTML)를 참고하라.
5. 과금 방식을 대상에 맞춰라. 반드시 렌더링해야 하는 무거운 페이지는 성공 건당 과금이 더 저렴할 수 있고, 가벼운 HTML 또는 JSON 대상은 대개 대역폭 과금이 더 저렴하다. 혼합된 포트폴리오는 종종 둘 다 사용한다. 더 폭넓은 트레이드오프는 웹 스크래핑 인프라의 구축 대 구매(build vs buy for web scraping infrastructure)에서 다룬다.
재무팀에 보고하기
지표는 일관되게 보고될 때만 의미가 있다. 매달 한 번, 작업별로 다음을 공개하라.
- 대역폭 또는 크레딧, 컴퓨팅, 그리고 (계산에 넣는다면) 인건비로 나눈 총비용.
- 깨끗한 레코드 수와 깨끗한 레코드당 비용.
- 모니터링 작업의 경우, 유효 변경 수와 유효 변경당 비용.
- 침묵하는 실패율과 깨끗한 레코드당 바이트, 두 가지 선행 지표로서.
- 지난달 대비 주요 변화와 그 이유.
한 분기 동안 이를 유지하면, 웹 데이터는 불투명한 인프라 항목에서 예산을 세우고 다른 곳에서 데이터를 구매하는 것과 비교하고 근거를 댈 수 있는 단위 비용으로 바뀐다.
결론
기가바이트와 요청 수는 노력을 설명한다. 재무팀이 신경 쓰는 것은 결과물이다. 사용 가능한 레코드 하나의 비용, 그리고 모니터링의 경우 실제 변경 하나의 비용이다. 상태 코드가 아니라 검증을 기준으로 깨끗한 레코드를 정의하고, 작업이 발생시키는 모든 비용을 집계하며, 그 결과를 매달 작업별로 보고하라.
레버는 좀처럼 특별하지 않다. 아무것도 변하지 않는 곳은 덜 자주 페치하고, 페이지당 덜 페치하며, 성공한 것처럼 보이지만 그렇지 않은 페이지에 비용을 지불하지 말고, 깨지지 않는 소스에서 추출하라. 각각은 방 안의 모두가 읽을 수 있는 하나의 숫자에 직접 나타난다.
출처 및 참고 자료
- HTTP Archive, Web Almanac 2025: Page Weight. 2025년 7월 크롤 기준 중간값 페이지 및 HTML 무게.
- Shifter, 레지덴셜 프록시 대역폭 및 과금(Residential Proxies bandwidth and billing) 및 Web Scraping API 오류 및 제한(Web Scraping API errors and limits) 문서.