Claude Code × AWS Lambda 완전 가이드 | 함수 생성부터 배포 자동화까지
Claude Code로 AWS Lambda 함수를 초고속 개발. 핸들러 생성, IAM 정책 설계, SAM 배포 자동화까지 API Gateway/S3/DynamoDB 연동 실전 코드로 완전 해설.
AWS Lambda 개발에서 이런 경험을 해본 적 있으신가요? 핸들러의 타입 정의가 번거롭고, IAM 정책을 매번 찾아보며 작성하고, SAM 템플릿 작성법을 잊어버린 경험… Claude Code는 이 모든 것을 한 번에 해결합니다.
Lambda 함수 구현부터 IAM 정책 생성, 로컬 테스트, 프로덕션 배포까지 — Claude Code를 활용한 초고속 AWS Lambda 개발의 전 과정을 실전 코드로 해설합니다.
왜 Claude Code × AWS Lambda인가
Lambda 개발의 “번거로운 부분”은 사실 대부분 보일러플레이트입니다.
- 핸들러 함수의 타입 정의 (APIGatewayProxyHandler, S3Handler …)
- 에러 핸들링과 응답 형식
- IAM 정책의 최소 권한 설계
- SAM / CloudFormation 템플릿 작성
- 로컬 테스트 환경 설정
Claude Code는 이것들을 “이런 Lambda가 필요해”라는 한 마디로 생성해줍니다. 게다가 AWS CLI나 SAM CLI와 조합하면 코드 생성부터 배포까지 한 번에 실행할 수 있습니다.
환경 세팅
# 필요한 도구 확인
aws --version # AWS CLI v2
sam --version # SAM CLI 1.100+
node --version # Node.js 20+
# AWS 자격증명 설정 (미설정인 경우)
aws configure
# → Access Key ID, Secret Access Key, Region (ap-northeast-2), Output (json)
<!-- CLAUDE.md에 추가 -->
## AWS 설정
- 기본 리전: ap-northeast-2 (서울)
- Lambda 런타임: nodejs20.x
- 배포 도구: AWS SAM
- 소스 디렉터리: src/functions/
- SAM 템플릿: template.yaml
- 스택 이름: my-app-{dev|staging|prod}
## IAM 정책 방침
- 최소 권한 원칙 엄수
- 와일드카드 (*) 사용 금지
- 리소스 ARN은 구체적으로 지정
구현 예시 1: API Gateway + Lambda (REST API)
가장 기본적인 패턴. HTTP 요청을 받아 처리하는 Lambda입니다.
Claude Code에 대한 지시
claude -p "
/users/{userId}에 GET 요청이 오면
DynamoDB의 Users 테이블에서 사용자 정보를 가져와 반환하는
Lambda 함수를 TypeScript로 구현해줘.
- 런타임: Node.js 20
- 테이블 이름: UsersTable (환경 변수로 받음)
- 에러 핸들링: 404/500을 적절히 반환
- 타입 정의: @types/aws-lambda 사용
- src/functions/getUser/index.ts에 출력
"
생성되는 구현 예시
// src/functions/getUser/index.ts
import {
APIGatewayProxyHandler,
APIGatewayProxyResult,
} from "aws-lambda";
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient, GetCommand } from "@aws-sdk/lib-dynamodb";
const client = new DynamoDBClient({ region: process.env.AWS_REGION });
const ddb = DynamoDBDocumentClient.from(client);
const response = (statusCode: number, body: unknown): APIGatewayProxyResult => ({
statusCode,
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
},
body: JSON.stringify(body),
});
export const handler: APIGatewayProxyHandler = async (event) => {
const userId = event.pathParameters?.userId;
if (!userId) {
return response(400, { error: "userId is required" });
}
try {
const result = await ddb.send(
new GetCommand({
TableName: process.env.USERS_TABLE!,
Key: { userId },
})
);
if (!result.Item) {
return response(404, { error: `User '${userId}' not found` });
}
return response(200, result.Item);
} catch (err) {
console.error("DynamoDB error:", err);
return response(500, { error: "Internal server error" });
}
};
SAM 템플릿도 자동 생성
# template.yaml (발췌)
GetUserFunction:
Type: AWS::Serverless::Function
Properties:
Handler: src/functions/getUser/index.handler
Runtime: nodejs20.x
Timeout: 10
Environment:
Variables:
USERS_TABLE: !Ref UsersTable
Policies:
- DynamoDBReadPolicy:
TableName: !Ref UsersTable
Events:
GetUserApi:
Type: Api
Properties:
Path: /users/{userId}
Method: GET
UsersTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: !Sub "${AWS::StackName}-users"
BillingMode: PAY_PER_REQUEST
AttributeDefinitions:
- AttributeName: userId
AttributeType: S
KeySchema:
- AttributeName: userId
KeyType: HASH
구현 예시 2: S3 이벤트 트리거 Lambda
파일 업로드를 트리거로 처리를 실행하는 Lambda.
// src/functions/generateThumbnail/index.ts
import { S3Handler } from "aws-lambda";
import { S3Client, GetObjectCommand, PutObjectCommand } from "@aws-sdk/client-s3";
import sharp from "sharp";
const s3 = new S3Client({ region: process.env.AWS_REGION });
export const handler: S3Handler = async (event) => {
const record = event.Records[0];
const bucket = record.s3.bucket.name;
const key = decodeURIComponent(record.s3.object.key.replace(/\+/g, " "));
// thumbnails/ 접두사는 무시 (무한 루프 방지)
if (key.startsWith("thumbnails/")) return;
const { Body } = await s3.send(new GetObjectCommand({ Bucket: bucket, Key: key }));
const buffer = Buffer.from(await Body!.transformToByteArray());
const thumbnail = await sharp(buffer)
.resize(200, 200, { fit: "cover" })
.webp({ quality: 85 })
.toBuffer();
const thumbnailKey = `thumbnails/${key.replace(/\.[^.]+$/, ".webp")}`;
await s3.send(
new PutObjectCommand({
Bucket: bucket,
Key: thumbnailKey,
Body: thumbnail,
ContentType: "image/webp",
})
);
console.log(`썸네일 생성 완료: s3://${bucket}/${thumbnailKey}`);
};
구현 예시 3: 스케줄 실행 Lambda (EventBridge)
# template.yaml의 EventBridge 설정
SendReminderFunction:
Type: AWS::Serverless::Function
Properties:
Handler: src/functions/sendReminder/index.handler
Runtime: nodejs20.x
Timeout: 300
Environment:
Variables:
DATABASE_URL: !Sub "{{resolve:secretsmanager:${AWS::StackName}/database-url}}"
RESEND_API_KEY: !Sub "{{resolve:secretsmanager:${AWS::StackName}/resend-api-key}}"
Events:
DailyReminder:
Type: Schedule
Properties:
Schedule: cron(0 0 * * ? *) # UTC 0:00 = KST 9:00
IAM 정책을 Claude Code에 설계시키기
claude -p "
다음 Lambda에 필요한 최소 권한 IAM 정책을 JSON으로 생성:
- S3 my-uploads에서 GetObject
- DynamoDB ProcessingJobs에 PutItem/UpdateItem
- SQS ProcessingQueue에 SendMessage
- CloudWatch Logs에 쓰기
ARN은 리소스 이름으로부터 추론하여 구체적으로 작성할 것.
"
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:GetObject"],
"Resource": "arn:aws:s3:::my-uploads/*"
},
{
"Effect": "Allow",
"Action": ["dynamodb:PutItem", "dynamodb:UpdateItem"],
"Resource": "arn:aws:dynamodb:ap-northeast-2:*:table/ProcessingJobs"
},
{
"Effect": "Allow",
"Action": ["sqs:SendMessage"],
"Resource": "arn:aws:sqs:ap-northeast-2:*:ProcessingQueue"
},
{
"Effect": "Allow",
"Action": ["logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents"],
"Resource": "arn:aws:logs:*:*:log-group:/aws/lambda/*"
}
]
}
로컬 테스트와 프로덕션 배포
# 빌드
sam build
# 로컬에서 API 실행
sam local start-api --port 3001
# 단발 테스트
sam local invoke GetUserFunction --event events/get-user.json
# Claude Code로 일괄 실행
claude -p "
sam build를 실행하고,
sam local invoke GetUserFunction --event events/test-get-user.json으로 결과 확인.
문제없으면 sam deploy --config-env dev를 실행.
"
주의해야 할 함정 5선
1. 콜드 스타트를 고려하지 않은 초기화
// ❌ 핸들러 내에서 매번 클라이언트 생성
export const handler = async () => {
const ddb = new DynamoDBClient({}); // ← 매번 인스턴스화
};
// ✅ 모듈 스코프에서 1회만 초기화
const ddb = DynamoDBDocumentClient.from(new DynamoDBClient({}));
export const handler = async () => { /* ddb 재사용 */ };
2. 타임아웃을 기본값(3초)으로 방치 DynamoDB + 외부 API라면 최소 10-30초. 처리 내용에 맞춰 반드시 설정해야 합니다.
3. Secrets를 환경 변수에 직접 작성
# ❌ 템플릿에 직접 작성
Environment:
Variables:
DB_PASSWORD: "my-secret"
# ✅ Secrets Manager 경유
Environment:
Variables:
DB_PASSWORD: !Sub "{{resolve:secretsmanager:myapp/db-password}}"
4. VPC Lambda의 콜드 스타트 RDS 연결 목적으로 VPC에 넣으면 콜드 스타트가 수 초 늘어납니다. Provisioned Concurrency 또는 RDS Proxy로 대처하세요.
5. 거대한 배포 패키지
node_modules를 통째로 패키징하면 250MB 상한에 걸립니다. 공통 라이브러리는 Lambda Layer로 분리하세요.
정리
| 작업 | Claude Code의 기여 |
|---|---|
| 핸들러 구현 | 타입 정의·에러 처리·로직을 한 번에 생성 |
| SAM 템플릿 | 이벤트·IAM·환경 변수를 자동 출력 |
| IAM 정책 | 최소 권한 설계를 정확하게 생성 |
| 로컬 테스트 | sam invoke 실행과 결과 판단을 자동화 |
| 배포 | build + deploy를 일련의 흐름으로 실행 |
Lambda 개발에서 가장 시간을 잡아먹는 “타입 정의와 템플릿 작성”을 Claude Code가 통째로 처리합니다. 비즈니스 로직에 집중할 수 있는 환경이 구현 속도를 3-5배 끌어올립니다.
관련 기사
참고 자료
Claude Code 워크플로우를 한 단계 업그레이드하세요
지금 바로 Claude Code에 복사해 쓸 수 있는 검증된 프롬프트 템플릿 50선.
무료 PDF: 5분 완성 Claude Code 치트시트
이메일 주소만 등록하시면 A4 한 장짜리 치트시트 PDF를 즉시 보내드립니다.
개인정보는 엄격하게 관리하며 스팸은 보내지 않습니다.
이 글을 작성한 사람
Masa
Claude Code를 적극 활용하는 엔지니어. 10개 언어, 2,000페이지 이상의 테크 미디어 claudecode-lab.com을 운영 중.
관련 글
Claude Code로 신규 엔지니어 온보딩 시간을 대폭 단축하는 방법
3개월 램프업을 2주로. Claude Code를 신입의 코드베이스·환경·첫 PR 코파일럿으로 활용하세요.
Claude Code로 기술 부채를 시각화하고 체계적으로 줄이는 방법
갚지 않은 기술 부채는 엔지니어링 속도를 갉아먹습니다. Claude Code로 부채를 드러내고, 우선순위를 정하고, 점진적으로 상환하는 법을 배워보세요.
Claude Code로 개발 환경을 순식간에 세팅하는 방법
새 PC든 새 프로젝트든, Claude Code에 맡기면 개발 환경 세팅이 몇 분이면 끝납니다.