요약
AWS Lambda API는 개발자가 서버리스 함수를 프로그래밍 방식으로 배포, 관리 및 호출할 수 있도록 지원합니다. 이 API는 IAM 인증, 함수 관리를 위한 RESTful 엔드포인트, 계정 수준의 동시성 제한과 함께 비동기 및 동기 호출 옵션을 사용합니다. 이 가이드에서는 인증 설정, 함수 배포, 호출 패턴, 이벤트 소스 매핑 및 프로덕션 서버리스 아키텍처 전략을 다룹니다.
소개
AWS Lambda는 1백만 명 이상의 활성 사용자를 위해 매월 수조 건의 요청을 처리합니다. 서버리스 애플리케이션, 자동화 도구 또는 이벤트 기반 아키텍처를 구축하는 개발자에게 Lambda API 통합은 선택 사항이 아니라 Infrastructure as Code(IaC) 및 CI/CD 파이프라인에 필수적입니다.
실제 현장에서는 50개 이상의 Lambda 함수를 수동으로 관리하는 팀이 배포, 구성 업데이트 및 모니터링에 매주 10~15시간을 소모합니다. 견고한 Lambda API 통합은 배포 자동화, 블루/그린 릴리스 구현, 수요 기반 동적 확장에 필수적입니다.
이 가이드는 AWS Lambda API 통합의 전 과정을 실전 예제와 함께 안내합니다. IAM 인증, 함수 생성 및 배포, 호출 패턴(동기/비동기), 이벤트 소스 매핑, 계층형 아키텍처, 프로덕션 배포 전략까지 단계별로 구현하세요.
AWS Lambda API란 무엇인가요?
AWS Lambda는 서버리스 컴퓨팅 함수를 관리하기 위한 RESTful API를 제공합니다. 주요 제공 기능은 다음과 같습니다:
- 함수 생성, 업데이트, 삭제
- 코드 배포 및 버전 관리
- 함수 호출(동기/비동기)
- 이벤트 소스 매핑(SQS, Kinesis, DynamoDB, S3)
- 레이어 관리(공유 코드)
- 별칭 및 라우팅 구성
- 동시성 및 예약 용량 관리
- 로깅 및 모니터링 통합
주요 기능
| 기능 | 설명 |
|---|---|
| RESTful API | 표준 HTTPS 엔드포인트 |
| IAM 인증 | AWS Signature Version 4 |
| 비동기 호출 | 이벤트 기반, 응답 대기 없음 |
| 동기 호출 | 요청-응답 패턴 |
| 이벤트 소스 | 200개 이상의 AWS 서비스 통합 |
| 레이어 | 공유 코드 및 종속성 |
| 버전/별칭 | 트래픽 전환, 롤백 지원 |
| 프로비저닝된 동시성 | 콜드 스타트 제거 |
Lambda 런타임 지원
| 런타임 | 버전 | 사용 사례 |
|---|---|---|
| Node.js | 18.x, 20.x | API 백엔드, 이벤트 처리 |
| Python | 3.9, 3.10, 3.11 | 데이터 처리, ML 추론 |
| Java | 11, 17, 21 | 엔터프라이즈 애플리케이션 |
| Go | 1.x | 고성능 API |
| Rust | 1.x | 저지연 함수 |
| .NET | 6, 8 | Windows 워크로드 |
| Ruby | 3.x | 웹 애플리케이션 |
| 커스텀 | 모든 | 컨테이너 기반 런타임 |
API 아키텍처 개요
Lambda는 AWS 서비스 API 구조를 따릅니다:
https://lambda.{region}.amazonaws.com/2015-03-31/
API 버전
| 버전 | 상태 | 사용 사례 |
|---|---|---|
| 2015-03-31 | 현재 | 모든 Lambda 작업 |
| 2018-01-31 | 런타임 API | 커스텀 런타임 인터페이스 |
시작하기: 인증 설정
1단계: AWS 계정 및 IAM 사용자 생성
- AWS 콘솔에 접속
- AWS 계정 생성
- IAM 콘솔 > 사용자 > 사용자 생성
- Lambda 실행 정책 연결
2단계: IAM 자격 증명 생성
API 사용을 위한 액세스 키를 생성합니다:
# AWS CLI 사용
aws iam create-access-key --user-name lambda-deployer
# 출력 예시
{
"AccessKey": {
"AccessKeyId": "AKIAIOSFODNN7EXAMPLE",
"SecretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
}
}
자격 증명을 안전하게 저장합니다:
# ~/.aws/credentials
[lambda-deployer]
aws_access_key_id = AKIAIOSFODNN7EXAMPLE
aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
# 또는 환경 변수 사용
export AWS_ACCESS_KEY_ID="AKIAIOSFODNN7EXAMPLE"
export AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
export AWS_DEFAULT_REGION="us-east-1"
3단계: AWS Signature Version 4 이해
모든 Lambda API 요청은 SigV4 서명이 필요합니다. 직접 구현 예:
const crypto = require('crypto');
class AWSSigner {
constructor(accessKeyId, secretAccessKey, region, service = 'lambda') {
this.accessKeyId = accessKeyId;
this.secretAccessKey = secretAccessKey;
this.region = region;
this.service = service;
}
sign(request, body = null) {
const now = new Date();
const amzDate = now.toISOString().replace(/[:-]|\.\d{3}/g, '');
const dateStamp = amzDate.slice(0, 8);
// Canonical request
const hashedPayload = body ? crypto.createHash('sha256').update(body).digest('hex') : 'UNSIGNED-PAYLOAD';
const canonicalUri = request.path;
const canonicalQuerystring = request.query || '';
const canonicalHeaders = `host:${request.host}\nx-amz-date:${amzDate}\n`;
const signedHeaders = 'host;x-amz-date';
const canonicalRequest = `${request.method}\n${canonicalUri}\n${canonicalQuerystring}\n${canonicalHeaders}\n${signedHeaders}\n${hashedPayload}`;
// String to sign
const algorithm = 'AWS4-HMAC-SHA256';
const credentialScope = `${dateStamp}/${this.region}/${this.service}/aws4_request`;
const hash = crypto.createHash('sha256').update(canonicalRequest).digest('hex');
const stringToSign = `${algorithm}\n${amzDate}\n${credentialScope}\n${hash}`;
// Signature 계산
const kDate = this.hmac(`AWS4${this.secretAccessKey}`, dateStamp);
const kRegion = this.hmac(kDate, this.region);
const kService = this.hmac(kRegion, this.service);
const kSigning = this.hmac(kService, 'aws4_request');
const signature = this.hmac(kSigning, stringToSign, 'hex');
// Authorization header
const authorizationHeader = `${algorithm} Credential=${this.accessKeyId}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signature}`;
return {
'Authorization': authorizationHeader,
'X-Amz-Date': amzDate,
'X-Amz-Content-Sha256': hashedPayload
};
}
hmac(key, string, encoding = 'buffer') {
return crypto.createHmac('sha256', key).update(string).digest(encoding);
}
}
// 사용 예시
const signer = new AWSSigner(
process.env.AWS_ACCESS_KEY_ID,
process.env.AWS_SECRET_ACCESS_KEY,
'us-east-1'
);
4단계: Lambda API 클라이언트 생성
const LAMBDA_BASE_URL = 'https://lambda.us-east-1.amazonaws.com/2015-03-31';
const lambdaRequest = async (path, options = {}) => {
const url = new URL(`${LAMBDA_BASE_URL}${path}`);
const method = options.method || 'GET';
const body = options.body ? JSON.stringify(options.body) : null;
const signer = new AWSSigner(
process.env.AWS_ACCESS_KEY_ID,
process.env.AWS_SECRET_ACCESS_KEY,
'us-east-1'
);
const headers = signer.sign({ method, host: 'lambda.us-east-1.amazonaws.com', path }, body);
const response = await fetch(url.toString(), {
method,
headers: {
'Content-Type': 'application/json',
...headers,
...options.headers
},
body
});
if (!response.ok) {
const error = await response.json();
throw new Error(`Lambda API Error: ${error.Message}`);
}
return response.json();
};
// 함수 목록 조회 예시
const functions = await lambdaRequest('/functions');
console.log(`함수 ${functions.Functions.length}개를 찾았습니다`);
대안: AWS SDK 사용
AWS SDK를 사용하면 SigV4 서명이 자동 처리됩니다. 실제 구현 예:
const { LambdaClient, ListFunctionsCommand, CreateFunctionCommand, InvokeCommand } = require('@aws-sdk/client-lambda');
const lambda = new LambdaClient({ region: 'us-east-1' });
// 함수 목록 조회
const listCommand = new ListFunctionsCommand({});
const result = await lambda.send(listCommand);
// 함수 생성
const createCommand = new CreateFunctionCommand({
FunctionName: 'my-function',
Runtime: 'nodejs20.x',
Role: 'arn:aws:iam::123456789012:role/lambda-execution-role',
Handler: 'index.handler',
Code: {
S3Bucket: 'my-bucket',
S3Key: 'function.zip'
}
});
const fn = await lambda.send(createCommand);
함수 관리
함수 생성
Lambda 함수 생성 API 예제:
const createFunction = async (functionConfig) => {
const response = await lambdaRequest('/functions', {
method: 'POST',
body: {
FunctionName: functionConfig.name,
Runtime: functionConfig.runtime || 'nodejs20.x',
Role: functionConfig.roleArn,
Handler: functionConfig.handler || 'index.handler',
Code: {
S3Bucket: functionConfig.s3Bucket,
S3Key: functionConfig.s3Key
},
Description: functionConfig.description || '',
Timeout: functionConfig.timeout || 3,
MemorySize: functionConfig.memorySize || 128,
Environment: {
Variables: functionConfig.environment || {}
},
Tags: functionConfig.tags || {}
}
});
return response;
};
// 사용 예
const fn = await createFunction({
name: 'order-processor',
roleArn: 'arn:aws:iam::123456789012:role/lambda-execution-role',
handler: 'index.handler',
runtime: 'nodejs20.x',
s3Bucket: 'my-deployments-bucket',
s3Key: 'order-processor/v1.0.0.zip',
description: 'Process orders from SQS queue',
timeout: 30,
memorySize: 512,
environment: {
DB_HOST: 'db.example.com',
LOG_LEVEL: 'info'
}
});
console.log(`함수가 생성되었습니다: ${fn.FunctionArn}`);
코드 직접 업로드
작은 코드(50MB 미만)는 직접 업로드 가능합니다.
const fs = require('fs');
const path = require('path');
const createFunctionWithZip = async (functionName, zipPath) => {
const zipBuffer = fs.readFileSync(zipPath);
const base64Code = zipBuffer.toString('base64');
const response = await lambdaRequest('/functions', {
method: 'POST',
body: {
FunctionName: functionName,
Runtime: 'nodejs20.x',
Role: 'arn:aws:iam::123456789012:role/lambda-execution-role',
Handler: 'index.handler',
Code: {
ZipFile: base64Code
}
}
});
return response;
};
// zip -r function.zip index.js node_modules/
await createFunctionWithZip('my-function', './function.zip');
함수 코드 업데이트
새 버전 코드 배포:
const updateFunctionCode = async (functionName, updateConfig) => {
const response = await lambdaRequest(`/functions/${functionName}/code`, {
method: 'PUT',
body: {
S3Bucket: updateConfig.s3Bucket,
S3Key: updateConfig.s3Key,
Publish: updateConfig.publish || false
}
});
return response;
};
// 사용 예
const updated = await updateFunctionCode('order-processor', {
s3Bucket: 'my-deployments-bucket',
s3Key: 'order-processor/v1.1.0.zip',
publish: true // 새 버전 생성
});
console.log(`다음 버전으로 업데이트됨: ${updated.Version}`);
함수 구성 업데이트
타임아웃, 메모리, 환경변수 등 실시간 수정:
const updateFunctionConfig = async (functionName, config) => {
const response = await lambdaRequest(`/functions/${functionName}/configuration`, {
method: 'PUT',
body: {
Runtime: config.runtime,
Handler: config.handler,
Description: config.description,
Timeout: config.timeout,
MemorySize: config.memorySize,
Environment: {
Variables: config.environment
}
}
});
return response;
};
// 사용 예
const updated = await updateFunctionConfig('order-processor', {
timeout: 60,
memorySize: 1024,
environment: {
DB_HOST: 'new-db.example.com',
LOG_LEVEL: 'debug'
}
});
함수 삭제
const deleteFunction = async (functionName, qualifier = null) => {
const path = qualifier
? `/functions/${functionName}?Qualifier=${qualifier}`
: `/functions/${functionName}`;
await lambdaRequest(path, { method: 'DELETE' });
console.log(`함수 ${functionName}가 삭제되었습니다`);
};
함수 호출
동기 호출(요청-응답)
const invokeFunction = async (functionName, payload, qualifier = null) => {
const path = qualifier
? `/functions/${functionName}/invocations?Qualifier=${qualifier}`
: `/functions/${functionName}/invocations`;
const response = await lambdaRequest(path, {
method: 'POST',
headers: {
'X-Amz-Invocation-Type': 'RequestResponse',
'X-Amz-Log-Type': 'Tail'
},
body: payload
});
const result = JSON.parse(Buffer.from(response.Payload).toString());
const logs = Buffer.from(response.LogResult, 'base64').toString();
return { result, logs };
};
// 사용 예
const { result, logs } = await invokeFunction('order-processor', {
orderId: 'ORD-12345',
customerId: 'CUST-67890',
items: [
{ sku: 'PROD-001', quantity: 2 },
{ sku: 'PROD-002', quantity: 1 }
]
});
console.log(`결과: ${JSON.stringify(result)}`);
console.log(`로그:\n${logs}`);
비동기 호출(요청 후 응답 대기 없음)
const invokeAsync = async (functionName, payload) => {
const response = await lambdaRequest(`/functions/${functionName}/invocations`, {
method: 'POST',
headers: {
'X-Amz-Invocation-Type': 'Event',
'X-Amz-Log-Type': 'None'
},
body: payload
});
return {
statusCode: response.StatusCode,
executionId: response['X-Amz-Execution-Id']
};
};
// 비동기 트리거 예시
const result = await invokeAsync('email-sender', {
to: 'customer@example.com',
template: 'order-confirmation',
data: { orderId: 'ORD-12345' }
});
console.log(`비동기 호출 ID: ${result.executionId}`);
드라이 런 호출
const dryRunInvocation = async (functionName) => {
const response = await lambdaRequest(`/functions/${functionName}/invocations`, {
method: 'POST',
headers: {
'X-Amz-Invocation-Type': 'DryRun'
}
});
return response;
};
// 권한 테스트 예시
try {
await dryRunInvocation('order-processor');
console.log('호출 권한 확인 완료');
} catch (error) {
console.error('권한 거부됨:', error.message);
}
호출 응답 유형
| 호출 유형 | 동작 | 사용 사례 |
|---|---|---|
RequestResponse |
동기, 결과 대기 | API/CLI 호출 |
Event |
비동기, 즉시 반환 | 이벤트 처리, 알림 |
DryRun |
권한만 테스트 | 검증, 디버깅 |
버전 및 별칭 관리
버전 게시
const publishVersion = async (functionName, description = null) => {
const response = await lambdaRequest(`/functions/${functionName}/versions`, {
method: 'POST',
body: description ? { Description: description } : {}
});
return response;
};
// 예시
const version = await publishVersion('order-processor', 'v1.2.0 - Add tax calculation');
console.log(`게시된 버전: ${version.Version}`);
별칭 생성
const createAlias = async (functionName, aliasName, version, description = null) => {
const response = await lambdaRequest(`/functions/${functionName}/aliases`, {
method: 'POST',
body: {
Name: aliasName,
FunctionVersion: version,
Description: description
}
});
return response;
};
// 프로덕션 별칭 예시
const prodAlias = await createAlias('order-processor', 'prod', '5', 'Production version');
console.log(`별칭 ARN: ${prodAlias.AliasArn}`);
라우팅 구성으로 트래픽 전환
const updateAliasWithRouting = async (functionName, aliasName, routingConfig) => {
const response = await lambdaRequest(`/functions/${functionName}/aliases/${aliasName}`, {
method: 'PUT',
body: {
RoutingConfig: {
AdditionalVersionWeights: routingConfig
}
}
});
return response;
};
// 버전 6에 10% 트래픽, 나머지는 버전 5
await updateAliasWithRouting('order-processor', 'prod', {
'6': 0.1
});
// 검증 후 100%로 전환
await updateAliasWithRouting('order-processor', 'prod', {});
별칭 사용 사례
| 별칭 | 버전 | 목적 |
|---|---|---|
dev |
$LATEST | 개발 테스트 |
staging |
최근 테스트 | QA 검증 |
prod |
안정 버전 | 프로덕션 트래픽 |
blue |
현재 | 블루/그린 배포 |
green |
새 버전 | 블루/그린 배포 |
이벤트 소스 매핑
SQS 트리거 생성
const createSQSEventSource = async (functionName, queueArn, batchSize = 10) => {
const response = await lambdaRequest('/event-source-mappings', {
method: 'POST',
body: {
EventSourceArn: queueArn,
FunctionName: functionName,
BatchSize: batchSize,
Enabled: true
}
});
return response;
};
// 예시
const mapping = await createSQSEventSource(
'order-processor',
'arn:aws:sqs:us-east-1:123456789012:orders-queue',
10
);
console.log(`이벤트 소스가 생성됨: ${mapping.UUID}`);
DynamoDB 스트림 트리거 생성
const createDynamoDBEventSource = async (functionName, streamArn, startingPosition = 'LATEST') => {
const response = await lambdaRequest('/event-source-mappings', {
method: 'POST',
body: {
EventSourceArn: streamArn,
FunctionName: functionName,
StartingPosition: startingPosition,
BatchSize: 100,
BisectBatchOnFunctionError: true,
MaximumRetryAttempts: 3
}
});
return response;
};
// 예시
await createDynamoDBEventSource(
'user-analytics',
'arn:aws:dynamodb:us-east-1:123456789012:table/Users/stream/2026-03-25T00:00:00.000'
);
이벤트 소스 유형
| 소스 | 사용 사례 | 배치 지원 |
|---|---|---|
| SQS | 메시지 큐 | 예 (1-10) |
| Kinesis | 실시간 스트림 | 예 (1-10,000) |
| DynamoDB | 데이터베이스 변경 | 예 (1-1,000) |
| S3 | 객체 이벤트 | 아니요 (1개/이벤트) |
| EventBridge | 이벤트 라우팅 | 예 |
| API Gateway | HTTP API | 아니요 |
| 스케줄 | 크론 작업 | 아니요 |
레이어 관리
레이어 생성
const createLayer = async (layerName, layerConfig) => {
const response = await lambdaRequest('/layers', {
method: 'POST',
body: {
LayerName: layerName,
Description: layerConfig.description,
CompatibleRuntimes: layerConfig.runtimes,
Content: {
S3Bucket: layerConfig.s3Bucket,
S3Key: layerConfig.s3Key
}
}
});
return response;
};
// 예시
const layer = await createLayer('shared-utils', {
description: 'Shared utilities and dependencies',
runtimes: ['nodejs20.x', 'nodejs18.x'],
s3Bucket: 'my-layers-bucket',
s3Key: 'shared-utils/v1.zip'
});
console.log(`레이어 ARN: ${layer.LayerArn}`);
함수에서 레이어 사용
const createFunctionWithLayers = async (functionConfig) => {
const response = await lambdaRequest('/functions', {
method: 'POST',
body: {
FunctionName: functionConfig.name,
Runtime: functionConfig.runtime,
Role: functionConfig.roleArn,
Handler: functionConfig.handler,
Code: {
S3Bucket: functionConfig.s3Bucket,
S3Key: functionConfig.s3Key
},
Layers: functionConfig.layers
}
});
return response;
};
// 예시
await createFunctionWithLayers({
name: 'api-handler',
roleArn: 'arn:aws:iam::123456789012:role/lambda-execution-role',
handler: 'index.handler',
runtime: 'nodejs20.x',
s3Bucket: 'my-deployments-bucket',
s3Key: 'api-handler/v1.0.0.zip',
layers: [
'arn:aws:lambda:us-east-1:123456789012:layer:shared-utils:1',
'arn:aws:lambda:us-east-1:123456789012:layer:aws-sdk:3'
]
});
동시성 및 스케일링
예약된 동시성 설정
const putFunctionConcurrency = async (functionName, reservedConcurrentExecutions) => {
const response = await lambdaRequest(`/functions/${functionName}/concurrency`, {
method: 'PUT',
body: {
ReservedConcurrentExecutions: reservedConcurrentExecutions
}
});
return response;
};
// 100개 동시성 예약
await putFunctionConcurrency('order-processor', 100);
계정 동시성 제한
| 계정 유형 | 기본 제한 | 증대 가능 여부 |
|---|---|---|
| 프리 티어 | 1,000 | 예 |
| 종량제 | 1,000 | 예 |
| 엔터프라이즈 | 1,000+ | 사용자 지정 제한 |
프로덕션 배포 체크리스트
- [ ] AWS SDK로 자동 SigV4 서명
- [ ] 별칭 기반 버전 관리
- [ ] 예약된 동시성 구성
- [ ] 비동기 호출 데드 레터 큐(DLQ) 설정
- [ ] X-Ray 트레이싱 활성화
- [ ] VPC 네트워크 구성
- [ ] 구조화된 로그(JSON) 적용
- [ ] CloudWatch 경보 구성
- [ ] 레이어로 종속성 공유
- [ ] 블루/그린 배포 전략 구현
실제 사용 사례
API 백엔드
한 SaaS 기업의 서버리스 REST API 구축 사례:
- 과제: 트래픽 변동, 예측 불가한 스케일
- 솔루션: Lambda + API Gateway 자동 스케일링
- 결과: 99.99% 가동, EC2 대비 60% 비용 절감
핵심 구현:
- 리소스별 Lambda 함수
- API Gateway 라우팅 및 인증
- DynamoDB 데이터 저장소
- 프로비저닝된 동시성으로 일관된 지연 시간
이벤트 처리 파이프라인
전자상거래 플랫폼의 주문 이벤트 처리:
- 과제: 판매 이벤트로 인한 주문 급증
- 솔루션: SQS + Lambda 배치 처리
- 결과: 주문 손실 제로, 10배 트래픽 탄력 처리
핵심 구현:
- SQS 큐로 주문 버퍼링
- Lambda에서 배치(10개씩) 처리
- DLQ로 실패 메시지 관리
- CloudWatch로 큐 깊이 모니터링
결론
AWS Lambda API는 실전 서버리스 컴퓨팅을 위한 강력한 도구입니다. 핵심 요점:
- SigV4 기반 IAM 인증(AWS SDK 권장)
- 동기/비동기 호출 패턴
- 버전/별칭 기반 배포 관리
- 다양한 이벤트 소스 매핑
- 레이어를 통한 코드 및 종속성 공유
- Apidog는 API 테스트 및 팀 협업을 간소화합니다
FAQ 섹션
Lambda API로 어떻게 인증하나요?
Signature Version 4 서명을 사용하는 AWS IAM 자격 증명을 사용하세요. AWS SDK는 서명을 자동으로 처리합니다.
동기 및 비동기 호출의 차이점은 무엇인가요?
동기식(RequestResponse)은 함수 완료를 기다리고 결과를 반환합니다. 비동기식(Event)은 요청을 대기열에 넣고 즉시 반환합니다.
Lambda 버전은 어떻게 작동하나요?
게시된 각 버전은 함수의 불변 스냅샷입니다. 별칭을 사용하여 특정 버전을 가리키고 트래픽 전환을 활성화하세요.
Lambda 레이어는 무엇인가요?
레이어는 함수 코드와 별도로 코드 및 종속성을 패키징하여 여러 함수에서 공유 라이브러리를 사용할 수 있도록 합니다.
콜드 스타트를 줄이려면 어떻게 해야 하나요?
지연 시간에 민감한 함수에는 프로비저닝된 동시성, 더 작은 배포 패키지, 컴파일된 언어(Go, Rust)를 사용하세요.
예약된 동시성이란 무엇인가요?
예약된 동시성은 특정 함수에 대한 실행 슬롯을 보장하여 시끄러운 이웃 문제를 방지합니다.
S3에서 Lambda를 트리거할 수 있나요?
예, S3 이벤트 알림을 구성하여 객체 생성/삭제 시 Lambda를 호출할 수 있습니다.
Top comments (0)