Chromium은 대략 280MB 정도 됩니다. 그런데 AWS Lambda의 압축 해제 후 패키지 한도는 250MB입니다. npm install puppeteer를 해 놓고 그 상태 그대로 Lambda에 올려 본 적이 있다면, 이 계산이 어떤 결말로 이어지는지는 이미 몸소 겪어 보셨을 겁니다. 안 됩니다.
저는 새벽 2시에 “Failed to launch the browser process” 오류를 붙잡고 씨름한 경험이 꽤 있어서, 이 주제는 한 가지 방법만 던져 주고 나머지는 알아서 감으로 넘기라는 식의 튜토리얼로는 부족하다고 생각했습니다. 그래서 이 글에서는 Layers, Container Images, 직접 ZIP 업로드 방식을 하나씩 비교하고, 2026년 기준으로도 그대로 쓸 수 있는 버전 호환성 표와 함께, 실제로 가장 자주 마주치게 될 다섯 가지 오류에 대한 트러블슈팅까지 정리했습니다.
AWS Lambda에서 Puppeteer를 쓴다는 건 무엇이고, 왜 써야 할까?
Puppeteer는 Chrome DevTools Protocol을 통해 headless Chromium을 제어하는 Node.js 라이브러리입니다. Lambda는 AWS의 서버리스 컴퓨팅 서비스로, 호출한 만큼만 비용을 내고 자동으로 확장되며 서버 관리는 전혀 신경 쓸 필요가 없습니다. 이 둘을 함께 쓰면 EC2 인스턴스를 직접 하나도 띄우지 않고도, 수백 개 병렬 실행까지 확장 가능한 브라우저 자동화 환경을 만들 수 있습니다.
팀마다 쓰는 방식은 꽤 비슷합니다. 웹 스크래핑, 스크린샷 및 PDF 생성, 합성 모니터링, SEO를 위한 SPA 사전 렌더링, 자동화 UI 테스트 등이 대표적입니다. 문제는 늘 앞서 말한 그 지점입니다. Chromium의 덩치와 Lambda 패키지 제한이 정면으로 충돌한다는 거죠. 그래서 사람들은 Chromium 다운로드까지 몽땅 포함된 풀 패키지 puppeteer를 Lambda에 그대로 올리지 않습니다. 대신 브라우저가 들어 있지 않은 puppeteer-core와 Lambda에 맞게 손본 Chromium 바이너리를 함께 씁니다. 가장 많이 쓰는 조합은 @sparticuz/chromium입니다.
이 한 가지 교체, 즉 puppeteer 대신 puppeteer-core를 쓰는 것만으로도 배포 설정을 손대기 전부터 용량 문제의 80%는 정리됩니다.
Layers vs. Container Image vs. ZIP: 먼저 배포 방식을 고르자
조사하면서 제일 답답했던 점이 하나 있었습니다. 기존 가이드들은 거의 전부 배포 방식 하나만 다룬다는 겁니다. AWS SAM 튜토리얼은 Layers를, CDK 예시는 Docker를, 어딘가의 Substack 글은 S3에 올린 Chromium 바이너리를 쓰는 raw ZIP 방식을 소개합니다. 그런데 이 셋을 나란히 놓고 비교해 주는 곳은 거의 없어서, 정작 가장 먼저 정해야 할 질문인 “내 상황에는 어떤 배포 방식이 맞는가?”가 통째로 빠져 버립니다.
그래서 여기서 한 번 정리해 보겠습니다.
| 기준 | Lambda Layers | 컨테이너 이미지(Docker) | 직접 ZIP 업로드 |
|---|---|---|---|
| 최대 패키지 크기 | 250MB(압축 해제 후, 모든 레이어 합산) | 10GB 이미지 | 250MB(압축 해제 후) |
| 배포 복잡도 | 중간(레이어 ARN 관리 필요) | 높음(Dockerfile + ECR 푸시) | 가장 낮음(zip으로 묶어 업로드) |
| 콜드 스타트 영향 | 보통 | 약간 높음(더 큰 이미지 pull) | 보통 |
| Chromium 업데이트 방식 | 레이어 버전 재발행 | 이미지 재빌드 | ZIP 재업로드 |
| 추천 대상 | 빠른 프로토타입, Serverless Framework 사용자 | 운영 환경, Docker CI를 쓰는 팀 | 간단한 단발성 함수 |
| IaC 지원 | SAM, Serverless Framework | CDK, SAM, Terraform | 콘솔, 모든 IaC |
250MB와 10GB 제한은 모두 AWS 공식 Lambda 할당량 문서에 있는 값입니다. 숫자가 자주 바뀌는 편은 아니지만, 배포 전략 전체를 처음부터 결정하게 만드는 핵심 제약은 바로 이 숫자들입니다.
제가 실제로 쓰는 판단 기준은 이렇습니다. 프로토타이핑 중이거나 이미 Serverless Framework를 쓰고 있다면 Layers부터 시작하세요. 운영 배포가 목표고 팀에 Docker CI/CD가 이미 깔려 있다면 Container Image가 좋습니다. 10GB 한도 덕분에 여유가 훨씬 큽니다. 그냥 스크린샷 몇 장만 가끔 찍는 함수라면 직접 ZIP 업로드가 가장 간단합니다.
세 가지 방식 모두 내부적으로는 같은 핵심 의존성 조합을 씁니다. puppeteer-core + @sparticuz/chromium입니다. 배포 방식이 달라지는 건 이 조합을 어떻게 포장하느냐이지, 무엇을 담느냐가 아닙니다.

