Introduction

부트캠프에서 서버리스 아키텍처를 파고들며 정리한 글이다. "서버리스"는 서버가 없다는 뜻이 아니라 서버 운영의 책임을 클라우드에 넘긴다는 뜻인데, 그렇다면 그 대가로 무엇을 얻고 무엇을 잃는가? 이 글은 AWS Lambda의 실행 모델과 API Gateway의 구조를 먼저 해부한 뒤, 자동 확장과 NoOps라는 장점, 그리고 콜드 스타트 · 실행 시간 제한 · 상태 관리라는 세 가지 한계와 그 해결 전략을 차례로 정리한다.

Lambda

AWS Lambda는 서버를 직접 관리하지 않고 코드만 실행할 수 있는 서버리스 컴퓨팅 서비스다.

서버리스

"서버리스(serverless)"라는 개념은 서버가 없다는 것이 아니라, 인프라의 프로비저닝, 운영체제 패치, 네트워크 계층 관리 등의 운영 책임을 AWS가 전적으로 담당한다는 것을 의미한다. 사용자는 애플리케이션 코드에만 집중할 수 있으며, AWS는 고가용성high availability1과 확장성을 보장하는 인프라를 백그라운드에서 운영한다. 기존의 온프레미스 환경에서는 서버의 크기에 변화를 주기도 어렵고 서버를 관리할 인원도 따로 필요했지만, 서버리스 환경에서는 기존 서버 기반 모델로는 달성하기 어려운 수준의 자동 확장성과 운영 편의성이 존재한다.

핵심 아키텍처 및 실행 환경

핵심 아키텍처

AWS Lambda의 보안 및 격리 모델의 핵심인 Firecracker는 AWS가 개발한 오픈 소스 가상화 기술로, KVM2을 기반으로 하여 경량 마이크로VM(microVM)을 생성한다. 각 Lambda 함수는 고유한 마이크로VM 내에서 실행되며, 이는 전통적인 컨테이너 기술보다 더 강력한 하드웨어 수준의 격리를 제공하여 멀티 테넌트3 환경에서도 데이터 유출이나 이웃 소음(noisy neighbor)4 문제를 방지한다. Firecracker는 수 밀리초(ms) 내에 VM을 시작할 수 있도록 최적화되어 있어, 트래픽 급증 시에도 신속하게 실행 환경을 프로비저닝할 수 있다.

운영 모델

Lambda는 워크로드의 특성에 따라 세 가지 운영 모델을 제공한다.

기능표준 Lambda (Standard)관리형 인스턴스 (Managed)내구성 함수 (Durable)
기반 인프라Firecracker MicroVMEC2 Nitro 컨테이너MicroVM
동시성 처리1 요청 / 1 환경다중 요청 / 1 환경1 요청 / 1 환경 (재개 시)
최대 실행 시간15분 (900초)인스턴스 가동 시간 (무제한)1년 (일시 중단 포함)
확장 기준이벤트 발생량 (즉시)CPU/메모리 사용률 (비동기)이벤트 및 워크플로 진행
상태 관리StatelessStatelessStateful
주요 비용 요소실행 시간(ms) + 요청 수인스턴스 비용 + 15% 수수료작업 수(Ops) + 스토리지

표준 Lambda(Standard Ephemeral Function-as-a-Service)는 가장 전통적인 모델로, 상태를 저장하지 않고 이벤트에 따라 즉시 확장되는 단기 실행 함수다. 실행 환경당 1개의 요청만 처리하며, 요청이 증가하면 실행 환경의 수도 그에 맞게 1:1로 증가한다. 트래픽에 따라 밀리초 단위로 반응하며 10초마다 최대 1,000개의 동시 실행까지 확장할 수 있고, 유휴 상태에서는 실행 환경의 수가 0으로 축소되어 비용이 발생하지 않는다.

Lambda Managed Instances(EC2 기반 관리형 인스턴스)는 서버리스의 운영 편의성과 EC2 인스턴스의 유연성 및 경제성을 결합한 모델이다. 사용자가 선택한 EC2 인스턴스 타입(예: m8g.large, m7a.xlarge) 위에서 실행되며, 이는 AWS가 관리하는 용량 공급자(capacity provider)5를 통해 구성된다. 가장 큰 특징은 하나의 실행 환경(인스턴스)이 동시에 여러 요청을 처리할 수 있다는 점이며, 이는 I/O 대기 시간이 많은 워크로드에서 리소스 활용률을 극대화한다. 요청 수에 따라 즉시 확장되는 표준 모델과 달리 CPU 사용률과 같은 리소스 지표를 기반으로 비동기적으로 확장되므로, 콜드 스타트는 없지만 급격한 트래픽 증가 시 스케일링 지연이 발생할 수 있어 최소 용량 유지가 필요하다. 요청당 처리 시간에 대한 과금이 없는 대신, 프로비저닝된 EC2 인스턴스 비용과 15%의 관리 수수료를 지불한다.

