
Project 2: Serverless Data Pipeline with Terraform (S3 + Lambda + SQS + DynamoDB)
Terraform으로 구축한 서버리스 데이터 파이프라인 인프라입니다. S3 Ingestion 버킷에 파일을 업로드하면 Lambda Router가 파일 형식을 자동으로 판별하여 정형 데이터(CSV/JSON)는 DynamoDB에 적재하고, 비정형 데이터(PDF/이미지)는 텍스트를 추출해 처리 결과를 S3 Processed 버킷에 저장합니다. 지원하지 않는 파일 형식은 Quarantine 버킷으로 격리되며, CloudWatch + SNS로 실시간 모니터링 및 이메일 알림을 제공합니다.
파이프라인 전체가 Lambda와 SQS로 비동기 연결되어 있어, 대용량 파일이나 동시 다발적 업로드에도 확장 가능한 구조입니다. API Gateway를 통해 외부에서 HTTP로 파이프라인을 직접 트리거할 수도 있습니다.
아키텍처
[파일 업로드 경로]
S3 Ingestion 버킷 API Gateway (HTTP POST)
│ │
└──────────────┬───────────────────────┘
▼
[Lambda Router]
파일 확장자 감지 및 분기
│
┌───────────┼──────────────┐
▼ ▼ ▼
.csv / .json .pdf / .jpg 그 외 확장자
│ .png / .tiff │
▼ │ ▼
[SQS 정형 큐] │ [Quarantine 버킷]
│ ▼ (격리)
▼ [SQS 비정형 큐]
[Lambda Parser] │
DynamoDB 적재 ▼
│ [Lambda Extractor]
▼ PDF 텍스트 추출 (pypdf)
[DynamoDB] 이미지 레이블 감지 (Rekognition)
│
▼
[S3 Processed 버킷]
results/ 경로에 JSON 저장
[모니터링 / 알림 흐름]
CloudWatch (Lambda 호출 수, 에러율, SQS 메시지 수)
│
▼
SNS Topic ──── 이메일 알림 (처리 실패 시 즉시 발송)
주요 구성 요소
| 리소스 | 설명 | 비용 |
|---|---|---|
| S3 Ingestion | 외부에서 파일이 최초로 업로드되는 입력 버킷. S3 이벤트 트리거로 Lambda Router 호출 | 프리 티어: 5GB / 월 |
| S3 Processed | Extractor Lambda가 추출한 결과(JSON)를 저장하는 버킷. Lifecycle 규칙으로 90일 후 자동 삭제 | 프리 티어: 5GB / 월 |
| S3 Quarantine | 지원하지 않는 확장자 또는 처리 실패 파일을 격리하는 버킷. 원인 메타데이터 함께 저장 | 프리 티어: 5GB / 월 |
| Lambda Router | S3 이벤트 또는 API Gateway 요청 수신 → 파일 확장자 감지 → 정형/비정형/격리 분기 처리 | 프리 티어: 월 1,000,000 요청 |
| Lambda Parser | SQS 정형 큐 트리거 → CSV/JSON 파싱 → DynamoDB에 레코드 적재 | 프리 티어: 월 1,000,000 요청 |
| Lambda Extractor | SQS 비정형 큐 트리거 → PDF 텍스트 추출(pypdf) / 이미지 레이블 감지(Rekognition) → S3 Processed 저장 | 프리 티어: 월 1,000,000 요청 |
| SQS 정형 큐 | Router → Parser 간 메시지 버퍼. Visibility Timeout 60초, DLQ 포함 | 프리 티어: 월 1,000,000 요청 |
| SQS 비정형 큐 | Router → Extractor 간 메시지 버퍼. Visibility Timeout 300초(PDF 처리 대응), DLQ 포함 | 프리 티어: 월 1,000,000 요청 |
| DynamoDB | 정형 데이터 적재 및 비정형 처리 메타데이터 저장. record_id(PK) 기반 단일 테이블 |
프리 티어: 25GB / 월 |
| API Gateway | 외부 HTTP POST 요청을 받아 Lambda Router에 전달하는 HTTP API(v2). /v1/upload 엔드포인트 |
프리 티어: 월 1,000,000 호출 |
| CloudWatch | Lambda 실행 로그, 에러율, SQS 큐 깊이 모니터링 및 대시보드 제공 | 프리 티어: 5GB 로그 수집 |
| SNS | 처리 오류 발생 시 등록된 이메일로 즉시 알림 발송 | 프리 티어: 월 1,000건 이메일 |
사전 준비
IAM 사용자 생성
AWS CLI와 Terraform이 AWS 리소스를 생성할 수 있도록 프로그래밍 방식 액세스 권한을 가진 IAM 사용자가 필요합니다.
1단계: IAM 사용자 생성
AWS 콘솔에서 IAM → 사용자 → 사용자 생성으로 이동합니다. 사용자 이름(예: terraform-admin)을 입력하고, AWS Management Console 액세스는 체크하지 않습니다 (CLI 전용 사용자이므로 콘솔 로그인 권한 불필요).
2단계: AdministratorAccess 권한 부여
권한 설정 단계에서 직접 정책 연결을 선택하고 AdministratorAccess 정책을 검색하여 체크합니다.
⚠️ AdministratorAccess는 실습/테스트용 편의 설정입니다. 실제 운영 환경에서는 최소 권한 원칙에 따라 필요한 서비스만 허용하는 커스텀 정책을 사용하세요.
3단계: 액세스 키(Access Key) 발급 및 저장
사용자 생성 후 해당 사용자 → 보안 자격 증명 → 액세스 키 만들기로 이동합니다. 사용 사례로 **Command Line Interface(CLI)**를 선택합니다. 발급된 액세스 키 ID와 시크릿 액세스 키는 이 화면에서만 확인 가능하므로, CSV를 다운로드하거나 별도로 메모해 두세요. 화면을 닫으면 시크릿 키는 다시 볼 수 없습니다.
AWS CLI v2 설치
# Linux / macOS의 경우
brew install awscli
aws --version
# Windows의 경우
winget install Amazon.AWSCLI
aws --version
Terraform 설치
# Linux / macOS의 경우
# 1. HashiCorp 저장소 추가
brew tap hashicorp/tap
# 2. Terraform 설치
brew install hashicorp/tap/terraform
terraform --version
# Windows의 경우
winget install Hashicorp.Terraform
terraform --version
설치 후 주의사항
Windows winget 약관 동의: winget 명령어를 처음 사용하면 중간에 ‘소스 약관에 동의하십니까?’ 라는 질문(Y/N)이 뜰 수 있습니다. Y를 입력하고 엔터를 누르면 계속 진행됩니다.
버전 확인 에러 발생 시: 설치가 완료된 직후
aws --version이나terraform --version을 입력했을 때 *‘명령어를 찾을 수 없다’*고 나온다면, 터미널(또는 PowerShell) 창을 완전히 닫고 새로 열어 다시 입력해 보세요. 환경 변수가 새로고침되어 정상 동작합니다.VSCode 사용 시: 터미널 탭만 닫는 것이 아니라 VSCode 창 전체를 완전히 껐다가 다시 켜야 합니다.
배포 가이드
1. AWS CLI 자격증명 등록
aws configure
# AWS Access Key ID: <발급받은 Access Key ID>
# AWS Secret Access Key: <발급받은 Secret Access Key>
# Default region name: ap-northeast-2
# Default output format: json
연결 확인:
aws sts get-caller-identity
정상 결과:
{
"UserId": "AIDAXXXXXXXXXXXXXXXXX",
"Account": "123456789012",
"Arn": "arn:aws:iam::123456789012:user/terraform-admin"
}
2. 변수 설정 (variables.tf)
project2-serverless-pipeline/variables.tf 파일을 열어 아래 두 변수를 수정합니다.
| 변수 | 설명 | 예시 |
|---|---|---|
suffix |
버킷 이름 뒤에 붙는 고유 식별자. 전 세계에서 유일해야 하므로 이름+날짜 조합 권장 | myname-20260612 |
alert_email |
Lambda 처리 실패 시 SNS 알림을 받을 이메일 주소 | your@email.com |
3. Terraform 배포
프로젝트 폴더로 이동 후 순서대로 실행합니다.
cd project2-serverless-pipeline
terraform init # Provider 플러그인 다운로드 (최초 1회)
terraform plan # 생성될 리소스 미리보기
terraform apply # 실제 배포, 확인 프롬프트에 'yes' 입력
배포가 완료되면 터미널에 Outputs이 출력됩니다. api_endpoint, ingestion_bucket, dynamodb_table 등의 값을 메모해 두면 이후 테스트에 편리합니다.
4. Lambda 함수 생성 확인
배포 후 Lambda 함수 3개가 정상 생성되었는지 확인합니다.
aws lambda list-functions --query "Functions[*].FunctionName" --region ap-northeast-2
p2-router, p2-parser, p2-extractor (또는 설정한 이름) 3개가 목록에 보이면 정상입니다.
AWS 콘솔에서 함수가 보이지 않는 경우: 콘솔 우측 상단의 리전이
아시아 태평양 (서울) ap-northeast-2로 선택되어 있는지 확인하세요. 리전이 다르게 선택되어 있으면 해당 리전의 함수 목록이 표시됩니다.
5. SNS 구독 이메일 확인
variables.tf에 입력한 이메일 주소로 AWS Notification - Subscription Confirmation 메일이 발송됩니다. 메일 내 Confirm subscription 링크를 클릭해야 이후 처리 실패 알림이 정상적으로 수신됩니다. 링크를 클릭하지 않으면 알림이 발송되지 않습니다.
테스트 시나리오
테스트 전 아래 4개 변수를 본인 환경에 맞게 메모해 두세요.
[ingestion-bucket]:terraform output ingestion_bucket값[quarantine-bucket]:terraform output check_quarantine값에서 버킷명[processed-bucket]:terraform output processed_bucket값[api-endpoint]:terraform output api_endpoint값
① CSV 업로드 → DynamoDB 저장 확인
정형 데이터(CSV) 파일이 파이프라인을 거쳐 DynamoDB에 적재되는지 확인합니다.
# 1. CSV 파일 업로드
aws s3 cp sample_data/test.csv s3://[ingestion-bucket]/test.csv
# 2. 약 5~10초 후 DynamoDB 스캔 (데이터 확인)
# 테이블 이름은 terraform output dynamodb_table 값 사용
aws dynamodb scan --table-name p2-pipeline-records --region ap-northeast-2
업로드 후 DynamoDB 테이블에 레코드가 추가된 것을 확인할 수 있습니다.