2026년 버전 호환성 표: 더 이상 추측하지 마세요
이 부분이 실제로 사람들의 시간을 몇 달씩 잡아먹습니다. Stack Overflow와 GitHub 이슈에서 가장 자주 나오는 불만은 “어떻게 배포하나요”가 아니라 “왜 잘 되던 배포가 npm 업데이트 후 조용히 깨졌나요”입니다. 원인은 대부분 @sparticuz/chromium, puppeteer-core, Node.js 런타임 사이의 불일치입니다.
먼저 중요한 점부터 짚고 가겠습니다. chrome-aws-lambda(원래 alixaxel 패키지)는 더 이상 유지보수되지 않습니다. Node 18 이상에서 문제가 있고 Chromium 릴리스 속도도 따라가지 못합니다. 관련 튜토리얼을 발견하면 바로 닫으세요. 너무 오래된 정보입니다. 이제는 모든 최신 가이드가 @sparticuz/chromium를 안내해야 합니다.
패키지 메이저 버전을 대충 맞추지 말고, 아래 원칙을 따르세요.
| 구성 요소 | 버전 규칙 | 배포 전 확인할 사항 |
|---|---|---|
puppeteer-core | 애플리케이션이 필요한 Puppeteer 버전을 선택 | 해당 Puppeteer 릴리스가 지원하는 Chromium 빌드 확인 |
@sparticuz/chromium | 메이저 버전은 Puppeteer가 아니라 Chromium 메이저를 따름 | Puppeteer 지원 표의 Chromium 빌드와 맞추고 Sparticuz 릴리스 노트 확인 |
| AWS Lambda Node.js 런타임 | 현재 지원되는 Lambda 런타임 사용 | 런타임이나 패키지 업데이트 후 반드시 호출 테스트 수행 |
| 아키텍처 | npm 패키지는 x64 바이너리를 포함하며, arm64 지원은 Chromium v135부터 arm64 레이어 또는 remote pack으로 시작 | Lambda 아키텍처, 레이어/팩 아티팩트, Chromium 버전을 정확히 일치시킬 것 |
제가 여기서 패키지 조합을 딱 잘라 박아 두지 않은 이유는 @sparticuz/chromium이 Chromium 릴리스 주기를 따라가고, 일반적인 시맨틱 버전 규칙을 그대로 쓰지 않기 때문입니다. 먼저 공식 Puppeteer Chromium Support 페이지에서 선택한 Puppeteer 릴리스가 지원하는 Chromium 메이저를 확인하세요. 그다음 그 메이저에 맞는 @sparticuz/chromium을 고르시면 됩니다. 마지막으로 Sparticuz 릴리스 노트를 읽고 패치 수준 변경과 아키텍처 정보를 확인하세요. 이 두 자료에서 서로 맞는다고 확인되지 않으면, 두 패키지에 같은 메이저 숫자를 무작정 맞춰 넣지 마세요.