Lambda Durable Functions(내구성 함수)는 상태 관리의 복잡성을 해결하기 위해 도입된 모델로, 코드를 통해 장기적으로 실행되는 워크플로우를 정의할 수 있다.

그림 1. Lambda Durable Functions의 실행 흐름.

함수 실행 중 wait()step()을 호출하면 상태가 저장되고 실행이 일시 중단된다. 재개 시에는 처음부터 코드를 다시 실행하지만, 이미 완료된 단계는 저장된 결과를 사용하여 건너뛴다. 최대 1년까지 실행 상태를 유지하며 대기할 수 있고, 대기 상태에서는 컴퓨팅 비용이 발생하지 않는 대신 상태 저장 스토리지 비용과 작업(operation) 단위 과금이 적용된다.

실행 환경

Lambda의 실행 과정은 크게 초기화(Init), 호출(Invoke), 종료(Shutdown)의 세 단계로 구분된다.

  • 초기화 단계(Init Phase): 함수가 처음 호출되거나 확장이 필요할 때 발생한다. AWS는 새로운 실행 환경을 생성하고, 함수 코드를 다운로드하며, 런타임(예: Node.js, Python, Java)을 시작한 뒤 핸들러 외부의 초기화 코드를 실행한다. 이 과정에서 발생하는 지연을 콜드 스타트(cold start)라고 부른다.
  • 호출 단계(Invoke Phase): 핸들러 함수가 이벤트를 처리하는 단계다. 표준 Lambda에서는 하나의 실행 환경이 한 번에 하나의 요청만 처리하지만, 관리형 인스턴스 모델에서는 하나의 환경이 동시에 여러 요청을 처리할 수 있다.
  • 종료 단계(Shutdown Phase): 일정 시간 동안 요청이 없으면 실행 환경은 동결되고 결국 회수된다.

API Gateway

그림 2. 클라이언트와 백엔드 서비스 사이에 위치한 API Gateway.

API Gateway는 클라이언트(웹, 모바일, IoT 디바이스 등)와 백엔드 서비스(Lambda, EC2, DynamoDB 등) 사이에 위치하여 API 요청을 관리·변환·보호해 주는 완전관리형 서비스다. 클라이언트가 백엔드의 복잡한 구성을 알 필요 없이 단일 엔드포인트를 통해 서비스에 접근할 수 있게 함으로써, 클라이언트와 서버 간의 결합도를 낮추고 독립적인 확장을 가능하게 한다. 즉 API Gateway는 중간에서 사용자의 요청을 받아 Lambda와 같은 AWS의 백엔드 서비스로 연결해 준다.

아키텍처 및 핵심 구성 요소

작동 원리

AWS API Gateway는 논리적으로 제어 플레인(control plane)과 데이터 플레인(data plane)으로 구분되어 작동한다. 제어 플레인은 API의 생성, 배포, 설정 변경 등을 관리하며, 데이터 플레인은 실제 API 트래픽을 처리하고 백엔드로 라우팅하는 역할을 담당한다. 클라이언트가 API Gateway로 요청을 보내면, Gateway는 설정된 라우팅 규칙에 따라 해당 요청을 적절한 백엔드 통합으로 전달한다. 이 과정에서 API Gateway는 다음과 같은 역할을 수행한다.

  • 라우팅 및 로드 밸런싱(routing & load balancing): 들어오는 HTTP/WebSocket 요청의 경로와 메서드를 분석하여 지정된 백엔드 리소스로 트래픽을 분산시킨다. 이는 마이크로서비스 환경에서 서비스 디스커버리와 유사한 기능을 수행하여, 클라이언트가 개별 마이크로서비스의 물리적 위치를 알 필요가 없게 한다.
  • 프로토콜 변환(protocol translation): 클라이언트 측의 웹 친화적인 프로토콜(HTTP, WebSocket)과 백엔드 서비스의 프로토콜 간의 변환을 담당한다. 예를 들어 REST API 요청을 AWS Lambda의 이벤트 객체로 변환하거나, Kinesis Data Streams6로 직접 데이터를 수집하는 역할을 수행한다.
  • 보안 경계(security boundary): 모든 요청에 대해 인증과 인가를 수행하는 중앙화된 보안 진입점 역할을 한다. 이를 통해 백엔드 서비스는 비즈니스 로직에만 집중할 수 있으며, 보안 정책의 일관성을 유지할 수 있다.

엔드포인트의 유형

