Claude Code × AWS Lambda 완전 가이드 | 함수 생성부터 배포 자동화까지
Claude Code로 AWS Lambda를 안전하게 구축: Node.js 24, SAM, IAM, API Gateway, S3, 로그와 함정.
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 24+
# AWS 자격증명 설정 (미설정인 경우)
aws configure
# → Access Key ID, Secret Access Key, Region (ap-northeast-2), Output (json)
<!-- CLAUDE.md에 추가 -->
## AWS 설정
- 기본 리전: ap-northeast-2 (서울)
- Lambda 런타임: nodejs24.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 24
- 테이블 이름: 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: nodejs24.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: nodejs24.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배 끌어올립니다.
관련 기사
Lambda가 맞는 경우와 아닌 경우
Lambda는 짧고 이벤트 형태가 분명하며 안전하게 재시도할 수 있는 작업에 적합합니다. 주문 조회 API, S3 업로드 후 이미지 처리, EventBridge 일일 리포트, Stripe/GitHub webhook이 예입니다. 장시간 worker, 지속 연결, 큰 모델 상주, 복잡한 WebSocket 상태에는 약합니다. 코드 생성 전 적합한 이유와 아닌 이유를 Claude Code에 쓰게 하세요.
용어를 쉽게 정리
| Term | Plain meaning |
|---|---|
| handler | The function Lambda calls. API Gateway passes an HTTP event; S3 passes an object event. |
| event | The JSON payload given to Lambda. API Gateway, S3, and EventBridge shapes are different. |
| least privilege | Grant only the exact actions and resources the function needs. |
| cold start | The slower first run when Lambda creates a new execution environment. |
| idempotency | The same event can run twice without duplicate payment, email, or database writes. |
Node.js 24에서 복사해 실행하는 단위 테스트
// test/getUser.test.ts
import { describe, expect, it, vi } from "vitest";
import type { APIGatewayProxyEvent } from "aws-lambda";
const mockSend = vi.fn();
vi.mock("@aws-sdk/lib-dynamodb", async () => {
const actual = await vi.importActual<typeof import("@aws-sdk/lib-dynamodb")>("@aws-sdk/lib-dynamodb");
return { ...actual, DynamoDBDocumentClient: { from: () => ({ send: mockSend }) } };
});
const { handler } = await import("../src/functions/getUser/index");
function event(userId?: string): APIGatewayProxyEvent {
return {
pathParameters: userId ? { userId } : null,
httpMethod: "GET",
path: userId ? "/users/" + userId : "/users",
headers: {},
multiValueHeaders: {},
queryStringParameters: null,
multiValueQueryStringParameters: null,
body: null,
isBase64Encoded: false,
requestContext: {} as APIGatewayProxyEvent["requestContext"],
resource: "/users/{userId}",
stageVariables: null,
};
}
describe("getUser Lambda", () => {
it("returns 200 when the user exists", async () => {
process.env.USERS_TABLE = "UsersTable";
mockSend.mockResolvedValueOnce({ Item: { userId: "u-1", name: "Masa" } });
const res = await handler(event("u-1"), {} as never, vi.fn());
expect(res.statusCode).toBe(200);
expect(JSON.parse(res.body).userId).toBe("u-1");
});
it("returns 400 when path parameter is missing", async () => {
const res = await handler(event(), {} as never, vi.fn());
expect(res.statusCode).toBe(400);
});
});
API Gateway, S3, EventBridge 이벤트 형태 분리
API Gateway는statusCode와body가 필요합니다. S3는Records의 bucket/key를 읽고, EventBridge는 보통source, detail-type, detail을 봅니다. 프롬프트에 HTTP API v2, S3 ObjectCreated, EventBridge schedule을 명시하세요.
환경 변수, Secrets Manager, 최소 권한 IAM
환경 변수는USERS_TABLE, APP_STAGE 같은 설정에 씁니다. API key, DB password, webhook secret은 Secrets Manager에 두고 ARN만 Lambda에 전달합니다. IAM은 구체 Action과 ARN으로 좁힙니다.
CloudWatch Logs, 재시도, 멱등성
CloudWatch Logs가 첫 조사 지점입니다. 로그가 없으면 리전, 함수명, 실행 역할 로그 권한, 스택명을 확인합니다. S3/EventBridge/async invoke는 재시도될 수 있으므로 event ID나 업무 ID를 멱등성 키로 사용합니다.
Claude Code 안전 리뷰 프롬프트
Review this AWS Lambda change before production.
Check handler shape, API Gateway/S3/EventBridge event type, IAM least privilege, Secrets Manager usage, CloudWatch Logs, retries, idempotency, cold starts, timeout, memory, and cost risk.
Return blockers first, then recommended fixes, missing tests, and human approvals required before deploy.
수익화, 교육, 상담 CTA
ClaudeCodeLab은 Lambda, IAM, SAM, 로그 감사, Claude Code 리뷰 게이트 교육과 상담을 제공합니다. 개인은 무료 PDF와 Gumroad 자료로 시작하고, 팀은 교육 및 상담에서 권한, 배포 승인, 수익 경로를 설계하세요.
직접 확인한 결과
예제를 handler와 test로 나누어npm test로 로컬 확인할 수 있게 했습니다. 실제sam deploy는 계정, 리전, Secret ARN, DynamoDB 테이블에 의존하므로 여기서는 실행하지 않았습니다.
추가 공식 링크
- Lambda runtimes
- Building Lambda functions with Node.js
- Working with Lambda environment variables
- Security best practices in IAM
- Sending Lambda function logs to CloudWatch Logs
- Invoking Lambda with API Gateway
- Using an S3 trigger to invoke Lambda
- Retries and failures in Lambda applications
내부 링크 체크리스트
참고 자료
무료 PDF: Claude Code 치트시트
이메일을 입력하면 명령, 리뷰 습관, 안전한 워크플로를 정리한 PDF를 받을 수 있습니다.
개인정보를 안전하게 관리하며 스팸을 보내지 않습니다.
작성자 소개
Masa
Claude Code 실무 워크플로와 팀 도입을 검증하는 엔지니어입니다.
관련 글
Obsidian 메모를 CLAUDE.md로 바꾸는 Claude Code 워크플로
Obsidian 작업 메모를 CLAUDE.md 운영 노트로 정리해 Claude Code 세션의 문맥 반복을 줄입니다.
Claude Code Revenue CTA Routing: 글에서 PDF, Gumroad, 상담으로 보내기
독자 의도에 따라 무료 PDF, Gumroad 상품, 상담으로 나누는 Claude Code CTA 설계입니다.
Claude Code 팀 인계 규칙: 리뷰 증거, 권한, 롤백, 수익 경로까지 넘기는 법
Claude Code 작업을 팀에 넘길 때 필요한 증거, 권한 규칙, 롤백, 무료 PDF, Gumroad, 상담 경로 체크리스트.