ArchitectureScalingNode.js

수천만 건의 스크린샷을 찍어내는 무한 확장 아키텍처

5 min read
수천만 건의 스크린샷을 찍어내는 무한 확장 아키텍처

소개

안정적인 스크린샷 서비스를 만드는 건 생각보다 훨씬 빡셉니다. 저희가 SiteSnapshot을 처음 만들 땐, 그냥 Puppeteer 띄워서 스크린샷만 찍으면 되는 줄 알았습니다. 그게 얼마나 순진한 생각이었는지 깨닫는 데는 오래 걸리지 않았죠.

우리가 직면한 문제들 (일명 '삽질')

요즘 웹사이트들은 무겁습니다. 이걸 서버에서 렌더링하려면 CPU와 메모리가 녹아납니다.

  • 메모리 누수: 헤드리스 브라우저를 오래 켜두면 메모리를 끝도 없이 잡아먹습니다.
  • 좀비 프로세스: 브라우저가 죽지 않고 살아서 리소스만 축내는 유령이 됩니다.
  • 확장성 이슈: 사용자가 몰릴 때 서버 한 대로는 감당이 안 됩니다.

이 글에서는 우리가 이 지옥 같은 문제들을 어떻게 해결했는지 '기술적'으로 파헤쳐 봅니다.

해결책: 분산 워커 아키텍처 (Distributed Worker Model)

우리는 기존의 '모놀리식 크론(Cron)' 방식(한 서버가 다 하는 방식)을 버리고, **"일꾼(Worker)"**들이 나눠서 처리하는 구조로 바꿨습니다.

// 워커가 큐(Queue)에서 일감을 가져가는 로직(예시)
async function processQueue(jobId) {
  const browser = await getBrowserInstance();
  try {
    const page = await browser.newPage();
    await page.goto(job.url); // 사이트 접속
    await page.screenshot({ path: job.path }); // 찰칵!
  } finally {
    await browser.close(); // 중요: 꼭 닫아줘야 메모리가 안 샙니다
  }
}

핵심 3대장

  1. 큐 매니저 (Queue Manager): 일감(스캔 요청)을 관리하고 워커들에게 공평하게 나눠줍니다.
  2. 워커 노드 (Worker Nodes): 실제로 브라우저를 띄워 스캔만 하는, 상태가 없는(Stateless) 컨테이너들입니다. 필요하면 100개, 1000개로 늘릴 수 있습니다.
  3. 스토리지 (Storage): 데이터는 Supabase에, 이미지는 비용이 저렴한 S3(오브젝트 스토리지)에 저장합니다.

결론

"스케줄 관리"와 "실제 작업"을 분리한 덕분에, 서버가 터지지 않고 99.9%의 안정성을 달성했습니다. 트래픽이 폭주해도 워커만 더 띄우면 되니까요.

당신의 서비스도 시각적으로 안전하게 지키고 싶나요? 지금 무료로 시작해보세요.