API Gateway를 배포할 때는 서비스의 대상 고객 위치와 네트워크 요구 사항에 따라 세 가지 엔드포인트 유형 중 하나를 선택해야 한다.

  • 엣지 최적화(edge-optimized) 엔드포인트는 지리적으로 분산된 클라이언트를 대상으로 하는 퍼블릭 API에 적합하다. 이 유형을 선택하면 API 요청은 가장 가까운 Amazon CloudFront POP(Point of Presence)7로 라우팅된 후, AWS의 최적화된 글로벌 백본 네트워크8를 통해 API Gateway가 위치한 리전으로 전달된다. 이는 인터넷 구간에서의 지연 시간을 최소화하고 연결 안정성을 높이는 효과가 있다.
  • 리전(regional) 엔드포인트는 API Gateway와 클라이언트가 동일한 AWS 리전 내에 있거나, 이미 자체적인 CDN을 사용하고 있는 경우에 적합하다. CloudFront를 거치지 않고 리전 내에서 직접 요청이 처리되므로 불필요한 네트워크 홉(hop)9을 줄일 수 있다.
  • 프라이빗 엔드포인트는 공용 인터넷을 통해 접근할 수 없으며, 오직 Amazon VPC(Virtual Private Cloud) 내에서만 접근 가능하다. AWS PrivateLink 기술을 기반으로 인터페이스 VPC 엔드포인트10를 통해 트래픽이 안전하게 전송된다. 금융권이나 공공기관과 같이 보안 규정상 내부 네트워크 통신만 허용되는 마이크로서비스 간 통신에 필수적인 유형이다.

API의 유형

AWS API Gateway는 다양한 애플리케이션 요구 사항을 충족하기 위해 REST API, HTTP API, WebSocket API라는 세 가지 유형의 API를 제공한다.

기능 영역REST APIHTTP APIWebSocket API
프로토콜HTTP/1.1HTTP/1.1WebSocket
주요 사용 사례엔터프라이즈 통합, 퍼블릭 API 관리서버리스 프록시, 마이크로서비스실시간 채팅, 알림, 스트리밍
지연 시간 (Latency)중간 (기능 처리 오버헤드 존재)낮음 (최소한의 오버헤드)낮음 (연결 유지)
비용 구조높음 (기능 중심)낮음 (REST 대비 ~70% 절감)메시지 수 및 연결 시간 기반 과금
보안 (AWS WAF)지원함지원하지 않음지원함
네트워크 (Private API)지원함 (VPC Endpoint)지원하지 않음지원함 (VPC Endpoint)
데이터 변환 (VTL)지원함지원하지 않음지원함
인증/인가IAM, Cognito, Lambda, API KeyIAM, Lambda, JWT (Native)IAM, Lambda, API Key (제한적)
트래픽 제어사용량 계획 (Usage Plans), 스로틀링경로별/전체 스로틀링연결 및 메시지 스로틀링

REST API는 AWS API Gateway가 처음 출시되었을 때부터 제공된 유형으로, 가장 풍부한 기능 세트를 자랑한다. 복잡한 엔터프라이즈 환경에서의 통합, 세밀한 트래픽 제어, 레거시 시스템과의 연동이 필요한 경우에 주로 사용된다. API 키 관리, 클라이언트별 사용량 계획, 요청/응답 유효성 검사, AWS WAF(Web Application Firewall)와의 통합 등을 기본적으로 지원하는데, 이러한 기능들은 API를 외부에 공개하고 수익화하거나 보안 위협으로부터 보호해야 할 때 필수적이다. 또한 VTL(Velocity Template Language)을 사용하여 요청·응답 페이로드를 변환할 수 있다. 예를 들어 클라이언트가 보낸 JSON 데이터를 백엔드의 레거시 XML 기반 SOAP11 서비스가 이해할 수 있는 형식으로 변환하거나, 백엔드의 복잡한 응답에서 필요한 필드만 추출하여 클라이언트에게 전달하는 것이 가능하다. VPC 내에서만 접근 가능한 프라이빗 API를 구축할 수 있는 유일한 유형이기도 하다.

HTTP API는 REST API보다 가볍고 빠르며 비용 효율적인 대안으로 설계되었다. 주로 서버리스 워크로드(Lambda)나 최신 HTTP 백엔드를 위한 프록시 역할에 최적화되어 있다. REST API 대비 지연 시간이 낮고 비용은 약 71% 저렴하므로, 복잡한 변환 로직이나 관리 기능 없이 단순히 Lambda나 백엔드 서비스로 요청을 빠르게 전달하는 것이 목적이라면 가장 합리적인 선택이다. JWT 기반의 인증(OIDC, OAuth 2.0)을 기본적으로 지원하고 CORS 설정이 간소화되어 있으며, 스테이지 변경 사항을 자동으로 배포하는 기능도 제공한다. 그러나 API 캐싱, 사용량 계획, AWS WAF 통합, 프라이빗 엔드포인트 등 일부 고급 기능은 지원하지 않는다.

WebSocket API는 클라이언트와 서버 간에 지속적인 연결을 유지하여 실시간 양방향 통신을 가능하게 한다. 채팅 애플리케이션, 실시간 대시보드, 멀티플레이어 게임, 주식 시세 알림 등 즉각적인 데이터 전송이 필요한 시나리오에 특화되어 있다. REST API의 무상태 특성과 달리 WebSocket API는 연결 상태를 관리한다. 클라이언트가 연결을 맺으면 연결 ID가 생성되며, 서버는 이 ID를 사용하여 특정 클라이언트에게 언제든지 메시지를 푸시할 수 있다. 라우팅도 URL 경로가 아닌 메시지의 내용에 따라 이루어지는데, 라우트 선택 표현식(route selection expression)을 정의하여 메시지의 특정 속성값(예: action 필드)에 따라 다른 Lambda 함수나 통합을 실행하도록 구성한다.

