본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 05. 27. 12:41

1688 상품 이미지 대량 다운로드: 대역폭(Bandwidth) 최대치 도달에 관한 교훈

요약

1688 상품 이미지 대량 다운로드 과정에서 발생한 대역폭 포화 문제를 해결한 사례를 다룹니다. 동시성 제어와 속도 제한이 없는 스크립트가 시스템 전체에 미치는 영향을 분석하고, Guzzle과 토큰 버킷 알고리즘을 이용한 안정적인 다운로드 설계 방안을 제시합니다.

핵심 포인트

  • 무분별한 동시 요청은 네트워크 대역폭을 포화시켜 서비스 중단을 초래함
  • Guzzle의 Pool을 활용한 동시성 제어(Concurrency Control)의 중요성
  • 토큰 버킷 알고리즘을 통한 안정적인 대역폭 제한 구현
  • 지수 백오프(Exponential Backoff)를 적용한 재시도 메커니즘 구축

구매 시스템이 갑자기 중단되었습니다. 모니터링 결과, 아웃바운드 대역폭(Outbound Bandwidth)이 500Mbps로 최대치에 도달하여 모든 외부 API 요청이 타임아웃(Timeout)을 발생시키고 있었습니다. 원인은 1688 상품 이미지를 대량으로 다운로드하는 스크립트였습니다. 이 스크립트는 속도 제한(Rate Limiting) 없이 200개의 동시 다운로드 스레드(Thread)를 실행하여, 공유 대역폭을 완전히 포화 상태로 만들었습니다.

문제 시나리오: 이미지 다운로드에 대한 무식한 접근 방식

우리는 메인 이미지와 상세 이미지를 포함하여, 제품당 평균 5장의 이미지를 포함한 약 3,000개의 1688 상품을 매일 동기화해야 했습니다. 초기 구현 방식은 단순했지만 조잡했습니다:

// 기존의 무차별 대입(Brute-force) 다운로드 스크립트
function downloadAllImages($productIds) {
    foreach ($productIds as $id) {
...

이 스크립트에는 세 가지 치명적인 문제가 있었습니다:

  1. 동시성 제어(Concurrency Control) 부재: file_get_contents는 동기적(Synchronous)이지만, 외부 루프에 제한이 없어 엄청난 양의 HTTP 요청이 동시에 발사되었습니다.
  2. 재시도 메커니즘(Retry Mechanism) 부재: 이미지 다운로드에 실패하면(예: 네트워크 지터(Network Jitter)), 스크립트는 단순히 해당 이미지를 건너뛰어 상품 이미지가 누락되는 결과를 초래했습니다.
  3. 대역폭 제한(Bandwidth Limiting) 부재: 200개의 요청이 동시에 다운로드를 진행하며 각각 평균 2MB를 소모하자, 즉시 400MB의 대역폭을 점유했습니다.

즉각적인 결과로 주문 처리 및 물류 조회 등 다른 모든 비즈니스 운영이 18분 동안 중단되었습니다. 우리는 수동으로 프로세스를 종료해야 했고, 실패한 이미지를 다시 다운로드하는 데 2시간을 소비해야 했습니다.

해결책: 속도 제한과 큐(Queue)를 갖춘 다운로더

우리는 Guzzle의 비동기(Async) 기능을 사용하여 대역폭 제어와 재시도 메커니즘을 추가한 새로운 다운로더를 설계했습니다.

첫 번째 단계: 최대 동시성 제한(Maximum Concurrency Limit)이 있는 Guzzle의 동시 요청 풀(Concurrent Request Pool)을 사용합니다.
두 번째 단계: 대역폭 제어를 위해 간단한 토큰 버킷(Token Bucket) 알고리즘을 구현합니다.

// 새로운 속도 제한(Rate-limited) 다운로더
use GuzzleHttp\Client;
use GuzzleHttp\Pool;
...

주요 개선 사항:

  • 동시성 제어 (Concurrency control): concurrency를 10으로 설정하여 즉각적인 대역폭 포화 방지
  • 토큰 버킷 속도 제한 (Token bucket rate limiting): consumeBandwidth 메서드를 통해 다운로드가 초당 50MB를 초과하지 않도록 보장
  • 지수 백오프 재시도 (Exponential backoff retry): 실패 시 2^i초 동안 대기하며, 최대 3회까지 시도

교훈: 대역폭 재앙에서 안정적인 동기화까지

새로운 다운로더를 배포한 후 A/B 테스트를 진행했습니다. 기존 스크립트는 3,000개의 상품(약 15,000개의 이미지) 이미지를 다운로드하는 데 12분이 걸렸지만, 500Mbps의 대역폭을 소모했습니다. 새로운 스크립트는 동일한 작업에 18분이 소요되었으나, 대역폭은 45-50Mbps로 안정적으로 유지되었으며 다른 서비스에 미치는 영향은 전혀 없었습니다.

추가 최적화: 증분 다운로드 및 캐싱

기존 이미지를 다시 다운로드하지 않도록 간단한 파일 해시 체크 기능도 추가했습니다:

// 증분 체크 - 새로운 이미지만 다운로드
function needsDownload($url, $localPath) {
    if (!file_exists($localPath)) {
...

매일 상품 이미지의 약 10%만 업데이트되기 때문에, 이 최적화를 통해 일일 증분 동기화 시간을 18분에서 3-5분으로 단축했습니다.

요약: 제3자 리소스를 대량으로 다운로드할 때,

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0