뉴스 기사 수집 파이프라인을 설계하면서 제가 가장 먼저 내린 결정은 "Puppeteer나 Playwright 같은 동적 스크래핑은 배제한다"는 것이었습니다.
하지만 이 결정은 단순히 구현을 줄이기 위한 편의상의 선택이 아니었습니다. 일부 사이트는 수집할 수 없고, JavaScript로 렌더링되는 컨텐츠는 포기해야 하며, 구조 변경 시 대응 수단이 제한된다는 점을 명확히 인지한 상태에서 내린 전략적 선택이었습니다. 이 글에서는 그 대가가 무엇이었고, 이를 어떻게 감당하려 했는지, 그리고 왜 그럼에도 이 선택이 합리적이었는지를 정리해 보고자 합니다.
정적 스크래핑(axios + cheerio)만 사용할 경우, React나 Vue 등으로 만들어진 SPA 기반 언론사 사이트에서는 본문을 가져올 수 없습니다.
private getSelectorMap(): Record<string, string[]> {
return {
'yna.co.kr': ['article', '.story-news', '.article'], // ✅ SSR
'chosun.com': ['.article-body', 'section.article-body'], // ✅ SSR
// CSR 기반 사이트는 과감히 제외하여 파이프라인 안정성 확보
};
댓글이나 관련 기사 등 스크롤 시 로드되는 부가 데이터는 수집 범위에서 제외되었습니다.
정적 스크래핑을 선택하더라도, 수만 건 이상의 데이터를 처리할 때는 기술적 장벽이 존재합니다.
가장 큰 이유는 Node.js 런타임 특성 때문입니다. 싱글 스레드 기반인 Node.js에서 브라우저 렌더링 작업을 수행하면 CPU와 메모리 사용량이 급격히 증가하여 전체 시스템의 응답성이 저하됩니다.
동적 크롤링에서 발생하는 주요 CPU 사용은 함께 실행되는 브라우저 프로세스에서 발생한다고 합니다.
근데, 결국 이 브라우저 프로세스는 node.js와 동일한 서버 자원을 공유하기 때문에, 결과적으로 node.js 기반 파이프라인 전체의 처리량과 안정성에 영향을 미치게 됩니다.
특히 프로젝트가 단일 서버 환경이기 때문에 이런 자원 경쟁이 이벤트 루프 지연을 일으킬 수 있기 때문에, 동적 크롤링을 채택하는것이 효율이 떨어진다고 판단했습니다.
동적 스크래핑을 포기한 대신, 정적 파이프라인의 신뢰성을 극대화하는 데 집중했습니다.
단시간에 과도한 요청이 발생할 경우 안티 크롤링 차단이나 네트워크 오류가 발생할 수 있기 때문에, Node.js의 비동기 특성을 활용하면서 동시 처리 요청 수를 제한하는 방식을 선택했습니다.
private readonly concurrencyLimit = 5;
for (let i = 0; i < articles.length; i += this.concurrencyLimit) {
const chunk = articles.slice(i, i + this.concurrencyLimit);
await Promise.allSettled(chunk.map(...));
await this.delay(500);
}
실패 원인을 상세히 분류하여 어떤 문제가 발생했는지 즉각적으로 파악할 수 있게 했습니다.
private classifyError(error: any): string {
if (axios.isAxiosError(error)) {
const status = error.response?.status;
if (status === 429) return 'RATE_LIMITED';
if (error.code === 'ECONNABORTED') return 'TIMEOUT';
}
if (error.message?.includes('빈 본문')) return 'EMPTY_CONTENT';
return 'UNKNOWN';
}
수집된 데이터는 즉시 메시지 큐로 전달하여, 수집 단계와 후속처리 단계를 분리했습니다.
중복 데이터는 큐 레이어에서 차단함으로써,
불필요한 후속 처리 비용을 줄이고 효율을 높였습니다.
현재 설계의 한계를 인지하고 있으며, 서비스 성장에 따라 다음과 같은 확장 로드맵을 고민해봤습니다.
이 프로젝트의 목표는 모든 페이지를 무조건 수집하는 것이 아니라, "Node.js 환경에서 지속 가능한 수집 파이프라인을 설계하는 것"이었습니다.
동적 스크래핑을 선택하지 않음으로써 일부 데이터는 포기해야 했지만, 대신 예측 가능한 리소스 사용량과 안정적인 시스템을 유지할 수 있었습니다. 현재 이 설계로 선정된 언론사에서 평균 95% 이상의 수집 성공률을 달성하고 있으며, 한 차례에 4,000건 이상을 안정적으로 처리하고 있습니다.