서버리스 아키텍처의 장점

자동 확장(Auto-Scaling)

전통적인 EC2 기반의 Auto-Scaling Group(ASG)이 CPU 사용률이나 메모리 점유율과 같은 메트릭을 기반으로 수 분에 걸쳐 인스턴스를 추가하는 것과 달리, AWS Lambda의 확장은 개별 요청 단위로 즉각적이고 수평적으로 이루어진다.

Lambda의 동시성 모델

Lambda의 확장성을 이해하기 위한 핵심 지표는 동시성(concurrency)이다. 동시성은 특정 시점에 처리 중인 요청의 수를 의미하며, 다음과 같은 공식으로 산출된다.

Concurrency=Requests per second (RPS)×Average Duration (seconds)\text{Concurrency} = \text{Requests per second (RPS)} \times \text{Average Duration (seconds)}

요청이 들어오면 Lambda 서비스는 즉시 해당 함수를 실행할 수 있는 실행 환경(execution environment)이 준비되어 있는지 확인한다. 유휴 상태의 환경이 있다면 이를 재사용하지만, 없다면 즉시 새로운 마이크로VM을 프로비저닝하여 요청을 처리한다. 이 과정은 트래픽이 급증할 때 사실상 무제한에 가까운 속도로 확장이 가능하게 하며, 사용자는 별도의 스케일링 정책을 설정할 필요가 없다.

하지만 무한한 확장은 백엔드 자원의 고갈을 초래할 수 있으므로, AWS는 계정 수준과 함수 수준에서 동시성 제한을 둔다.

  • 예약된 동시성(reserved concurrency): 특정 함수가 사용할 수 있는 최대 동시성을 보장하며, 동시에 다른 함수가 이 한도를 침범하지 못하도록 격리하는 역할을 한다.
  • 프로비저닝된 동시성(provisioned concurrency): 미리 초기화된 실행 환경을 확보하여 콜드 스타트 없이 즉각적인 확장을 보장하는 기능이다. 이는 뒤에서 다룰 콜드 스타트 해결 전략의 핵심 요소이다.

트래픽 관리 및 보호

API Gateway는 백엔드 시스템이 과도한 트래픽으로 인해 마비되는 것을 방지하기 위해 정교한 트래픽 제어 메커니즘을 제공한다. 이는 토큰 버킷(token bucket) 알고리즘을 기반으로 작동한다.

스로틀링(throttling)은 속도(rate)와 버스트(burst)라는 두 가지 매개변수로 제어된다. 속도는 초당 허용되는 요청 수(RPS)로 버킷에 토큰이 채워지는 속도를 의미하고, 버스트는 단기간의 트래픽 급증을 처리하기 위해 버킷에 최대로 담을 수 있는 토큰의 용량이다. 예를 들어 속도가 1,000 RPS이고 버스트가 2,000인 경우, 평소에는 초당 1,000건을 처리하지만 순간적으로 2,000건까지의 요청을 허용하여 유연성을 제공한다. 한도를 초과한 요청은 429 Too Many Requests 오류와 함께 거부된다.

스로틀링 제한은 여러 단계에서 적용되며, 하위 단계의 제한이 상위 단계보다 우선한다.

  1. 클라이언트별 제한(Usage Plan): 특정 API 키를 사용하는 클라이언트에 대해 설정된 제한.
  2. 메서드별 제한: 특정 API 스테이지의 특정 메서드(예: POST /checkout)에 대해 설정된 제한.
  3. 계정 수준 제한: AWS 리전별, 계정별로 적용되는 기본 제한(일반적으로 10,000 RPS). AWS Support를 통해 상향 조정이 가능하다.

사용량 계획을 통해 API 제공자는 'Free', 'Basic', 'Premium'과 같은 등급을 나누고, 각 등급별로 요청 속도와 월간 총 요청 수(quota)를 차등 적용할 수 있다. 이는 특정 테넌트(사용자)의 과도한 사용이 다른 테넌트의 서비스 품질에 영향을 미치지 않도록 격리하는 핵심 전략이다.

NoOps

서버리스 아키텍처는 인프라 관리의 부담을 'NoOps' 수준으로 낮춘다. 이는 운영 인력이 필요 없다는 뜻이 아니라, 인프라 유지보수라는 반복적이고 가치가 낮은 작업을 제거한다는 의미이다.

인프라 추상화 및 보안 패치 자동화

EC2나 컨테이너를 직접 운영할 경우 OS 보안 패치, 런타임 버전 업그레이드, 네트워크 설정 등의 작업이 필수적이다. 반면 Lambda는 실행 환경의 기저에 있는 OS와 런타임(Node.js, Python, Java 등)의 보안 및 유지보수를 AWS가 전담한다. 예컨대 AWS는 Node.js 16, 18과 같은 오래된 런타임의 지원 종료를 관리하며, 사용자가 새로운 런타임(Node.js 20, 22 등)으로 전환할 수 있도록 명확한 로드맵과 자동화된 관리 도구를 제공한다. 이는 보안 취약점(CVE)에 대한 대응 속도를 높이고, 운영팀이 비즈니스 로직 개선에 집중할 수 있는 환경을 제공한다.