Lambda Layers로 AWS Lambda에 Puppeteer 배포하기
Lambda Layer를 쓰면 Chromium을 함수 코드와 분리해서 패키징할 수 있어서, 실제 핸들러는 작게 유지하면서 여러 함수에서 같은 Chromium 레이어를 재사용할 수 있습니다. 이 분야에서는 가장 “빨리 시작하는” 방식에 가장 가까운 선택지입니다.
1단계: puppeteer-core와 -min 패키지 설치하기
Chromium 파일이 Lambda Layer에 있다면, 함수 패키지는 @sparticuz/chromium-min을 써서 최대한 작게 유지하세요. 아래 플레이스홀더는 앞에서 확인한 호환 버전으로 바꾸면 됩니다.
npm install puppeteer-core@$PUPPETEER_VERSION \
@sparticuz/chromium-min@$CHROMIUM_VERSION
여기서는 puppeteer가 아니라 puppeteer-core를 설치합니다. 자동 브라우저 다운로드를 건너뛰기 때문입니다. -min 패키지는 실행에 필요한 도우미만 제공하고, 레이어가 /opt/chromium 아래에 Brotli 압축된 Chromium 파일을 제공합니다.
2단계: Chromium Lambda Layer 만들기 또는 참조하기
공식 Sparticuz 릴리스에 포함된 아키텍처별 레이어 아카이브를 쓰거나, 공식 저장소에서 직접 빌드하세요. x86_64 Lambda의 경우 문서화된 빌드는 다음과 같습니다.
git clone --depth=1 https://github.com/sparticuz/chromium.git
cd chromium
make chromium.x64.zip
이렇게 하면 chromium.x64.zip이 만들어집니다. 이를 S3에 업로드한 뒤, 실제 사용하는 런타임과 아키텍처에 맞춰 Lambda Layer를 발행하세요. arm64라면 해당 arm64 릴리스 아티팩트나 빌드 타깃을 써야 합니다. x64 아카이브를 arm64 함수에 붙이면 안 됩니다.
SAM을 쓴다면 template.yaml에서 layer ARN을 직접 연결할 수 있습니다.
Resources:
PuppeteerFunction:
Type: AWS::Serverless::Function
Properties:
Layers:
- arn:aws:lambda:us-east-1:XXXXXXXXXXXX:layer:chromium-layer:1
3단계: Lambda 핸들러 작성하기
아래는 URL로 이동한 뒤 페이지 제목을 반환하는, 실제로 동작하는 핸들러 패턴입니다.
import puppeteer from "puppeteer-core";
import chromium from "@sparticuz/chromium-min";
export const handler = async () => {
const browser = await puppeteer.launch({
args: puppeteer.defaultArgs({ args: chromium.args, headless: "shell" }),
executablePath: await chromium.executablePath("/opt/chromium"),
headless: "shell",
});
try {
const page = await browser.newPage();
await page.goto("https://example.com", { waitUntil: "domcontentloaded" });
return { title: await page.title() };
} finally {
await browser.close();
}
};
여기서 finally 블록을 꼭 보세요. 브라우저는 항상 여기서 닫아야 합니다. 그렇지 않으면 warm Lambda 환경에 좀비 브라우저 프로세스가 호출마다 쌓이고, 결국 실제 코드와는 상관없는 이상한 메모리 오류를 만나게 됩니다.
4단계: 메모리, 타임아웃, 아키텍처 설정하기
메모리는 최소 1024MB로 설정하세요. 스크린샷 정도가 아니라면 1536~2048MB를 권장합니다. 타임아웃은 최소 60초로 두세요. arm64 지원을 확실히 확인하지 않았다면 아키텍처는 x86_64로 고정하는 게 안전합니다. 이 부분은 Chromium 버전마다 달라질 수 있습니다.
5단계: 배포 및 테스트
sam build && sam deploy --guided
테스트 이벤트로 호출한 뒤, 조금이라도 이상하면 바로 CloudWatch Logs를 확인하세요. 아래 트러블슈팅 섹션의 오류 90%는 로그에 그대로 찍혀 있습니다.
Docker 컨테이너 이미지로 AWS Lambda에 Puppeteer 배포하기
컨테이너 이미지는 250MB라는 답답함을 10GB 한도로 한 번에 풀어 줍니다. 특히 팀이 이미 CI 파이프라인에서 Docker를 쓰고 있다면, 운영 환경에서는 대체로 이 방법이 더 낫습니다.
1단계: Dockerfile 만들기
Node.js용 공식 AWS Lambda 베이스 이미지에서 시작해 의존성을 설치하고 핸들러를 지정하세요.
FROM public.ecr.aws/lambda/nodejs:20
COPY package*.json ./
RUN npm install --production
COPY . .
CMD ["index.handler"]
Chromium 패키지에 따라서는 몇 가지 공유 라이브러리를 yum install로 추가해야 할 수도 있습니다(이 부분은 트러블슈팅 섹션에서 더 설명합니다). 다만 @sparticuz/chromium는 필요한 것 대부분을 같이 들고 있어서, Chrome 전체를 직접 설치하는 것보다 훨씬 수월합니다.
2단계: Amazon ECR에 빌드 후 푸시하기
aws ecr create-repository --repository-name puppeteer-lambda
docker build -t puppeteer-lambda .
docker tag puppeteer-lambda:latest <account-id>.dkr.ecr.<region>.amazonaws.com/puppeteer-lambda:latest
aws ecr get-login-password | docker login --username AWS --password-stdin <account-id>.dkr.ecr.<region>.amazonaws.com
docker push <account-id>.dkr.ecr.<region>.amazonaws.com/puppeteer-lambda:latest
이미지는 Lambda 함수와 같은 리전에 두세요. 리전이 다르면 이미지 pull 때 쓸데없는 지연이 생깁니다.
3단계: 컨테이너 이미지로 Lambda 함수 만들기
CLI나 CDK를 통해 함수가 ECR 이미지 URI를 바라보도록 설정하세요. 메모리(15362048MB)와 타임아웃(60120초)은 레이어 기반 배포와 같은 기준으로 잡으면 됩니다.
4단계: 배포 및 테스트
테스트 이벤트로 호출하고 결과를 확인하세요. Layers 방식과 비교했을 때 가장 큰 차이는 이미지가 더 크기 때문에 콜드 스타트가 약간 늘 수 있다는 점이지만, 그 대신 의존성에 훨씬 많은 여유가 생깁니다.
직접 ZIP 업로드로 AWS Lambda에 Puppeteer 배포하기
가장 간단한 방식입니다. 레이어도 없고, Docker 빌드도 없습니다. 프로토타입이나 전체 브라우저 자동화 플랫폼까지 확장할 필요가 없는 단일 함수에 잘 맞습니다.
1단계: 로컬에서 의존성 설치하기
자체 포함 ZIP을 만들려면 puppeteer-core + @sparticuz/chromium 조합을 쓰고 두 버전 모두 고정하세요. 풀 패키지는 압축된 Chromium 파일을 포함하고 있으며, 실행 시 /tmp에 풀립니다. @sparticuz/chromium-min은 해당 파일들이 Lambda Layer나 빠른 원격 pack URL을 통해 별도로 제공될 때만 쓰세요. -min 패키지 자체에는 Brotli 파일이 들어 있지 않습니다.
2단계: 함수 번들링 후 ZIP 만들기
npm install --production
zip -r function.zip . -x "*.git*"
여기서는 --production 플래그가 중요합니다. 개발용 의존성은 이유 없이 250MB 예산을 갉아먹습니다.
3단계: Lambda 함수 업로드 및 설정하기
aws lambda update-function-code --function-name my-puppeteer-fn --zip-file fileb://function.zip
ZIP이 50MB를 넘으면 콘솔이나 단순 CLI 호출로는 직접 업로드할 수 없습니다. 먼저 S3에 올린 뒤 S3 URI를 참조해야 합니다. 메모리, 타임아웃, 아키텍처 설정은 앞의 두 방식과 동일하게 맞추면 됩니다.
4단계: 배포 및 테스트
동일하게 호출 후 로그를 확인하는 흐름을 쓰면 됩니다. 풀 패키지를 사용할 때는 chromium.executablePath()에 인자를 넣을 필요가 없습니다. chromium-min을 쓸 때는 레이어 경로 또는 원격 pack URL을 정확히 넘겨야 합니다. 예를 들어 앞의 레이어 구조라면 chromium.executablePath("/opt/chromium")처럼 사용합니다. remote pack은 첫 콜드 스타트 때 다운로드 작업이 추가되므로, 함수와 가까운 곳에 호스팅하고 아티팩트 버전과 아키텍처를 반드시 검증하세요.
Lambda에서 실제로 동작하는 puppeteer.launch() 인자
다들 복붙하는 부분이니, 제대로 정리해 보겠습니다. Lambda 실행 환경에는 /dev/shm이 없고 GPU 접근도 없으며 권한도 제한됩니다. 즉, 로컬 노트북에서는 잘 되던 기본 puppeteer.launch() 호출이 여기서는 그냥 안 됩니다.
const viewport = {
width: 1920,
height: 1080,
deviceScaleFactor: 1,
isMobile: false,
hasTouch: false,
isLandscape: true,
};
const browser = await puppeteer.launch({
args: await puppeteer.defaultArgs({ args: chromium.args, headless: "shell" }),
executablePath: await chromium.executablePath(),
headless: "shell",
defaultViewport: viewport,
});
@sparticuz/chromium의 chromium.args 배열에는 서버리스 환경에 필요한 플래그가 이미 들어 있습니다. --no-sandbox, --disable-gpu, --disable-dev-shm-usage 같은 옵션들입니다. 직접 플래그 목록을 손으로 짤 이유가 없는 게 바로 이 패키지를 쓰는 핵심 이유입니다. Chromium의 요구 사항을 패키지가 따라가 주기 때문입니다.

