📦 서버리스 아키텍처

120

왜 서버리스 아키텍처를 배울까?

여러분이 스타트업의 백엔드 개발자라고 상상해 보세요. CEO가 새로운 주문 처리 시스템을 만들어 달라고 합니다. 요구사항은 이렇습니다:

서버리스 아키텍처 전체 구성도 — API Gateway + Lambda + DynamoDB
  1. 고객이 주문을 하면 데이터 검증 (가격, 재고, 주소 형식)
  2. 검증 통과 시 결제 처리 (외부 PG사 API 호출)
  3. 결제 성공 시 확인 이메일 발송
  4. 어느 단계에서든 실패하면 자동 재시도, 그래도 실패하면 관리자에게 알림

전통적인 방식이라면 EC2 서버를 띄우고, 워커 프로세스를 관리하고, 메시지 큐를 직접 운영해야 합니다. 서버가 다운되면? 트래픽이 갑자기 10배로 늘면? 이 모든 상황에 대한 대비를 직접 해야 합니다.

서버리스 아키텍처는 이 모든 인프라 관리 부담을 AWS에 맡기고, 개발자는 비즈니스 로직에만 집중하는 접근법입니다.

이 실습에서 만들 시스템의 핵심 서비스 역할은 다음과 같습니다:

서비스역할전통 방식 대체
Lambda각 단계의 비즈니스 로직 실행EC2 + 애플리케이션 서버
Step Functions워크플로 순서 제어 + 에러 핸들링직접 구현한 오케스트레이터
SQS비동기 메시지 전달 + 버퍼링RabbitMQ / Redis Queue
SNS이벤트 알림 (이메일, SMS)직접 구현한 알림 시스템

오늘 이 네 가지 서비스를 조합하여 프로덕션 수준의 서버리스 워크플로를 직접 구축해 보겠습니다.

실습을 시작하기 전에 AWS 콘솔에 로그인되어 있는지 확인하세요. 리전은 ap-northeast-2 (서울) 을 사용합니다.

아키텍처 개요

서버리스 vs 전통적 아키텍처 비용 비교

서버리스 워크플로

다이어그램 로딩 중...
서버리스 워크플로 처리 흐름

비용 예측

비용 계산기

2시간
0h24h
Lambda

100만 요청당 $0.20 + 실행시간

$0.0040
Step Functions

상태 전환 1,000당 $0.025

$0.0040
SQS

요청 100만당 $0.40

$0.0020
SNS

요청 100만당 $0.50

$0.0020
예상 총 비용$0.0120

* 실제 비용은 AWS 요금 정책에 따라 달라질 수 있습니다.

Step 1: Lambda 함수 작성

먼저 워크플로의 각 단계를 담당할 Lambda 함수 3개를 만듭니다. 각 함수는 독립적으로 동작하며, Step Functions가 순서를 제어합니다.

  1. Lambda 콘솔 → 함수 생성 → 새로 작성
  2. 함수 이름: lab-serverless-validate, 런타임: Python 3.12
  3. 아래 코드를 붙여넣습니다 — 입력 데이터의 필수 필드를 검증하고 결과를 반환합니다:
    코드
    def lambda_handler(event, context):
        required = ['name', 'email', 'amount']
        missing = [f for f in required if f not in event]
        if missing:
            raise ValueError(f"필수 필드 누락: {', '.join(missing)}")
        if event['amount'] <= 0:
            raise ValueError("금액은 0보다 커야 합니다")
        return {**event, 'validated': True, 'step': 'validate'}
  4. Deploy 클릭 후, 테스트 탭에서 {"name":"홍길동","email":"test@example.com","amount":10000} 입력 후 실행
  5. 같은 방식으로 lab-serverless-process 함수 생성 — 결제를 시뮬레이션합니다:
    코드
    import uuid, datetime
    def lambda_handler(event, context):
        return {**event, 'orderId': str(uuid.uuid4())[:8],
                'processedAt': datetime.datetime.now().isoformat(),
                'step': 'process'}
  6. lab-serverless-notify 함수 생성 — 처리 결과를 요약합니다:
    코드
    def lambda_handler(event, context):
        return {'message': f"주문 {event['orderId']} 처리 완료",
                'email': event['email'], 'step': 'notify'}
  7. 세 함수 모두 테스트 이벤트로 동작 확인
코드
# 예시: validate 함수 생성
aws lambda create-function \
  --function-name lab-serverless-validate \
  --runtime python3.12 \
  --role arn:aws:iam::ACCOUNT_ID:role/lambda-execution-role \
  --handler lambda_function.lambda_handler \
  --zip-file fileb://validate.zip

Step 2: Step Functions 상태 머신 설계

Step Functions는 Lambda 함수들의 실행 순서, 에러 핸들링, 재시도 정책을 시각적으로 관리합니다.