가용성(Availability) 및 내결함성(Fault Tolerance) 내장

그림 3. 여러 가용 영역에 걸쳐 동작하는 리전 레벨 서비스의 가용성.

전통적인 아키텍처에서 고가용성을 확보하려면 여러 가용 영역(AZ, Availability Zone)에 서버를 배치하고 로드 밸런서를 설정하는 복잡한 구성이 필요했다. 그러나 Lambda와 API Gateway는 기본적으로 리전(region) 레벨의 서비스로 설계되어 있어, 별도의 설정 없이도 자동으로 여러 AZ에 걸쳐 실행된다. 특정 데이터 센터에 장애가 발생하더라도 서비스는 중단 없이 다른 AZ에서 요청을 처리하므로, 아키텍처 설계 단계에서 재해 복구에 대한 부담을 획기적으로 줄여 준다.

서버리스 아키텍처의 한계

콜드 스타트

그림 4. 콜드 스타트가 발생하는 지점.

콜드 스타트는 Lambda 함수가 호출될 때 유휴 상태의 실행 환경이 없을 경우 발생하며, 기술적으로는 INIT 단계에서 소요되는 시간을 의미한다. 이 과정은 다음과 같은 세부 단계로 나뉜다.

  1. 컴퓨팅 자원 할당 및 다운로드: AWS는 함수 실행을 위해 격리된 마이크로VM을 할당하고, S3 등 내부 스토리지에서 함수 코드를 다운로드하여 압축을 해제한다. 코드 패키지의 크기가 클수록 이 단계의 지연이 길어진다.
  2. 런타임 초기화: 선택된 언어의 런타임(예: JVM, Python 인터프리터)을 부트스트랩한다. Java와 같이 무거운 런타임은 JIT 컴파일러12 초기화와 클래스 로딩으로 인해 Node.js나 Python에 비해 상대적으로 긴 시간이 소요된다.
  3. 함수 초기화 코드 실행: 핸들러 함수 밖(global scope)에 정의된 코드가 실행된다. 데이터베이스 연결 수립, 무거운 라이브러리 로딩, 설정 파일 파싱 등이 이 단계에서 수행된다.

콜드 스타트에 대응하는 전략은 크게 세 가지이고, 각각의 트레이드오프는 다음과 같다.

전략적용 대상비용 구조장점단점
SnapStartJava, Python, .NET매우 저렴 (스토리지 비용 미미)코드 변경 없이 극적인 성능 향상, 유휴 비용 없음지원 런타임 제한, 배포 시간 증가, 고유성(uniqueness) 관리 필요
Provisioned Concurrency모든 런타임높음 (유휴 자원 비용 발생)지연 시간 완전 제거, 즉각적 응답 보장높은 비용, 사전 용량 계획 필요
코드 최적화모든 런타임없음 (개발 공수)런타임 성능 및 비용 동시 개선근본적인 해결책은 아님, 개발 복잡도 증가

SnapStart

그림 5. 스냅샷을 복원해 초기화를 건너뛰는 SnapStart.

SnapStart는 콜드 스타트 문제를 근본적으로 해결하는 기능이다. 개발자가 새로운 함수 버전을 배포(publish)하면 Lambda 서비스는 자동으로 INIT 단계를 수행하여 런타임과 코드를 초기화한 뒤, 메모리와 디스크의 상태를 스냅샷(snapshot)으로 만들어 암호화하여 저장한다. 실제 요청이 들어오면 처음부터 초기화하는 대신 저장된 스냅샷을 메모리에 복원(restore)하여 실행을 시작한다.

이 방식은 초기화 시간을 수 초에서 1초 미만으로 단축시키며, 특히 Spring Boot와 같이 초기화가 무거운 프레임워크를 사용하는 Java 애플리케이션에서 최대 10배 이상의 시작 속도 향상을 기대할 수 있다. 현재 Java, Python, .NET 런타임만 지원하며, 임시 스토리지(ephemeral storage) 크기 제한이나 EFS 사용 불가 등의 제약이 있다. 또한 스냅샷 복원 시 난수 생성기가 동일한 시드 상태에서 시작될 수 있으므로, 암호화 키 생성 등 민감한 작업에는 런타임 훅을 통해 복원 후 재초기화 로직을 구현해야 한다.

프로비저닝된 동시성(Provisioned Concurrency)

그림 6. 미리 초기화된 실행 환경을 유지하는 프로비저닝된 동시성.

비용보다 성능이 절대적으로 중요한 초저지연 애플리케이션의 경우, 프로비저닝된 동시성이 가장 확실한 해결책이다. 사용자가 설정한 수만큼의 실행 환경을 항상 초기화된 상태(warm)로 유지하여, 요청이 도착하면 INIT 단계를 건너뛰고 즉시 INVOKE 단계로 진입하므로 콜드 스타트가 일어나지 않는다.