② JSON 업로드 → DynamoDB 저장 확인
JSON 형식의 정형 데이터도 동일한 경로로 처리됩니다.
# JSON 파일 업로드
aws s3 cp sample_data/test.json s3://[ingestion-bucket]/test.json
# DynamoDB 확인 (①번과 동일)
aws dynamodb scan --table-name p2-pipeline-records --region ap-northeast-2

③ 미지원 확장자 (.xyz) → Quarantine 격리 확인
지원하지 않는 확장자 파일이 에러 없이 격리 버킷으로 이동되는지 확인합니다.
# 테스트 파일 생성 및 업로드
"test" | Out-File -Encoding utf8 sample_data/test.xyz
aws s3 cp sample_data/test.xyz s3://[ingestion-bucket]/test.xyz
# 격리 버킷 확인 (약 5초 후)
aws s3 ls s3://[quarantine-bucket]/ --recursive
unknown/2026/06/12/test.xyz 형태로 Quarantine 버킷에 파일이 이동된 것을 확인할 수 있습니다.

④ PDF 업로드 → Processed 버킷 결과 확인
PDF 파일이 업로드되면 Extractor Lambda가 텍스트를 추출하여 결과 JSON을 Processed 버킷에 저장합니다.
# PDF 파일 업로드
aws s3 cp sample_data/test.pdf s3://[ingestion-bucket]/test.pdf
# 처리 결과 확인 (약 15~30초 후)
aws s3 ls s3://[processed-bucket]/results/ --recursive
results/2026/06/12/<uuid>.json 형태로 파일이 생성된 것을 확인합니다.