진행률 0/8
  1. 1AWS 콘솔에서 Step Functions 검색 후 클릭
  2. 2상태 머신 생성 클릭
  3. 3코드로 작성 선택 (Workflow Studio보다 정확한 제어 가능)
  4. 4유형: 표준 선택 (Express는 5분 이내 작업에 적합)
  5. 5정의에 아래 ASL(Amazon States Language) JSON을 붙여넣습니다: { "StartAt": "ValidateInput", "States": { "ValidateInput": { "Type": "Task", "Resource": "arn:aws:lambda:ap-northeast-2:ACCOUNT_ID:function:lab-serverless-validate", "Next": "ProcessOrder", "Retry": [{"ErrorEquals": ["States.TaskFailed"], "MaxAttempts": 2, "BackoffRate": 2}], "Catch": [{"ErrorEquals": ["States.ALL"], "Next": "HandleError"}] }, "ProcessOrder": { "Type": "Task", "Resource"
  6. 6ACCOUNT_ID를 본인의 AWS 계정 ID로 교체합니다 (콘솔 우측 상단에서 확인)
  7. 7이름: lab-serverless-workflow → 상태 머신 생성 클릭
  8. 8실행 역할은 새 역할 생성 선택 — Step Functions가 Lambda를 호출할 권한이 자동 부여됩니다

Step Functions Workflow Studio를 사용하면 드래그 앤 드롭으로 상태 머신을 시각적으로 설계할 수 있습니다. ASL(Amazon States Language)을 직접 작성하지 않아도 됩니다. 하지만 Retry/Catch 같은 세밀한 설정은 코드 편집기가 더 편리합니다.

Step 3: SQS 큐 생성 및 연동

SQS는 서비스 간 메시지를 안전하게 전달하는 버퍼 역할을 합니다. 처리 결과를 SQS에 넣으면 다른 서비스가 자신의 속도로 소비할 수 있습니다.

  1. AWS 콘솔에서 SQS 검색 후 클릭
  2. 대기열 생성 클릭
  3. 유형: 표준 대기열 선택 (순서보다 처리량이 중요한 경우)
  4. 이름: lab-serverless-queue
  5. 구성 섹션에서:
    • 가시성 제한 시간: 30초 (메시지를 가져간 후 처리 완료까지 기다리는 시간)
    • 메시지 보존 기간: 4일 (처리되지 않은 메시지가 보관되는 기간)
    • 배달 지연: 0초 (메시지가 즉시 소비 가능하도록)
  6. 대기열 생성 클릭
  7. 생성된 큐의 URL을 복사합니다 (Lambda에서 메시지를 보낼 때 사용)
코드
aws sqs create-queue \
  --queue-name lab-serverless-queue \
  --attributes VisibilityTimeout=30,MessageRetentionPeriod=345600

Step 4: SNS 토픽 연동

SNS는 "발행-구독(Pub/Sub)" 패턴으로 알림을 전송합니다. 한 번 발행하면 이메일, SMS, Lambda 등 여러 구독자에게 동시에 전달됩니다.

진행률 0/8
  1. 1AWS 콘솔에서 SNS 검색 후 클릭
  2. 2토픽 생성 → 유형: 표준 선택
  3. 3토픽 이름: lab-serverless-notification → 토픽 생성 클릭
  4. 4토픽 상세 페이지에서 구독 생성 클릭
  5. 5프로토콜: 이메일 선택, 엔드포인트: 본인 이메일 주소 입력 → 구독 생성 클릭
  6. 6이메일 받은편지함에서 "AWS Notification - Subscription Confirmation" 메일을 열어 Confirm subscription 링크 클릭
  7. 7SNS 콘솔에서 구독 상태가 확인됨으로 변경되었는지 확인
  8. 8메시지 발행 버튼으로 테스트 메시지 전송 → 이메일 수신 확인

Step 5: Lambda Layer 구성

여러 Lambda 함수에서 공통으로 사용하는 유틸리티 코드를 Layer로 분리하면 코드 중복을 줄이고 일괄 업데이트할 수 있습니다.

진행률 0/6
  1. 1로컬 터미널에서 공통 유틸리티를 작성합니다: mkdir -p python/lib/python3.12/site-packages # python/lib/python3.12/site-packages/utils.py 파일 작성: # def format_response(status, body): ...
  2. 2디렉토리를 zip으로 패키징: zip -r layer.zip python/
  3. 3Lambda 콘솔 → 왼쪽 메뉴 계층 → 계층 생성 클릭
  4. 4이름: lab-common-utils, 설명: "공통 유틸리티", ZIP 파일 업로드
  5. 5호환 런타임: Python 3.12 선택 → 생성 클릭
  6. 6각 Lambda 함수 설정 → 계층 섹션 → 계층 추가 → lab-common-utils 선택

Step 6: Cold Start 테스트

Lambda의 실행 성능을 이해하는 핵심 개념인 Cold Start와 Warm Start를 직접 체험합니다.

진행률 0/6
  1. 1Step Functions 콘솔에서 lab-serverless-workflow 선택 → 실행 시작 클릭
  2. 2입력값: {"name":"테스트","email":"test@example.com","amount":50000} 입력 후 실행
  3. 3실행 그래프에서 각 단계의 소요 시간을 확인합니다 — 첫 번째 실행이 가장 느립니다 (Cold Start)
  4. 4CloudWatch Logs에서 각 Lambda의 REPORT 줄을 찾아 Init Duration 값을 확인합니다
  5. 530초 이내에 동일 실행을 3회 반복합니다 — Init Duration이 사라지는 것을 확인합니다 (Warm Start)
  6. 65분 이상 대기 후 다시 실행하면 Cold Start가 다시 발생합니다

Cold Start를 줄이는 방법: 1) 함수 패키지 크기 최소화, 2) 전역 범위에서 SDK 클라이언트 초기화, 3) Provisioned Concurrency 설정 (프로덕션 환경). 이 실습에서는 1, 2번을 적용했습니다.