SnapStart는 스냅샷 스토리지 비용 등 미미한 추가 비용만 발생하여 가성비가 뛰어난 반면, 프로비저닝된 동시성은 유휴 상태에서도 실행 환경 유지 비용이 발생하므로 훨씬 비싸다. 따라서 트래픽 패턴이 예측 가능하거나(예: 특정 시간대에 반복되는 이벤트), SnapStart가 지원하지 않는 런타임(Node.js, Ruby 등)을 사용할 때 주로 고려해야 한다. AWS Application Auto Scaling13과 연동하여 트래픽에 따라 프로비저닝 수량을 동적으로 조절하는 것이 비용 최적화의 핵심이다.

코드 최적화 및 경량화

  • 의존성 최적화: aws-sdk 전체를 임포트하는 대신 필요한 클라이언트(예: DynamoDBClient)만 개별적으로 임포트하여 패키지 크기와 로딩 시간을 줄인다.
  • 지연 로딩(lazy loading): 잘 사용되지 않는 무거운 라이브러리는 전역 스코프가 아닌 핸들러 내부에서 조건부로 임포트하여 초기화 시간을 단축한다.
  • TCP Keep-Alive: HTTP 연결 재사용을 위해 Keep-Alive14를 활성화함으로써, 연속된 호출에서 SSL 핸드셰이크 오버헤드를 줄인다.

실행 시간 제한(Timeout)과 장기 실행 프로세스

AWS Lambda는 최대 15분, API Gateway는 최대 29초의 엄격한 실행 시간 제한을 가진다. 이는 대용량 데이터 처리나 복잡한 워크플로우를 구현할 때 큰 제약이 되며, 이를 극복하기 위한 아키텍처 패턴의 변화가 필요하다.

비동기 아키텍처(Asynchronous Architecture)로의 전환

웹 클라이언트가 29초 이상 응답을 기다리는 것은 사용자 경험상 좋지 않다. 따라서 API Gateway와 Lambda 간의 통합을 동기식에서 비동기식으로 전환하는 것이 필수적이다.

  • 스토리지 우선(storage-first) 패턴: API Gateway가 요청을 받으면 이를 즉시 처리하지 않고 SQS15나 Kinesis Data Streams와 같은 버퍼에 저장한 후, 클라이언트에게는 202 Accepted 응답과 요청 ID를 즉시 반환한다.
  • 이벤트 기반 처리: 이후 Lambda 함수가 큐에서 메시지를 꺼내어 백그라운드에서 실제 작업을 수행한다. 이 방식은 API Gateway의 29초 타임아웃을 우회할 뿐만 아니라, 트래픽 폭주 시 버퍼링을 통해 시스템을 보호하는 역할도 한다.
  • 결과 통지: 작업이 완료되면 SNS, WebSocket API, 또는 클라이언트의 폴링(polling) 엔드포인트를 통해 결과를 전달한다.

AWS Step Functions를 활용한 오케스트레이션

15분을 초과하거나 여러 단계로 이루어진 복잡한 로직은 단일 Lambda 함수로 구현하기보다 상태 머신(state machine)인 AWS Step Functions를 통해 관리해야 한다.

그림 7. Step Functions가 여러 Lambda 함수를 조율하는 모습.

  • 표준 워크플로우(Standard Workflow): 최대 1년까지 실행 상태를 유지할 수 있어, 결제 승인 대기나 인간의 승인이 필요한 프로세스, 장기 실행 ETL16 작업에 적합하다. 정확히 한 번 실행을 보장하므로 데이터 정합성이 중요한 작업에 필수적이다.
  • 동기식 익스프레스 워크플로우(Synchronous Express Workflow): API Gateway와 직접 통합되어 최대 5분 이내의 작업을 빠르게 처리하고 결과를 클라이언트에게 동기적으로 반환할 수 있다. 고성능 마이크로서비스 오케스트레이션에 적합하며, Lambda 함수 간의 직접 호출을 방지하여 결합도를 낮춘다.

Lambda와 Fargate/EC2의 연동

비디오 인코딩이나 대규모 배치 처리처럼 15분을 넘거나 고사양의 컴퓨팅 파워(GPU 등)가 필요한 작업은 Lambda만으로 처리하기 어렵다. 이때는 AWS Fargate나 최근 도입된 Lambda Managed Instances로 작업을 위임하는 전략을 사용한다.

  • Lambda-to-Fargate 핸드오버: Lambda는 가벼운 트리거 역할만 수행하고, 실제 무거운 작업은 ECS RunTask API를 호출하여 Fargate 컨테이너에서 실행한다. Fargate는 시간 제한이 없고 vCPU/메모리 구성을 자유롭게 할 수 있어 Lambda의 한계를 보완한다.
  • Lambda Managed Instances: Lambda 함수를 사용자가 지정한 EC2 인스턴스 위에서 실행할 수 있게 해 준다. 개발자는 Lambda의 프로그래밍 모델을 유지하면서도 EC2의 유연성과 비용 효율성을 누릴 수 있다. 특히 지속적으로 실행되는 워크로드나 15분 이상의 작업이 필요한 경우, 표준 Lambda보다 경제적이고 강력한 대안이 된다.

