Node.js에서 Promise.all 대신 p-limit로 동시성 제어하기

문제 상황

배치 작업으로 외부 API를 호출해 3000개 상품 정보를 갱신하는 스크립트를 작성했다. Promise.all로 모든 요청을 한 번에 날렸더니 서버가 응답하지 않았고, 결국 타임아웃으로 실패했다.

const productIds = [...]; // 3000개
const results = await Promise.all(
  productIds.map(id => fetchProductInfo(id))
);

원인

3000개의 HTTP 요청이 동시에 발생하면서 소켓이 고갈되고, 상대 서버도 rate limit에 걸렸다. Node.js의 기본 소켓 풀 제한도 문제였다.

해결

p-limit 라이브러리로 동시 실행 수를 제한했다.

const pLimit = require('p-limit');

const limit = pLimit(10); // 동시에 10개만
const results = await Promise.all(
  productIds.map(id => limit(() => fetchProductInfo(id)))
);

10개씩 처리하도록 제한하니 안정적으로 완료됐다. 전체 소요 시간은 늘었지만, 실패 없이 모든 데이터를 가져올 수 있었다.

추가 개선

재시도 로직도 추가했다. p-retry와 조합하면 더 견고해진다.

const pRetry = require('p-retry');

const fetchWithRetry = (id) => 
  pRetry(() => fetchProductInfo(id), { retries: 3 });

const results = await Promise.all(
  productIds.map(id => limit(() => fetchWithRetry(id)))
);

동시성 제어는 생각보다 중요하다. 특히 외부 API를 다룰 때는 필수다.