Step 7: 전체 워크플로 통합 테스트

모든 구성 요소가 올바르게 연결되어 동작하는지 체계적으로 검증합니다.

진행률 0/8
  1. 1Step Functions 콘솔 → lab-serverless-workflow 선택 → 실행 시작
  2. 2정상 입력 테스트: {"name":"홍길동","email":"test@example.com","amount":50000}
  3. 3실행 그래프에서 모든 단계가 초록색(성공) 인지 확인
  4. 4각 단계를 클릭하여 입력/출력 JSON이 다음 단계로 올바르게 전달되는지 확인
  5. 5검증 실패 테스트: {"name":"홍길동","email":"test@example.com","amount":-100}
  6. 6HandleError 상태로 분기되는지 확인
  7. 7SQS 콘솔에서 lab-serverless-queue 대기열에 메시지가 도착했는지 확인 (메시지 수신 후 확인)
  8. 8SNS 구독 이메일로 알림이 수신되었는지 확인

Step Functions 실행 상세 페이지에서 이벤트 기록 탭을 열면 각 상태 전환의 타임스탬프, 입력, 출력을 시간순으로 볼 수 있습니다. 디버깅할 때 매우 유용합니다.

트러블슈팅

Step Functions 실행이 "Failed" 상태로 끝나면: 실행 상세에서 어떤 단계에서 실패했는지 확인하세요. 가장 흔한 원인은 ASL 정의에서 ACCOUNT_ID를 실제 계정 ID로 교체하지 않은 것입니다. Lambda ARN이 정확한지 다시 확인하세요.

"States.TaskFailed" 에러가 나오면: 해당 Lambda 함수를 직접 테스트해 보세요. Lambda 콘솔 → 함수 선택 → 테스트 탭에서 Step Functions이 전달하는 것과 동일한 JSON 입력으로 실행합니다.

SNS 이메일이 오지 않으면:

  1. 스팸함을 확인하세요. 2) 구독 확인 링크를 클릭했는지 확인하세요.
  2. SNS 콘솔에서 구독 상태가 "확인됨"인지 확인하세요. "보류 중 확인"이면 아직 확인 링크를 클릭하지 않은 것입니다.

Step Functions 실행 역할 관련 에러: Step Functions이 Lambda를 호출하려면 실행 역할에 lambda:InvokeFunction 권한이 필요합니다. 상태 머신 생성 시 "새 역할 생성"을 선택했다면 자동으로 부여됩니다. 수동으로 역할을 만든 경우 AWSLambdaRole 정책을 연결하세요.

핵심 개념 확인

확장 아이디어

기본 워크플로가 완성되었으니, 더 발전시켜 보세요:

  1. Parallel 상태 활용: 결제 처리와 재고 확인을 동시에 실행하도록 Parallel 상태 추가
  2. Choice 상태로 분기: 금액이 10만 원 이상이면 추가 검증 단계를 거치도록 조건 분기
  3. SQS DLQ 연동: 실패한 메시지를 DLQ(Dead Letter Queue)로 보내고 별도 알림
  4. DynamoDB에 주문 이력 저장: 각 워크플로 실행 결과를 DynamoDB 테이블에 기록
  5. Map 상태: 여러 상품을 한 번에 처리하는 반복 실행 패턴 구현

학습 정리

핵심 치트시트

리소스 정리

실습 완료 후 반드시 아래 순서대로 리소스를 정리하여 불필요한 과금을 방지하세요.

  1. Step Functions 상태 머신 삭제
  2. Lambda 함수 3개 삭제 (validate, process, notify)
  3. Lambda Layer 삭제 (lab-common-utils)
  4. SQS 대기열 삭제 (lab-serverless-queue)
  5. SNS 토픽 삭제 (lab-serverless-notification)
  6. API Gateway 삭제 (생성한 경우)
  7. IAM 역할 정리
  8. CloudWatch 로그 그룹 삭제