상태 관리(State Management)의 복잡성과 데이터 일관성

서버리스 함수는 본질적으로 상태가 없다(stateless). 함수가 종료되면 로컬 변수나 /tmp 디렉터리의 데이터는 사라지며, 다음 호출에서 이를 재사용할 수 있다는 보장이 없다. 따라서 애플리케이션의 상태를 외부에서 관리하고, 분산 환경에서의 데이터 일관성을 유지하는 전략이 중요하다.

외부 지속성(Persistence) 계층 활용

상태 정보는 반드시 Lambda 외부의 관리형 데이터베이스나 캐시에 저장해야 한다.

  • Amazon DynamoDB: 서버리스 아키텍처의 사실상 표준 저장소이다. HTTP 기반의 비연결(connectionless) 모델을 사용하여, Lambda가 수천 개로 확장되더라도 기존 RDBMS처럼 커넥션 풀(connection pool) 고갈 문제를 겪지 않는다. 세션 데이터, 애플리케이션 상태, 작업 진행률 등을 저장하는 데 최적화되어 있다.
  • Amazon ElastiCache Serverless: 1밀리초 미만의 초고속 응답이 필요하거나 Redis의 복잡한 자료구조(Sorted Set 등)가 필요한 경우 사용한다. 서버리스 옵션을 사용하면 VPC 연결 설정이 간소화되고 트래픽에 따라 자동으로 확장되므로 운영 부담이 적다.

멱등성(Idempotency) 설계와 구현

SQS나 Kinesis와 같은 이벤트 소스는 최소 1회 전달을 보장하므로, 네트워크 오류나 타임아웃 발생 시 동일한 이벤트가 Lambda 함수로 중복 전달될 수 있다. 결제 처리나 재고 차감과 같은 중요한 로직에서 중복 실행은 데이터 불일치를 초래하므로 멱등성idempotency17 구현은 필수적이다.

  • 멱등성 키(idempotency key): 요청의 고유 식별자(예: order_id)나 페이로드의 해시값을 멱등성 키로 사용한다.
  • AWS Lambda Powertools 활용: 이 라이브러리는 DynamoDB를 상태 저장소로 사용하여 멱등성을 쉽게 구현할 수 있는 유틸리티를 제공한다. 함수가 시작되면 DynamoDB에 IN_PROGRESS 상태를 기록하고, 처리가 완료되면 결과를 저장한다. 동일한 키로 중복 요청이 들어오면 함수를 다시 실행하지 않고 저장된 결과를 반환하거나 진행 중임을 알림으로써 중복 처리를 방지한다.

분산 트랜잭션 관리: 사가 패턴(Saga Pattern)

마이크로서비스 환경에서는 단일 데이터베이스 트랜잭션을 사용할 수 없다. 대신 사가 패턴(saga pattern)18을 통해 최종적 일관성eventual consistency을 확보해야 한다.

대표적인 방식이 오케스트레이션 기반 사가로, Step Functions를 사용하여 트랜잭션의 각 단계를 조율한다. 예를 들어 '주문 생성 → 결제 → 배송 요청'의 흐름을 정의하고, 중간에 '결제'가 실패할 경우 정의된 보상 트랜잭션compensating transaction(예: 주문 취소 및 재고 복구)을 실행하여 시스템을 일관된 상태로 되돌린다. 이는 Lambda 함수 간에 이벤트를 주고받는 코레오그래피(choreography) 방식19보다 가시성이 높고 디버깅이 용이하여, 복잡한 비즈니스 로직에 권장된다.

결론

서버리스 아키텍처가 제공하는 것은 결국 하나의 교환이다. 인프라 운영(프로비저닝, 패치, 스케일링, 다중 AZ 구성)을 AWS에 넘기는 대신, 개발자는 플랫폼이 정한 제약 — 콜드 스타트, 실행 시간 제한, 무상태 — 안에서 설계하는 법을 익혀야 한다. 흥미로운 것은 그 제약들의 해결 전략이 모두 아키텍처 패턴이라는 점이다. 콜드 스타트는 SnapStart와 프로비저닝된 동시성으로, 시간 제한은 비동기 전환과 Step Functions 오케스트레이션으로, 상태 관리는 외부 지속성 계층과 멱등성, 사가 패턴으로 푼다. 즉 서버리스는 운영의 문제를 없애는 것이 아니라 설계의 문제로 바꾸는 것이고, Lambda의 실행 모델과 API Gateway의 트래픽 제어를 이해하는 일이 그 설계의 출발점이 된다.


출처

*[VPC]: Virtual Private Cloud *[RPS]: Requests Per Second