⑤ API Gateway 엔드포인트 테스트
외부 HTTP 요청으로 파이프라인을 트리거합니다. Windows PowerShell에서는 curl.exe -d 대신 Invoke-RestMethod를 사용해야 JSON 포맷이 깨지지 않습니다.
# 1. 요청 데이터를 PowerShell 오브젝트로 빌드
$bodyObj = @{
Records = @(
@{
s3 = @{
bucket = @{ name = "[ingestion-bucket]" }
object = @{ key = "test.csv"; size = 100 }
}
}
)
}
# 2. JSON 문자열로 변환
$jsonBody = $bodyObj | ConvertTo-Json -Depth 5 -Compress
# 3. API Gateway로 POST 요청
Invoke-RestMethod -Uri "[api-endpoint]" `
-Method Post `
-Headers @{"Content-Type"="application/json"} `
-Body $jsonBody
정상 응답:
{
"message": "routing complete"
}
⚠️ 비용 관리
서비스별 프리 티어 범위
| 서비스 | 프리 티어 한도 | 초과 시 단가 |
|---|---|---|
| Lambda × 3 | 월 1,000,000 요청 / 400,000 GB-초 | $0.20 / 백만 요청 |
| S3 × 3 (Ingestion, Processed, Quarantine) | 5GB 스토리지 / 월 20,000 GET, 2,000 PUT | $0.023 / GB |
| SQS × 2 (정형, 비정형) | 월 1,000,000 요청 | $0.40 / 백만 요청 |
| DynamoDB | 25GB 스토리지 / 월 200만 읽기·쓰기 | $0.25 / WCU, $0.10 / RCU |
| API Gateway (HTTP API) | 월 1,000,000 호출 (12개월) | $1.00 / 백만 호출 |
| CloudWatch | 5GB 로그 수집 / 3개 대시보드 | $0.50 / GB |
Textract 구독 주의: 향후 이 파이프라인에 AWS Textract를 연동할 경우, Textract는 AWS 계정 레벨에서 별도 구독(활성화)이 필요합니다. 구독하지 않은 상태에서 API를 호출하면
SubscriptionRequiredException: The AWS Access Key Id needs a subscription for the service오류가 발생하며, 계정 등록(신용카드) 완료 후 최대 1일 이상 소요될 수 있습니다. 본 프로젝트의 PDF 처리는 Textract 대신pypdf라이브러리로 대체되었습니다.
인프라 삭제 전 반드시 S3 버킷 비우기
S3 버킷에 오브젝트가 남아있으면 terraform destroy가 실패합니다. 아래 순서로 진행하세요.
# 3개 버킷 모두 비우기
aws s3 rm s3://[ingestion-bucket] --recursive
aws s3 rm s3://[processed-bucket] --recursive
aws s3 rm s3://[quarantine-bucket] --recursive
# 인프라 삭제
terraform destroy
트러블슈팅 노트
1. S3 Lifecycle 규칙에 filter {} 블록 필수
Terraform AWS 프로바이더는 aws_s3_bucket_lifecycle_configuration의 rule 블록 안에 filter 또는 prefix 중 하나를 반드시 명시해야 합니다. 버킷 전체에 규칙을 적용하고 싶더라도 빈 filter {} 블록을 생략하면 에러가 발생합니다.
# ❌ 잘못된 예 — filter 없음
rule {
id = "auto-delete-after-90-days"
status = "Enabled"
expiration { days = 90 }
}
# ✅ 올바른 예 — 빈 filter {}로 버킷 전체 적용
rule {
id = "auto-delete-after-90-days"
status = "Enabled"
filter {
prefix = "" # 모든 객체 대상
}
expiration { days = 90 }
}
2. API Gateway HTTP API v2 — event["body"] 파싱 주의
API Gateway HTTP API(v2)와 Lambda가 프록시 통합으로 연결되면, 클라이언트가 보낸 JSON 데이터는 event["Records"]에 직접 들어오지 않고 event["body"] 안에 문자열로 감싸져 전달됩니다. S3 직접 트리거와 구조가 다르기 때문에 Router Lambda에 분기 처리가 필요합니다.
# API Gateway를 통한 요청인 경우 body 파싱
if "body" in event:
if isinstance(event["body"], str):
event = json.loads(event["body"])
else:
event = event["body"]
이 분기를 추가하지 않으면 API Gateway 경로로 들어온 요청에서 KeyError: 'Records'가 발생합니다.
3. Windows PowerShell에서 JSON 전송 시 Invoke-RestMethod 사용
PowerShell에서 curl.exe -d '{"key": "value"}' 형태로 JSON을 전송하면 줄바꿈 문자가 삽입되거나 따옴표 이스케이프가 깨져 잘못된 JSON이 전달됩니다. Windows 환경에서는 반드시 Invoke-RestMethod를 사용하세요.
# ❌ PowerShell에서 문제가 생기는 방식
curl.exe -X POST https://... -d '{"test": "value"}'
# ✅ PowerShell 네이티브 방식 (JSON 포맷 보장)
$body = @{ test = "value" } | ConvertTo-Json -Compress
Invoke-RestMethod -Uri "https://..." -Method Post `
-Headers @{"Content-Type"="application/json"} -Body $body
4. Lambda 콘솔에서 함수 목록이 비어있는 경우
AWS 콘솔에서 Lambda 함수가 보이지 않는다면, 콘솔 우측 상단의 리전 선택을 확인하세요. Terraform 배포 시 기본 리전(ap-northeast-2, 서울)과 콘솔에서 선택된 리전이 다르면 해당 리전에는 함수가 없으므로 빈 목록이 표시됩니다. CLI로 확인하는 방법:
aws lambda list-functions --query "Functions[*].FunctionName" --region ap-northeast-2
인프라 삭제
# 1. S3 버킷 3개 비우기 (오브젝트가 남아있으면 terraform destroy 실패)
# 버킷 이름 확인 (terraform output으로 확인)
# [ingestion-bucket], [processed-bucket], [quarantine-bucket] 에 실제 값 입력
aws s3 rm s3://[ingestion-bucket] --recursive
aws s3 rm s3://[processed-bucket] --recursive
aws s3 rm s3://[quarantine-bucket] --recursive
# 2. 인프라 전체 삭제
cd project2-serverless-pipeline
terraform destroy
DynamoDB 테이블, Lambda 함수, SQS 큐, API Gateway, CloudWatch 알람, SNS 토픽 등 Terraform으로 생성된 모든 리소스가 삭제됩니다.
기술 스택
Terraform · AWS S3 · AWS Lambda · AWS SQS · AWS DynamoDB · AWS API Gateway · AWS CloudWatch · AWS SNS · AWS IAM · Python · pypdf · Amazon Rekognition