☁️ 클라우드

AWS Lambda

AWS의 서버리스 컴퓨팅 서비스

📖 상세 설명

AWS Lambda는 서버를 프로비저닝하거나 관리하지 않고 코드를 실행할 수 있는 서버리스 컴퓨팅 서비스입니다. 코드를 업로드하면 AWS가 자동으로 인프라를 관리하고, 요청이 들어올 때만 코드를 실행합니다. 실행 시간(밀리초 단위)과 메모리 사용량 기준으로만 과금됩니다.

2014년 re:Invent에서 출시된 Lambda는 "서버리스" 패러다임을 대중화시킨 선구자입니다. 초기에는 Node.js만 지원했지만, 현재는 Python, Java, Go, .NET, Ruby 등 다양한 런타임과 컨테이너 이미지까지 지원합니다. 월 100만 건 요청과 40만 GB-초 컴퓨팅 시간은 프리 티어로 무료입니다.

Lambda는 이벤트 기반 아키텍처의 핵심 구성요소입니다. S3 파일 업로드, DynamoDB 스트림, API Gateway 요청, SQS 메시지, EventBridge 스케줄 등 200개 이상의 AWS 서비스 이벤트를 트리거로 사용할 수 있습니다. 최대 15분까지 실행 가능하고, 메모리는 128MB~10GB, vCPU는 메모리에 비례해 자동 할당됩니다.

실무에서 Lambda는 API 백엔드, 파일 처리, 데이터 변환, 스케줄 작업, 실시간 스트림 처리 등 다양한 용도로 활용됩니다. 최근에는 Lambda SnapStart로 Java 콜드 스타트가 90% 개선되었고, Lambda@Edge와 CloudFront Functions로 엣지 컴퓨팅도 지원합니다.

💻 코드 예제

# Lambda 핸들러 함수 예제 (Python)
import json
import boto3
import logging
from datetime import datetime

logger = logging.getLogger()
logger.setLevel(logging.INFO)

# Lambda 컨테이너 재사용 시 연결 유지 (콜드 스타트 최적화)
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('users')

def lambda_handler(event, context):
    """
    API Gateway에서 호출되는 Lambda 핸들러
    event: API Gateway 이벤트 객체
    context: Lambda 실행 컨텍스트 (요청 ID, 남은 시간 등)
    """
    logger.info(f"Request ID: {context.aws_request_id}")
    logger.info(f"Remaining time: {context.get_remaining_time_in_millis()}ms")

    try:
        # API Gateway 이벤트 파싱
        body = json.loads(event.get('body', '{}'))
        user_id = event['pathParameters']['userId']

        # DynamoDB 조회
        response = table.get_item(Key={'userId': user_id})

        if 'Item' not in response:
            return {
                'statusCode': 404,
                'headers': {'Content-Type': 'application/json'},
                'body': json.dumps({'error': 'User not found'})
            }

        return {
            'statusCode': 200,
            'headers': {
                'Content-Type': 'application/json',
                'Access-Control-Allow-Origin': '*'
            },
            'body': json.dumps(response['Item'], default=str)
        }

    except Exception as e:
        logger.error(f"Error: {str(e)}")
        return {
            'statusCode': 500,
            'body': json.dumps({'error': 'Internal server error'})
        }


# S3 이벤트 트리거 예제
def s3_handler(event, context):
    """S3 파일 업로드 시 트리거되는 핸들러"""
    s3_client = boto3.client('s3')

    for record in event['Records']:
        bucket = record['s3']['bucket']['name']
        key = record['s3']['object']['key']

        logger.info(f"Processing: s3://{bucket}/{key}")

        # 파일 처리 로직...
        # 예: 이미지 리사이즈, 데이터 변환 등

    return {'processed': len(event['Records'])}

🗣️ 실무에서 이렇게 말하세요

💬 아키텍처 설계에서
"이 API는 호출량이 예측 불가능하고 피크 타임에 순간적으로 1000 TPS까지 올라가니까 Lambda가 적합해요. 콜드 스타트가 걱정되면 Provisioned Concurrency로 10개 정도 미리 띄워두면 되고, 비용은 예상 트래픽 기준으로 EC2 대비 60% 정도 절감될 겁니다."
💬 면접에서
"Lambda 기반 서버리스 아키텍처를 설계하고 운영한 경험이 있습니다. API Gateway + Lambda + DynamoDB 조합으로 REST API를 구축했고, SQS와 Lambda를 연동해서 비동기 처리 파이프라인을 만들었어요. 콜드 스타트 최적화를 위해 Python 대신 Node.js를 선택하고, 핸들러 바깥에서 DB 커넥션을 초기화하는 패턴을 적용했습니다."
💬 트러블슈팅 상황에서
"Lambda 동시 실행 한도가 1000개인데 지금 다 쓰고 있어서 throttling이 발생하고 있어요. Reserved Concurrency를 핵심 함수에 먼저 설정하고, 덜 중요한 함수는 SQS로 버퍼링해서 처리 속도를 조절합시다. 장기적으로는 계정 한도 증가 요청도 해야 해요."

⚠️ 흔한 실수 & 주의사항

콜드 스타트를 무시한 설계

Lambda는 일정 시간 요청이 없으면 컨테이너가 종료됩니다. 다시 시작할 때 콜드 스타트(Node.js 100-200ms, Python 200-400ms, Java 2-5초)가 발생합니다. 지연 시간에 민감한 API는 Provisioned Concurrency를 사용하거나 SnapStart(Java)를 활성화하세요.

핸들러 안에서 리소스 초기화

DB 연결, SDK 클라이언트를 핸들러 함수 안에서 생성하면 매 호출마다 초기화됩니다. 글로벌 스코프에서 한 번만 생성하면 warm start 시 재사용되어 성능이 크게 향상됩니다.

올바른 접근법

ARM64(Graviton2) 아키텍처를 사용하면 x86 대비 최대 34% 가성비가 좋아집니다. 메모리를 늘리면 vCPU도 비례해서 증가하니, CPU 바운드 작업은 메모리를 충분히 할당하세요. 1,769MB에서 1 vCPU가 할당됩니다.

🔗 관련 용어

📚 더 배우기