Footnotes

  1. 고가용성(high availability)은 IT 시스템이 다운타임을 제거하거나 최소화하여 언제든지 문제없이 서비스에 접근할 수 있게 하는 능력이다.

  2. KVM(Kernel-based Virtual Machine)은 리눅스 커널을 하이퍼바이저(가상 머신 모니터, VMM)로 만들어 주는 모듈로, CPU의 하드웨어 가상화 확장(Intel VT-x, AMD-V)을 사용해 게스트 코드를 CPU에서 직접 실행한다. Firecracker는 이 KVM API를 사용하여 사용자 공간에서 실행되는 VMM 프로세스로 작동한다.

  3. 테넌트(tenant)는 서비스 제공자의 클라우드 자원을 빌려서 서비스를 이용하는 주체이며, 클라우드의 하나의 자원을 쪼개서 여러 사용자에게 제공하는 것을 멀티 테넌시(multi-tenancy)라고 한다.

  4. 이웃 소음(noisy neighbor)은 멀티 테넌트 환경에서 같은 시스템을 사용하는 사용자들이 완벽히 격리되지 못해 서로의 성능에 영향을 주는 문제를 말한다.

  5. 용량 공급자(capacity provider)는 사용자가 Lambda를 특정 EC2 인스턴스에서 실행할 수 있게 해 주는 인프라 구성요소다.

  6. Amazon Kinesis Data Streams는 모든 규모의 스트리밍 데이터를 실시간으로 수집·저장하는 완전관리형 서버리스 데이터 스트리밍 서비스다.

  7. POP(Point of Presence)는 Edge Location이라고도 부르며, CloudFront가 콘텐츠를 캐싱하고 클라이언트에게 제공하는 지점 혹은 캐시 서버를 의미한다.

  8. 백본 네트워크(backbone network)는 대규모 네트워크 인프라의 중심을 이루는 고속 전송 경로로, 여러 네트워크를 연결하고 대규모 트래픽을 효율적으로 처리한다.

  9. 홉(hop)은 데이터 패킷이 출발지에서 목적지로 이동하는 경로 중 라우터, 브리지, 게이트웨이와 같은 네트워크 장비를 한 번 거쳐 갈 때 발생하는 단계를 의미한다.

  10. 인터페이스 VPC 엔드포인트(interface VPC endpoint)는 소비자의 각 가용 영역 서브넷 안에 ENI(탄력적 네트워크 인터페이스)를 생성해 사설 IP 주소를 할당하고, 소비자의 워크로드가 그 로컬 IP 주소를 통해 원격 서비스에 접근하게 하는 구조다.

  11. SOAP은 HTTP, HTTPS, SMTP 등을 통해 XML 기반의 메시지를 컴퓨터 네트워크 상에서 교환하는 프로토콜이다.

  12. JIT(Just-In-Time) 컴파일러는 실행 시점에 코드를 기계어로 번역하되 그 결과를 캐싱해 두어, 같은 코드를 다시 만나면 번역 없이 캐싱된 값을 사용함으로써 인터프리팅 시간을 단축한다.

  13. AWS Application Auto Scaling은 EC2 Auto Scaling 이외의 개별 AWS 서비스 리소스(Lambda 프로비저닝된 동시성, DynamoDB 처리량 등)를 자동으로 조정해 주는 서비스다.

  14. TCP Keep-Alive는 TCP 연결이 맺어진 이후 작은 패킷을 주기적으로 보내 연결 유지 여부를 확인하는 방법이다. 이를 통해 연결이 살아 있는지 지속적으로 점검하고, 끊어졌다면 즉시 알 수 있다.

  15. SQS(Simple Queue Service)는 분산 메시징 시스템에서 내구성 있고 가용성이 뛰어난 호스팅 대기열을 제공하는 AWS 서비스다.

  16. ETL(Extract, Transform, Load — 추출, 변환, 적재)은 다양한 소스의 데이터를 데이터 웨어하우스라는 대형 중앙 집중식 저장소로 결합하는 과정이다.

  17. 멱등성(idempotency)은 수학과 컴퓨터 과학에서 동일한 연산을 여러 번 적용하더라도 최종 결과가 처음 한 번 적용한 것과 같은 성질을 의미한다.

  18. 사가(saga) 패턴은 하나의 거대한 분산 트랜잭션을 여러 개의 독립적인 로컬 트랜잭션 시퀀스 T1,T2,,TnT_1, T_2, \ldots, T_n으로 분할한다. 각 로컬 트랜잭션 TiT_i는 해당 서비스의 데이터베이스를 업데이트하고 다음 단계를 위한 이벤트를 발행하며, TkT_k 단계에서 실패가 발생하면 이미 성공한 T1,,Tk1T_1, \ldots, T_{k-1} 단계들에 대응하는 보상 트랜잭션 Ck1,,C1C_{k-1}, \ldots, C_1을 역순으로 실행하여 시스템을 논리적으로 이전의 일관된 상태로 되돌린다. 이렇게 최종적 일관성을 달성한다.

  19. 코레오그래피(choreography) 방식은 중앙의 제어 장치 없이 각 서비스가 도메인 이벤트를 발행하고 구독함으로써 협력하는 방식이다. 무용수들이 중앙 지휘자 없이 서로의 동작에 반응하며 춤을 추는 것에 비유된다.