트러블슈팅: 모든 개발자가 한 번쯤 겪는 5가지 오류
제가 찾아본 기존 가이드들에는 제대로 된 트러블슈팅 섹션이 거의 없었습니다. 그런데 오류야말로 이 글을 읽고 있을 가능성이 가장 큰 이유라는 점을 생각하면 꽤 이상한 일입니다.
"Failed to launch the browser process"
원인: 공유 라이브러리(libnss3.so, libatk 등)가 없거나 executablePath가 잘못됨.
해결: @sparticuz/chromium는 필요한 의존성 대부분을 함께 넣어 줍니다. 그래서 직접 Chromium 바이너리를 굴리는 것보다 이 패키지를 추천합니다. Docker 배포에서 여전히 이 문제가 난다면, Dockerfile에 빠진 라이브러리를 yum install로 명시적으로 추가하세요.
"Unzipped size must be smaller than 262144000 bytes"
원인: 풀 버전 puppeteer를 설치했기 때문입니다. 이 패키지는 자체 Chromium 다운로드까지 포함해서 용량이 약 400MB에 달합니다.
해결: puppeteer-core + @sparticuz/chromium으로 바꾸세요. 정말 더 큰 공간이 필요하다면 Container Image 방식으로 옮겨 10GB 한도를 쓰면 됩니다.
"Browser disconnected" 또는 browser.newPage() 타임아웃
원인: Lambda 메모리가 부족하거나, launch 인자에서 --disable-gpu 같은 플래그가 빠졌기 때문입니다.
해결: 메모리를 최소 1024MB 이상으로 올리고(아래 벤치마크 참고), chromium.args를 그대로 넘기고 있는지 확인하세요. 축약한 커스텀 목록은 피하는 게 좋습니다.
잘 되던 코드가 Lambda 런타임 업데이트 후 깨짐
원인: AWS가 주기적으로 런타임을 패치하면서 공유 라이브러리 버전이나 Node.js 패치 버전이 바뀔 수 있습니다.
해결: @sparticuz/chromium 버전을 명시적으로 고정하고, 함수 설정에서 Node 런타임 버전도 고정하세요. 그리고 많은 사람들이 건너뛰는 부분이지만, AWS가 런타임을 공지할 때마다 다시 테스트해야 합니다. 문제가 생겼을 때만이 아니라, 공지될 때마다입니다.
약 30초 후 "Protocol error: Connection closed"
원인: Lambda 타임아웃이 페이지 로드 및 렌더링에 걸리는 시간보다 짧음.
해결: 타임아웃을 60~120초로 늘리고, page.setDefaultNavigationTimeout()을 명시적으로 설정하세요. 또한 다음 단계로 넘어가기 전에 모든 네트워크 요청이 끝나기를 기다릴 필요가 없다면 waitUntil: 'networkidle0' 대신 waitUntil: 'domcontentloaded'를 사용하세요.
운영 환경을 위한 강화: 메모리, 콜드 스타트, 비용
대부분의 가이드는 “메모리를 늘리세요”에서 끝납니다. 그건 실행 가능한 조언이 아닙니다. 실제로 메모리를 올리면 무엇이 달라지는지 살펴보겠습니다.
메모리와 성능의 관계
Lambda는 메모리에 비례해서 CPU도 함께 할당합니다. 이 점을 놓치기 쉽습니다. 즉, 메모리를 늘린다는 건 단순히 “RAM이 더 많아진다”는 뜻이 아니라 CPU도 빨라진다는 뜻이고, 이는 Chromium 렌더링 속도에 직접 영향을 줍니다. 실제로 Puppeteer 벤치마크를 돌린 팀들은 512MB에서 1536~2048MB 구간으로 갈수록 실행 시간이 눈에 띄게 줄어드는 경우가 많다고 보고합니다. 다만 정확한 수치는 렌더링하는 페이지에 따라 많이 달라집니다. 그래서 여기서 오래된 벤치마크 표를 가져오는 대신, 실제 대상 페이지에 대해 512MB, 1024MB, 1536MB, 2048MB로 직접 테스트해 보세요. 10분만 투자하면 비용과 성능의 최적 지점을 정확히 알 수 있습니다.
콜드 스타트 줄이기: Provisioned Concurrency
지연 시간이 중요한 작업, 예를 들어 합성 모니터링이나 실시간 스크린샷 API를 운영한다면 콜드 스타트는 꽤 아픈 문제입니다. Provisioned Concurrency는 일정 수의 실행 환경을 미리 따뜻하게 유지해 콜드 스타트 부담을 없애 주지만, 그만큼 유휴 용량 비용을 내야 합니다. 지연 시간이 비용 효율보다 더 중요할 때는 충분히 쓸 만합니다.
비용 절감을 위한 arm64(Graviton)
Graviton 기반 Lambda 함수는 x86_64보다 대략 20% 저렴합니다. 단점은 @sparticuz/chromium의 arm64 지원이 전통적으로 x86_64보다 제한적이었다는 점입니다. 운영 환경에서 Graviton을 쓰기로 결정하기 전에, 반드시 고정한 버전에서 arm64 지원 여부를 명확히 확인하세요.
VPC 사용 여부
예전에는 함수를 VPC에 넣으면 콜드 스타트 지연이 꽤 늘었습니다. AWS가 최근 몇 년간 이 차이를 많이 줄였지만, 완전히 0은 아닙니다. RDS나 ElastiCache 같은 사설 리소스에 접근해야 할 때만 VPC에 넣고, 그렇지 않다면 굳이 쓸 이유가 없습니다.
Lambda를 완전히 벗어나야 할 때
브라우저 작업이 자주 15분을 넘기거나, 10GB 이상의 메모리가 필요하거나, 요청 간에 이어지는 브라우저 세션이 필요하다면 그 시점부터는 Lambda가 발목을 잡습니다. 이런 경우엔 ECS Fargate가 더 적합합니다. 긴 실행 시간, 리소스 설정 가능, 초 단위 과금에 맞춰 설계된 서비스이기 때문입니다. Lambda는 짧고 폭발적이며 병렬화 가능한 브라우저 작업에는 훌륭하지만, 워크로드가 지속형 서비스처럼 변하면 잘 맞지 않습니다.
Puppeteer를 Lambda에 배포하는 것이 맞지 않은 경우
한 번쯤 진지하게 생각해 볼 만한 부분이 있습니다. “Puppeteer + Lambda” 가이드를 찾는 개발자들 중 상당수는 사실 브라우저 자동화 문제가 아니라 데이터 추출 문제를 풀고 있는 중입니다. 제품 목록, 연락처 정보, 페이지 내용처럼 웹페이지에서 구조화된 데이터를 얻고 싶은 거라면, 위에서 설명한 Chromium 패키징, 버전 고정, 레이어 관리가 전부 불필요한 오버헤드일 수 있습니다.
Lambda + Puppeteer를 유지해야 하는 경우는 진짜 브라우저 제어가 필요할 때입니다. 커스텀 폼 입력, 스크린샷/PDF 파이프라인, 합성 모니터링, DOM을 프로그래밍 방식으로 조작하는 브라우저 기반 테스트 등이 여기에 해당합니다.
스크래핑 API를 고려할 만한 경우는 직접 브라우저 세션을 돌리는 게 아니라 웹페이지에서 구조화된 JSON만 필요할 때입니다. Thunderbit Open API는 JS 렌더링, 봇 차단 대응, CAPTCHA 처리를 단일 HTTP 호출 뒤에서 처리해 줍니다. JSON Schema와 함께 POST /extract를 보내면 구조화된 데이터를 얻을 수 있고, POST /distill을 쓰면 깔끔한 Markdown을 얻을 수 있습니다. 브라우저를 띄우지 않고 워크플로우 중간에 데이터를 가져와야 하는 AI 에이전트를 만든다면 MCP 서버(thunderbit_extract, thunderbit_distill)도 있습니다.
| 요소 | Lambda + Puppeteer(DIY) | 추출 API(예: Thunderbit) |
|---|---|---|
| 설정 시간 | 수 시간(패키징, 레이어, 디버깅) | 수 분(API 키 + HTTP 호출) |
| 유지보수 | 지속적 필요(버전 고정, 런타임 업데이트) | 제공업체가 처리 |
| 봇 차단 대응 | 수동(스텔스 플러그인, 프록시) | 내장 |
| 출력 형식 | 직접 파싱해야 하는 원시 HTML/스크린샷 | 스키마 기반 구조화 JSON |
| 추천 대상 | 전체 브라우저 자동화, 테스트, 커스텀 플로우 | 데이터 추출, 스크래핑, 콘텐츠 수집 |
단도직입적으로 말하면, 상품 페이지에서 JSON만 뽑으려고 Chromium 바이너리를 몇 시간씩 디버깅하고 있다면 문제를 잘못 풀고 있는 신호입니다. 진짜 브라우저를 직접 조작해야 할 때만 DIY Lambda 방식을 선택하세요. 추출이 목적이라면 더 직접적인 방법이 있습니다. 특정 프로젝트에서 이 선택이 고민된다면, 저희 AI 웹 스크래퍼 가이드에서 전체 지형을 더 자세히 볼 수 있고, 먼저 추출 중심 접근을 시험해 보고 싶다면 Thunderbit Chrome Extension을 살펴볼 만합니다.
마무리
배포 방식은 세 가지지만 공통된 핵심은 하나입니다. 버전을 고정하고, Chromium이 숨 쉴 만큼 메모리를 주고, 가장 먼저 찾은 튜토리얼이 아니라 실제 제약 조건에 맞는 배포 방식을 고르세요. 빠른 반복에는 Layers, 운영 규모에는 Container Images, 단발성 단순 작업에는 ZIP이 어울립니다. 그리고 실제로 하려는 일이 브라우저 자동화가 아니라 데이터 추출이라면, 목적에 맞는 추출 API가 패키징 스트레스를 통째로 없애 줄 수 있는지 꼭 한 번 확인해 볼 가치가 있습니다.
FAQ
2026년에도 AWS Lambda에서 Puppeteer를 실행할 수 있나요?
네. puppeteer-core와 @sparticuz/chromium을 함께 쓰고, Layers, Container Image, 직접 ZIP 업로드 방식 중 하나로 배포하면 됩니다. 풀 버전 puppeteer와 더 이상 유지되지 않는 chrome-aws-lambda 패키지는 현재 Lambda 런타임에서 안정적으로 동작하지 않습니다.
AWS Lambda의 최대 패키지 크기는 얼마인가요? AWS Lambda 할당량 기준으로, Layers와 ZIP 배포는 압축 해제 후 250MB, Container Image 배포는 10GB입니다.
chrome-aws-lambda는 아직 유지보수되나요?
아니요. 원래 chrome-aws-lambda 패키지(alixaxel)는 더 이상 유지보수되지 않으며 Node 18+에서 깨집니다. 대신 @sparticuz/chromium를 사용하세요. 지금은 사실상 표준입니다.
AWS Lambda에서 Puppeteer에 필요한 메모리는 어느 정도인가요? 실무 기준 최소치는 1024MB입니다. 성능이 편안해지는 구간은 1536~2048MB 정도입니다. 1024MB 아래에서는 Lambda가 메모리에 따라 CPU를 할당하기 때문에 실행 속도가 눈에 띄게 느려질 수 있습니다.
Lambda에서 Puppeteer의 콜드 스타트 시간을 줄이려면 어떻게 하나요? 메모리를 더 할당해 CPU도 함께 확보하고, 지연 시간이 중요한 경우 Provisioned Concurrency를 고려하세요. 또 배포 패키지는 최대한 가볍게 유지하세요. 의존성이 하나 늘 때마다 콜드 스타트 시간도 조금씩 늘어납니다.


