학습 목표

느슨한 결합 — 왜 이것이 복원력을 만드는가 SAA 빈출

동기 호출은 장애를 전파합니다

웹 계층이 처리 계층을 직접 호출하는 구조를 생각해 봅니다. 처리 계층이 느려지면 웹 계층의 스레드가 응답을 기다리며 묶이고, 곧 웹 계층까지 응답하지 못합니다. 처리 계층이 아예 죽으면 그동안 들어온 요청은 어디에도 남지 않고 사라집니다. 장애가 한 방향으로만 번지는 것이 아니라, 두 계층의 가용성이 곱해져 전체 가용성이 됩니다.

사이에 큐를 두면 관계가 달라집니다. 웹 계층은 메시지를 넣고 즉시 응답하며, 처리 계층은 자기 속도로 꺼내 갑니다. 처리 계층이 멈춰도 메시지는 큐에 남아 있고, 복구되면 밀린 것부터 처리합니다. 두 계층이 서로의 가용 시간과 처리 속도를 알 필요가 없어지는 것이 느슨한 결합의 정의입니다.

웹 계층이 처리 API를 직접 호출하고 결과를 기다립니다. 처리 계층이 느려지면 웹 계층이 함께 느려지고, 타임아웃된 요청은 유실됩니다. 처리 계층을 배포하는 동안에도 요청을 받을 수 없습니다.

안티패턴 — 동기 호출로 계층을 붙인 구성
# 웹 계층이 처리 계층의 엔드포인트를 직접 호출하고 응답을 기다립니다.
curl -X POST https://internal-worker.example.internal/jobs \
  --data '{"orderId":"1001"}' \
  --max-time 30

웹 계층은 큐에 넣고 즉시 응답합니다. 처리 계층이 멈춰도 메시지는 보존 기간 동안 큐에 남고, 소비자를 늘리면 밀린 양을 따라잡습니다. 배포와 장애가 사용자 응답에 보이지 않습니다.

권장 — Amazon SQS로 접수와 처리를 분리 (큐 URL은 예시입니다)
aws sqs send-message \
  --queue-url https://sqs.ap-northeast-2.amazonaws.com/123456789012/orders \
  --message-body '{"orderId":"1001"}'

결합을 끊는 세 가지 도구

AWS에는 결합을 끊는 서비스가 셋 있고, 전달 모델이 서로 다릅니다. 이 차이를 모르면 문항에서 셋 중 아무거나 고르게 됩니다.

Amazon SQS · Amazon SNS · Amazon EventBridge의 전달 모델
서비스 전달 모델 소비 방식 이것을 고르는 신호
Amazon SQS 한 메시지를 한 소비자가 가져가 처리 소비자가 끌어온다(폴링) "작업을 하나씩 처리한다", "급증분을 완충한다", "실패를 재시도한다"
Amazon SNS 한 메시지를 모든 구독자에게 팬아웃 SNS가 밀어 준다(푸시) "하나의 사건을 여러 시스템이 동시에 받아야 한다"
Amazon EventBridge 이벤트 내용에 따라 규칙으로 라우팅 EventBridge가 대상으로 밀어 준다 "이벤트 종류에 따라 다른 대상으로 보낸다", "AWS 서비스의 이벤트에 반응한다", "일정에 따라 실행한다"

셋을 조합하는 형태가 특히 자주 나옵니다. SNS 주제에 여러 SQS 큐를 구독시키면 팬아웃과 완충을 한 번에 얻습니다. 각 구독자가 자기 큐를 갖게 되므로, 한 구독자가 느려도 다른 구독자의 처리에 영향을 주지 않습니다.

트래픽 급증을 큐로 완충한다

Auto Scaling 그룹은 지표를 보고 인스턴스를 늘리지만, 인스턴스가 준비되어 트래픽을 받기까지는 시간이 걸립니다. 스케일링 정책을 아무리 공격적으로 바꿔도 그 준비 시간은 사라지지 않습니다. 급증이 준비 시간보다 빠르면 요청은 유실됩니다.

큐를 앞에 두면 급증분이 큐에 쌓이고, 소비자는 자기 속도로 처리하면서 뒤따라 확장합니다. 요청은 늦게 처리될 뿐 사라지지 않습니다. 시나리오가 "판매 시작 직후처럼 예측 불가능한 급증에도 주문을 잃으면 안 된다"고 말하면, 정답 방향은 스케일링 정책 조정이 아니라 큐 완충입니다.

시점이 예측 가능한 급증이라면 예약 스케일링으로 미리 늘려 두는 안도 함께 검토합니다. 스케일링 정책 유형과 워밍업은 7장 로드 밸런싱과 오토스케일링에서 다룹니다.

AWS Lambda

실행 모델 — 무엇이 사라지고 무엇이 남는가

Lambda는 코드를 함수 단위로 등록해 두고, 이벤트가 올 때만 실행 환경을 띄워 실행합니다. 인스턴스 · 운영체제 · 용량 계획이 사라지고, 남는 것은 함수 코드 · 실행 역할 · 트리거 설정 셋입니다. 요청이 동시에 여러 개 오면 실행 환경도 여러 개가 만들어지며, 한 실행 환경은 한 번에 하나의 이벤트만 처리합니다.

실행이 끝나도 실행 환경은 잠시 유지되었다가 다음 호출에 재사용됩니다. 그래서 핸들러 바깥에서 초기화한 데이터베이스 연결이나 SDK 클라이언트는 재사용되지만, 어떤 이벤트가 어떤 실행 환경으로 갈지는 보장되지 않습니다. 메모리나 로컬 파일에 상태를 쌓아 두는 설계는 성립하지 않습니다.

확인된 한도 — 여기까지만 외웁니다

AWS Lambda의 기본값과 한도 (AWS API 모델과 공식 할당량 문서에서 확인한 값)
항목 기본값 한도 설계에 주는 함의
함수 실행 시간(Timeout) 3초 최대 900초(15분) 15분을 넘는 작업은 Lambda가 답이 아닙니다. 분할하거나 컨테이너 · 배치로 옮깁니다
메모리(MemorySize) 128 MB 최대 10,240 MB, 1 MB 단위 메모리를 늘리면 CPU 할당도 함께 증가합니다. 느린 함수의 첫 번째 조정 대상입니다
임시 스토리지(/tmp) 512 MB 최대 10,240 MB 큰 파일을 다뤄야 하면 늘립니다. 실행 환경이 사라지면 내용도 사라집니다
동기 호출 페이로드 — 6 MB 큰 데이터는 Amazon S3에 두고 참조만 전달합니다
AWS Lambda의 기본값과 최대값 대비 — 실행 시간은 3초에서 900초까지, 메모리는 128 MB에서 10,240 MB까지, 임시 스토리지는 512 MB에서 10,240 MB까지 조정할 수 있습니다. 기본값이 최대값과 자릿수 단위로 떨어져 있다는 점이 요지이며, 성능 문제의 상당수는 기본값을 그대로 둔 결과입니다

호출 모델 세 가지 — 재시도의 주체가 다릅니다 SAA 빈출

Lambda 문항의 핵심은 기능이 아니라 실패했을 때 메시지가 어디로 가는가입니다. 호출 경로마다 재시도하는 주체와 실패 메시지의 도착지가 다릅니다.

Lambda 호출 모델 세 가지 — 재시도 주체와 실패 도착지 AWS Lambda 함수를 부르는 방법은 세 가지이고 실패했을 때 벌어지는 일이 셋 다 다릅니다. 첫째 동기 호출은 API Gateway 같은 호출자가 응답을 기다리는 방식입니다. 함수가 실패하면 오류가 호출자에게 그대로 돌아가고 Lambda 는 아무것도 다시 시도하지 않으며 메시지를 받아 줄 배달 못 한 편지 큐도 없습니다. 재시도는 호출자의 책임입니다. 둘째 비동기 호출은 EventBridge 나 SNS 가 이벤트를 던지고 즉시 떠나는 방식입니다. Lambda 가 내부 큐에 이벤트를 넣고 스스로 기본 두 번까지 재시도하며, 그래도 실패하면 온-실패 대상이나 배달 못 한 편지 큐로 보냅니다. 이 대상을 설정하지 않으면 이벤트는 그냥 사라집니다. 셋째 이벤트 소스 매핑은 Lambda 가 SQS 큐나 Kinesis 및 DynamoDB 스트림을 직접 폴링하는 방식입니다. 재시도 규칙은 Lambda 가 아니라 소스가 정합니다. SQS 라면 가시성 제한 시간이 지나 메시지가 다시 보이고 수신 횟수가 maxReceiveCount 를 넘으면 그 큐에 붙은 배달 못 한 편지 큐로 옮겨집니다. 메시지가 사라졌다는 신고를 받으면 먼저 어느 호출 모델인지부터 확인해야 하는 이유가 이것입니다. 같은 함수라도 어떻게 불렀느냐에 따라 실패의 결말이 다릅니다 호출 모델 호출 주체 함수 실패했을 때 가는 곳 동기 호출 RequestResponse API Gateway 응답을 기다립니다 Lambda 재시도 없음 DLQ 라는 개념이 없습니다 오류가 호출자에게 그대로 돌아갑니다 재시도 주체 — 호출자 (클라이언트 또는 SDK 가 알아서 다시 부릅니다) 비동기 호출 Event EventBridge · SNS 던지고 떠납니다 Lambda 내부 큐 기본 2회 재시도 온-실패 대상 또는 DLQ SQS · SNS · EventBridge · Lambda 재시도 주체 — Lambda 서비스 · 대상을 설정하지 않으면 이벤트는 그냥 사라집니다 이벤트 소스 매핑 (폴링) Poll-based SQS · Kinesis DynamoDB 스트림 폴링 Lambda 배치로 꺼내 갑니다 소스에 붙은 DLQ SQS 는 maxReceiveCount 초과 시 이동 재시도 주체 — 이벤트 소스 · 가시성 제한 시간이 지나면 메시지가 다시 보입니다 "메시지가 사라졌다"의 진단은 호출 모델을 확인하는 데서 시작합니다 DLQ 를 붙일 자리가 모델마다 다르고, 동기 호출에는 아예 그런 자리가 없습니다
AWS Lambda 호출 모델 세 가지 — 동기 호출은 호출자가 직접 응답을 받고 재시도도 호출자의 몫이며, 비동기 호출은 Lambda가 내부 큐에 넣고 스스로 재시도한 뒤 실패분을 배달 못 한 편지 큐나 대상으로 보내고, 이벤트 소스 매핑은 Lambda가 소스를 폴링하며 소스의 규칙에 따라 재시도합니다
AWS Lambda 호출 모델별 재시도 주체와 실패 처리
호출 모델 대표 트리거 재시도 주체 실패 메시지의 도착지
동기(RequestResponse, 기본값) Amazon API Gateway, Application Load Balancer, 직접 Invoke 호출자 — Lambda는 오류를 그대로 돌려줍니다 없음. 호출자가 처리하지 않으면 그대로 실패입니다
비동기(Event) Amazon S3 이벤트 알림, Amazon SNS, Amazon EventBridge Lambda — 함수가 오류를 반환하면 기본적으로 두 번 더 재시도합니다 배달 못 한 편지 큐(DLQ) 또는 실패 대상(on-failure destination)
이벤트 소스 매핑(폴링) Amazon SQS, Amazon DynamoDB Streams, Amazon Kinesis, Amazon MQ, Amazon MSK Lambda 서비스가 폴링하며 소스의 규칙을 따릅니다 SQS는 큐의 DLQ, 스트림은 OnFailure 대상

비동기 호출은 Lambda가 이벤트를 내부 큐에 넣고 처리합니다. AWS 문서는 이 경우 기본적으로 두 번 재시도하며, 이벤트를 최대 6시간까지 큐에 보관한다고 명시합니다. 모든 시도가 실패하거나 큐에 너무 오래 머문 이벤트는 폐기되므로, 기록을 남기려면 DLQ나 실패 대상을 반드시 설정해야 합니다.

실패 대상으로는 Lambda 함수 · 표준 SQS 큐 · Amazon S3 버킷 · 표준 SNS 주제 · EventBridge 이벤트 버스를 지정할 수 있고, 성공(on-success)과 실패(on-failure)에 각각 다른 대상을 둘 수 있습니다. S3 버킷은 실패 대상으로만 지원됩니다.

동시성 — 예약과 프로비저닝은 다른 문제를 풉니다

동시성은 같은 순간에 실행 중인 함수 인스턴스의 수입니다. 계정과 리전 단위의 동시 실행 한도를 모든 함수가 나눠 쓰기 때문에, 한 함수가 폭주하면 같은 계정의 다른 함수가 스로틀링됩니다.

Lambda 동시성 풀과 예약·프로비저닝된 동시성 맨 위 막대는 리전마다 하나씩 있는 계정 동시성 한도입니다. 예약 동시성을 설정한 함수는 이 풀에서 자기 몫을 미리 떼어 갑니다. 떼어 온 몫은 그 함수만 쓸 수 있는 보장인 동시에 그 함수가 넘을 수 없는 상한이기도 합니다. 나머지 구간은 예약하지 않은 모든 함수가 먼저 쓰는 쪽이 먼저 가져가는 공유 풀입니다. 가운데는 그래서 벌어지는 사고입니다. 공유 풀을 쓰는 함수 하나가 폭주해 풀을 다 써 버리면 같은 공유 풀에 있는 다른 함수는 실행 환경을 얻지 못해 호출이 스로틀됩니다. 예약 동시성은 바로 이 굶주림을 막는 칸막이입니다. 맨 아래는 프로비저닝된 동시성입니다. 온디맨드에서는 요청이 도착한 뒤에 실행 환경을 초기화하느라 콜드 스타트 구간이 생기지만, 프로비저닝된 동시성은 요청이 오기 전에 실행 환경을 미리 만들어 두므로 요청이 도착하는 즉시 핸들러가 실행됩니다. 예약 동시성은 개수를 나누는 장치이고 프로비저닝된 동시성은 지연 시간을 없애는 장치라는 점에서 목적이 다릅니다. 계정 동시성 한도 — 리전마다 하나의 풀입니다 예약 동시성은 이 풀에서 몫을 떼어 옵니다. 보장이면서 동시에 상한입니다 예약 동시성 함수 A 전용 예약 동시성 함수 B 전용 예약하지 않은 모든 함수의 공유 풀 먼저 쓰는 쪽이 먼저 가져갑니다 두 조각의 합이 계정 한도를 넘을 수 없습니다 한 함수의 폭주가 다른 함수를 굶깁니다 예약 동시성은 이 굶주림을 막는 칸막이입니다 함수 C 폭주 공유 풀을 소진 함수 D 호출이 스로틀됩니다 함수 A · B 는 예약 몫이 있으므로 영향을 받지 않습니다 프로비저닝된 동시성 — 콜드 스타트 구간 자체를 없앱니다 요청 도착 온디맨드 초기화부터 시작 콜드 스타트 · 초기화 핸들러 실행 프로비저닝됨 이미 초기화되어 있음 핸들러 실행 초기화 구간이 없습니다 예약 동시성은 개수를 나누는 장치, 프로비저닝된 동시성은 지연 시간을 없애는 장치입니다
계정 동시성 풀과 두 가지 동시성 설정 — 예약된 동시성은 풀의 일부를 한 함수 몫으로 떼어 내 보장이자 상한으로 동시에 작동하고, 프로비저닝된 동시성은 실행 환경을 미리 초기화해 두어 콜드 스타트 구간을 없앱니다
예약된 동시성과 프로비저닝된 동시성의 목적 비교
항목 예약된 동시성(reserved concurrency) 프로비저닝된 동시성(provisioned concurrency)
푸는 문제 다른 함수에 굶기지 않기 + 폭주 함수가 하위 시스템을 무너뜨리지 않기 콜드 스타트로 인한 첫 호출 지연
동작 계정 풀에서 그만큼을 떼어 함수 전용으로 잡아 둡니다. 보장이자 상한입니다 지정한 수만큼 실행 환경을 미리 초기화해 대기시킵니다
적용 대상 함수 전체(모든 버전과 별칭 포함) 함수의 별칭 또는 버전
제약 함수별 한도를 두지 않은 함수들을 위해 최소 100개의 동시 실행은 예약하지 않고 남겨야 합니다 대기시켜 둔 만큼 비용이 발생합니다. 지연이 중요한 함수에만 씁니다
0으로 설정하면 그 함수는 호출되지 않습니다. 사고 시 즉시 차단하는 수단으로 쓰입니다 해당 없음
reserve-concurrency.sh — 함수에 예약된 동시성을 설정하고 확인 (함수 이름과 수치는 예시입니다)
aws lambda put-function-concurrency \
  --function-name order-processor \
  --reserved-concurrent-executions 50

# 계정과 리전의 동시 실행 한도는 계정 설정에서 확인합니다.
aws lambda get-account-settings \
  --query 'AccountLimit.ConcurrentExecutions'

콜드 스타트 — 무엇이 시간을 쓰는가

새 실행 환경을 만들 때는 코드를 내려받아 실행 환경을 준비하고, 런타임을 초기화하고, 핸들러 바깥의 초기화 코드를 한 번 실행합니다. 이 구간이 콜드 스타트이며, 두 번째 호출부터는 재사용되므로 나타나지 않습니다. 따라서 콜드 스타트는 호출이 뜸하거나 갑자기 늘어날 때 문제가 됩니다.

VPC 연결 — 연결하는 순간 경로가 바뀝니다

기본적으로 Lambda 함수는 우리 VPC 밖에서 실행되며 인터넷과 퍼블릭 AWS 엔드포인트에 접근할 수 있습니다. 프라이빗 서브넷의 Amazon RDS 인스턴스처럼 VPC 안의 리소스에 닿아야 하면 함수에 서브넷과 보안 그룹을 지정해 VPC에 연결합니다.

여기서 바뀌는 것이 있습니다. AWS API 문서는 "함수를 VPC에 연결하면 그 VPC를 통해서만 리소스와 인터넷에 접근할 수 있다"고 명시합니다. 즉 연결한 서브넷에 인터넷으로 나가는 경로가 없으면 외부 API 호출이 그 순간부터 실패합니다.

VPC에 연결한 Lambda 함수의 네트워크 경로 Lambda 함수 자체는 AWS 가 관리하는 계정에서 실행되고, VPC 설정을 붙이면 고객 VPC 의 프라이빗 서브넷에 네트워크 인터페이스가 만들어져 그 인터페이스를 통해 트래픽이 나갑니다. 같은 프라이빗 서브넷 안의 RDS 나 ElastiCache 같은 리소스에는 이 인터페이스만으로 바로 닿습니다. 그러나 인터넷의 외부 API 로 나가려면 퍼블릭 서브넷의 NAT 게이트웨이를 거쳐야 하고, S3 나 DynamoDB 같은 퍼블릭 AWS 엔드포인트로 가려면 NAT 게이트웨이나 VPC 엔드포인트가 필요합니다. 함수에 퍼블릭 IP 를 붙이는 선택지는 없기 때문에 이 두 가지 중 하나가 없으면 아웃바운드 호출은 오류가 아니라 타임아웃으로 끝납니다. VPC 에 연결한 순간부터 외부 API 호출이 멈췄다는 사고의 원인이 정확히 이것입니다. VPC 에 붙이면 안쪽은 열리고 바깥쪽은 닫힙니다 Lambda 함수 AWS 관리 계정 퍼블릭 IP 를 붙일 수 없습니다 고객 VPC 프라이빗 서브넷 ENI 네트워크 인터페이스 RDS · ElastiCache 추가 구성 없이 바로 닿습니다 퍼블릭 서브넷 NAT 게이트웨이 따로 만들어야 합니다 VPC 엔드포인트 게이트웨이 또는 인터페이스 인터넷 · 외부 API NAT 를 거쳐야만 닿습니다 S3 · DynamoDB 퍼블릭 AWS 엔드포인트 VPC 바깥 둘 중 하나도 없으면 아웃바운드는 오류가 아니라 타임아웃으로 끝납니다 "VPC 에 연결한 뒤부터 외부 API 호출이 멈췄다"는 사고의 원인이 이것입니다
VPC에 연결한 AWS Lambda의 네트워크 경로 — 함수는 네트워크 인터페이스를 통해 프라이빗 서브넷의 리소스에 닿지만, 인터넷이나 퍼블릭 AWS 엔드포인트로 나가려면 NAT 게이트웨이 또는 VPC 엔드포인트가 따로 필요합니다

실행 역할과 리소스 기반 정책

Lambda의 권한은 두 방향으로 나뉩니다. 시험이 자주 섞어 놓는 지점입니다.

AWS Lambda의 두 가지 권한 방향
구분 무엇을 정하는가 어디에 붙는가
실행 역할(execution role) 함수 코드가 다른 AWS 서비스를 호출할 때의 권한. 로그를 쓰고, S3를 읽고, DynamoDB에 쓰는 권한이 여기 들어갑니다 함수의 Role 속성. IAM 역할이며 신뢰 정책의 주체는 Lambda 서비스입니다
리소스 기반 정책 누가 이 함수를 호출할 수 있는가. API Gateway나 S3가 함수를 부를 수 있게 허용합니다 함수 자체에 붙는 정책. lambda:InvokeFunction을 특정 서비스나 계정에 허용합니다

이벤트 소스 매핑(SQS · DynamoDB Streams · Kinesis)은 방향이 반대입니다. Lambda가 소스를 폴링하므로 실행 역할에 큐를 읽고 삭제할 권한이 필요하고, 큐 쪽에 리소스 기반 정책을 붙일 필요는 없습니다. 역할과 신뢰 정책의 구조는 2장 IAM과 자격 증명에서 다룹니다.

lambda-trust-policy.json — Lambda 실행 역할의 신뢰 정책
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowLambdaToAssumeRole",
      "Effect": "Allow",
      "Principal": { "Service": "lambda.amazonaws.com" },
      "Action": "sts:AssumeRole"
    }
  ]
}

서버리스의 내결함성 정리

Lambda가 "알아서 확장되니까 장애가 없다"고 말할 수는 없습니다. 확장은 자동이지만 실패한 이벤트의 행방은 우리가 설계해야 합니다.

Amazon API Gateway

API Gateway는 HTTP 요청을 받아 Lambda 함수 · HTTP 엔드포인트 · AWS 서비스로 넘기는 관리형 진입점입니다. 인증 · 스로틀링 · 캐싱 · 로깅을 애플리케이션 코드 밖으로 빼내는 것이 존재 이유입니다.

REST API와 HTTP API

같은 서비스 안에 API 유형이 둘 있습니다. HTTP API는 기능을 줄이고 지연과 비용을 낮춘 유형이고, REST API는 기능이 더 많은 유형입니다. 선택 기준은 "REST API에만 있는 기능이 필요한가" 하나입니다.

Amazon API Gateway의 REST API와 HTTP API 비교 (API 모델에서 확인한 기능 범위 기준)
항목 REST API HTTP API
권한 부여자 Lambda 권한 부여자(TOKEN · REQUEST), Amazon Cognito 사용자 풀(COGNITO_USER_POOLS), IAM Lambda 권한 부여자(REQUEST), JWT 권한 부여자, IAM
API 키와 사용량 계획 지원 — 소비자별 쿼터와 스로틀을 붙일 수 있습니다 해당 API 작업이 없음
응답 캐싱 지원 — 스테이지 단위로 캐시를 켜고 크기를 고릅니다 해당 API 작업이 없음
엔드포인트 유형 REGIONAL · EDGE · PRIVATE(VPC 안에서만 접근) 리전 단위. 프라이빗 통합은 VPC 링크로 처리합니다
요청 · 응답 변환 매핑 템플릿과 요청 검증기 지원 파라미터 매핑 중심으로 단순화
WebSocket 별도 유형이 필요 같은 API에서 WEBSOCKET 프로토콜 지원
고르는 신호 "파트너에게 API 키로 쿼터를 준다", "응답을 캐시한다", "VPC 안에서만 호출한다" "단순한 프록시", "JWT로 인증", "지연과 비용을 낮춘다"

권한 부여 방식

스로틀링과 캐싱

API Gateway는 요청 속도를 계정 · 스테이지 · 메서드 · 사용량 계획 수준에서 제한할 수 있습니다. 한도를 넘으면 429 Too Many Requests를 돌려주므로, 백엔드가 무너지기 전에 게이트웨이가 먼저 막아 줍니다. 이것이 API Gateway를 붙이는 복원력 측면의 이유입니다.

REST API는 스테이지 단위로 응답 캐시를 켤 수 있고, 캐시 용량을 골라 지정합니다. 메서드 단위로 캐시를 켜고 끌 수 있으며, 필요하면 스테이지 캐시를 비웁니다. 같은 응답이 반복되는 읽기 위주 API에서 백엔드 호출 수와 지연을 함께 줄입니다.

Amazon SQS — 완전관리형 메시지 큐

SQS는 메시지를 저장했다가 소비자가 가져가게 하는 큐입니다. 생산자와 소비자가 서로를 알 필요가 없고, 소비자가 없어도 메시지는 보존 기간 동안 남습니다. 큐 하나의 메시지는 한 소비자만 가져갑니다.

표준 큐와 FIFO 큐 SAA 빈출

SQS 표준 큐와 FIFO 큐, 그리고 메시지 수명주기 생산자가 1, 2, 3, 4 순서로 보낸 같은 메시지 열을 두 종류의 큐에 각각 넣었을 때 소비자가 무엇을 받게 되는지 비교합니다. 표준 큐에서는 순서가 뒤섞여 도착할 수 있고 같은 메시지가 두 번 올 수 있습니다. 대신 처리량에 사실상 제한이 없습니다. FIFO 큐에서는 메시지 그룹 ID 로 묶인 그룹 안에서만 엄격한 순서가 보장되고 중복 제거가 이루어집니다. 서로 다른 그룹 사이의 순서는 보장되지 않으며 처리량은 배치 없이 초당 300개, 배치를 쓰면 초당 3,000개가 기준입니다. 아래쪽은 두 큐에 공통으로 적용되는 메시지 수명주기입니다. 소비자가 메시지를 수신하면 가시성 제한 시간 동안 다른 소비자에게 보이지 않게 감춰집니다. 기본값은 30초입니다. 그 안에 처리를 마치고 DeleteMessage 를 호출하면 메시지가 사라지지만, 호출하지 못한 채 시간이 지나면 메시지가 큐에 다시 나타나고 수신 횟수가 하나 늘어납니다. 이 수신 횟수가 재드라이브 정책의 maxReceiveCount 를 넘으면 메시지는 배달 못 한 편지 큐로 옮겨집니다. 생산자가 보낸 순서 1 2 3 4 같은 열을 두 큐에 똑같이 넣습니다 표준 큐 소비자가 받는 순서 (예) 2 1 3 3 4 중복 순서 보장 없음 — 최선 노력 중복 전달 가능 — 최소 1회 전달 처리량에 사실상 제한이 없습니다 소비자가 멱등하게 만들어야 합니다 FIFO 큐 메시지 그룹 안에서만 순서 보장 그룹 A 1 3 순서 그대로 그룹 B 2 4 순서 그대로 그룹이 다르면 서로의 순서는 무관합니다 중복 제거 — 정확히 한 번 처리 초당 300개 · 배치 사용 시 초당 3,000개 MessageGroupId 를 빠뜨리면 전송이 실패합니다 메시지 수명주기 — 두 큐에 공통입니다 소비자가 수신 가시성 제한 시간 동안 감춰집니다 (기본 30초) DeleteMessage — 사라짐 시간이 지나면 다시 보임 수신 횟수 +1 배달 못 한 편지 큐 (DLQ) maxReceiveCount 초과 한도 안이면 다시 수신됩니다 순서와 중복 제거가 요구되면 FIFO, 처리량이 요구되면 표준 — 큐 타입은 만든 뒤 바꿀 수 없습니다
같은 메시지 열을 표준 큐와 FIFO 큐에 넣었을 때의 차이 — 표준 큐는 순서를 보장하지 않고 중복 전달이 있을 수 있는 대신 처리량이 사실상 제한되지 않고, FIFO 큐는 메시지 그룹 안에서 순서와 중복 제거를 보장하는 대신 처리량에 정해진 값이 있습니다
Amazon SQS 표준 큐와 FIFO 큐 비교
항목 표준 큐 FIFO 큐
순서 최선 노력 — 보장되지 않음 메시지 그룹 안에서 엄격한 순서
중복 같은 메시지가 두 번 전달될 수 있음 5분 중복 제거 구간 안에서 같은 MessageDeduplicationId는 한 번만 전달
처리량 거의 무제한 배치 없이 초당 300개, 배치 사용 시 초당 3,000개. 고처리량 모드로 더 확장
필수 파라미터 없음 MessageGroupId가 없으면 전송이 실패
병렬 처리 단위 제한 없음 메시지 그룹. 같은 그룹은 한 번에 하나씩, 다른 그룹은 순서가 섞일 수 있음
고르는 신호 처리량이 우선이고 순서가 상관없음 "순서가 보장되어야 한다", "같은 요청을 두 번 처리하면 안 된다"
FIFO 큐의 처리량은 배치 사용 여부로 열 배 차이가 납니다 — 배치 없이 초당 300개, 배치를 쓰면 초당 3,000개입니다. 표준 큐는 공식 문서가 "거의 무제한"이라고만 서술하므로 수치로 비교하지 않습니다. FIFO를 고를지 말지는 이 값과 요구 처리량을 견주어 판단합니다

FIFO 큐의 순서 보장 범위는 큐 전체가 아니라 메시지 그룹입니다. AWS 문서는 같은 그룹의 메시지가 "항상 한 번에 하나씩, 엄격한 순서로 처리"되며 다른 그룹의 메시지는 순서가 어긋날 수 있다고 명시합니다. 따라서 그룹 ID를 잘 나누는 것이 순서와 병렬성을 동시에 얻는 방법입니다 — 예를 들어 주문 ID를 그룹 ID로 쓰면 주문별 순서는 지키면서 서로 다른 주문은 병렬로 처리됩니다.

처리량이 더 필요하면 FIFO 고처리량 모드를 켭니다. DeduplicationScope를 messageGroup으로, FifoThroughputLimit을 perMessageGroupId로 설정하는 두 가지를 모두 바꿔야 하며, 하나만 바꾸면 일반 처리량이 그대로 적용됩니다.

가시성 제한 시간

소비자가 메시지를 받으면 그 메시지는 큐에서 삭제되지 않고 일정 시간 동안 보이지 않게 됩니다. 이 시간이 가시성 제한 시간(visibility timeout)입니다. 소비자가 처리를 끝내고 명시적으로 삭제해야 메시지가 사라지며, 삭제 전에 시간이 지나면 메시지가 다시 보이게 되어 다른 소비자가 가져갑니다.

이 구조가 소비자 장애에 대한 방어입니다. 처리 중 소비자가 죽어도 메시지는 되살아납니다. 반대로 처리 시간보다 짧게 잡으면 같은 메시지를 두 소비자가 처리하게 됩니다. "메시지가 중복 처리된다"는 시나리오에서 가장 먼저 확인할 값입니다.

보존 기간과 배달 못 한 편지 큐

Amazon SQS 큐 속성의 유효 범위와 기본값 (CreateQueue API 문서 기준)
속성 범위 기본값 무엇에 쓰는가
VisibilityTimeout 0초 – 12시간 30초 처리 중인 메시지를 감춰 두는 시간. 처리 시간보다 길게 잡습니다
MessageRetentionPeriod 60초 – 14일 4일 소비자가 없어도 메시지를 보관하는 기간. 장애 복구 시간을 이 안에 넣습니다
DelaySeconds 0초 – 900초 0초 큐에 넣은 메시지를 일정 시간 뒤에 보이게 합니다(지연 큐)
MaximumMessageSize 1 KiB – 1 MiB 1 MiB 메시지 본문 크기 상한. 큰 데이터는 S3에 두고 참조만 보냅니다
ReceiveMessageWaitTimeSeconds 0초 – 20초 0초(짧은 폴링) 롱 폴링 대기 시간. 빈 응답과 요청 수를 줄입니다
RedrivePolicy의 maxReceiveCount — 10 이 횟수를 넘겨 수신된 메시지를 배달 못 한 편지 큐로 옮깁니다
Amazon SQS 큐 속성의 유효 범위와 기본값 — 기본값이 범위 안에서 어디에 있는지가 요지입니다. 가시성 제한 시간(기본 30초)과 롱 폴링(기본 0초, 즉 꺼짐)은 기본값 그대로 두면 중복 처리와 불필요한 빈 응답을 그대로 떠안게 되는 대표적인 항목입니다

반복해서 실패하는 메시지는 큐 앞을 계속 막습니다. 배달 못 한 편지 큐(dead-letter queue, DLQ)를 지정하고 최대 수신 횟수를 설정하면, 그 횟수를 넘긴 메시지가 DLQ로 옮겨져 정상 메시지의 처리를 방해하지 않습니다. 옮겨진 메시지는 원인을 조사한 뒤 원래 큐로 되돌릴 수 있습니다.

DLQ에는 짝 규칙이 있습니다. AWS 문서는 FIFO 큐의 DLQ는 FIFO 큐여야 하고, 표준 큐의 DLQ는 표준 큐여야 한다고 명시합니다.

redrive-policy.json — 최대 수신 횟수를 넘긴 메시지를 DLQ로 보내는 설정 (ARN은 예시입니다)
{
  "deadLetterTargetArn": "arn:aws:sqs:ap-northeast-2:123456789012:orders-dlq",
  "maxReceiveCount": "5"
}

짧은 폴링과 롱 폴링

ReceiveMessage는 기본적으로 짧은 폴링(short polling)으로 동작합니다. AWS 문서는 이때 서버의 일부만 표본으로 조회하므로, 큐에 메시지가 적으면 요청한 것보다 적게 받거나 아예 못 받을 수 있다고 설명합니다.

ReceiveMessageWaitTimeSeconds를 0보다 크게(최대 20초) 설정하면 롱 폴링이 켜집니다. 메시지가 도착할 때까지 기다렸다가 응답하므로 빈 응답이 줄고 요청 수가 줄며 지연도 낮아집니다. 큐를 만들 때 이 값을 20으로 두는 것이 대체로 합리적인 기본 선택입니다.

create-queue.sh — 롱 폴링과 가시성 제한 시간을 지정해 큐를 만듭니다 (값은 예시입니다)
aws sqs create-queue \
  --queue-name orders \
  --attributes '{
    "VisibilityTimeout": "180",
    "MessageRetentionPeriod": "1209600",
    "ReceiveMessageWaitTimeSeconds": "20"
  }'

Amazon SNS — 주제 기반 팬아웃

팬아웃 구조

SNS는 주제(topic)에 발행된 메시지를 모든 구독자에게 복사해 밀어 줍니다. 생산자는 구독자가 몇 개인지 알 필요가 없고, 구독자를 추가해도 생산자 코드는 그대로입니다. 하나의 사건을 여러 시스템이 각자의 목적으로 처리해야 할 때 쓰는 구조입니다.

실무와 시험 양쪽에서 가장 자주 나오는 형태는 SNS 주제 + 여러 SQS 큐입니다. 구독자마다 자기 큐를 가지므로 팬아웃과 완충을 동시에 얻고, 한 구독자가 느려도 다른 구독자에게 영향을 주지 않습니다.

구독 유형

구독의 Protocol 값이 곧 구독 유형입니다. AWS API 문서가 나열하는 값은 다음과 같습니다.

Amazon SNS 구독 프로토콜 (Subscribe API 문서 기준)
프로토콜 전달 대상 비고
sqs Amazon SQS 큐 팬아웃과 완충을 함께 얻는 대표 조합
lambda AWS Lambda 함수 비동기 호출이므로 실패 처리는 함수 쪽 설정이 담당합니다
http · https HTTP(S) 엔드포인트 구독 확인이 필요하고, 재시도 정책을 설정할 수 있습니다
email · email-json 이메일 주소 운영자 알림용. 구독 확인이 필요합니다
sms 문자 메시지 운영자 알림용
application 모바일 앱 엔드포인트 모바일 푸시 알림
firehose Amazon Data Firehose 전송 스트림 구독에 IAM 역할(SubscriptionRoleArn)이 필수입니다

HTTP(S)와 이메일 구독, 그리고 주제와 엔드포인트가 다른 계정에 있는 경우에는 엔드포인트 소유자가 구독 확인을 해야 전달이 시작됩니다. 확인 토큰은 이틀 동안 유효합니다.

필터 정책 — 구독자마다 받을 것을 고릅니다

구독에 FilterPolicy를 붙이면 그 구독자는 주제의 모든 메시지가 아니라 일부만 받습니다. 구독자 쪽에서 걸러 내면 필요 없는 메시지까지 전달되고 처리되지만, 필터 정책을 쓰면 SNS가 전달 단계에서 걸러 냅니다.

FilterPolicyScope로 무엇을 검사할지 고릅니다. 기본값 MessageAttributes는 메시지 속성을, MessageBody는 메시지 본문을 대상으로 삼습니다.

sns-filter-policy.json — 특정 이벤트 유형만 받는 구독 필터 정책
{
  "eventType": ["order_created", "order_cancelled"],
  "region": [{ "prefix": "ap-" }]
}

구독에는 RedrivePolicy로 배달 못 한 편지 큐를 붙일 수 있습니다. 엔드포인트에 닿지 못하거나 서버 오류로 전달하지 못한 메시지가 그 큐에 남아, SNS 쪽에서도 유실을 조사할 수 있게 됩니다. RawMessageDelivery를 켜면 SQS나 HTTP(S) 엔드포인트가 SNS 메타데이터 없이 원문만 받습니다.

Amazon EventBridge — 이벤트 버스와 규칙

이벤트 버스

EventBridge는 이벤트를 받아 규칙에 맞는 대상으로 보내는 서버리스 이벤트 버스입니다. 버스는 세 종류입니다.

AWS 서비스가 만드는 이벤트에 반응해야 한다는 요구가 나오면 EventBridge가 유력합니다. SNS나 SQS는 우리가 직접 발행해야 하지만, EventBridge는 AWS 서비스의 이벤트를 이미 받고 있습니다.

규칙과 패턴 매칭

규칙은 이벤트 패턴 또는 스케줄 표현식 중 최소 하나를 가져야 합니다. 이벤트 패턴이 있는 규칙은 일치하는 이벤트가 관측될 때 실행되고, 스케줄 표현식이 있는 규칙은 정해진 시각에 스스로 실행됩니다. 둘 다 지정할 수도 있습니다.

한 규칙은 하나의 이벤트 버스만 감시하며, AWS 문서는 한 규칙에 동시에 붙일 수 있는 대상이 최대 5개라고 명시합니다. 더 많은 대상이 필요하면 규칙을 나눕니다.

event-pattern.json — EC2 인스턴스가 종료 상태로 바뀐 이벤트만 고르는 패턴
{
  "source": ["aws.ec2"],
  "detail-type": ["EC2 Instance State-change Notification"],
  "detail": {
    "state": ["terminated", "stopping"]
  }
}

사용자 지정 이벤트는 PutEvents로 보냅니다. AWS 문서는 항목 하나의 크기가 256 KB 미만이어야 하고, 중첩 JSON은 1,000단계까지 처리한다고 명시합니다. 큰 데이터는 이벤트에 담지 말고 S3에 두고 참조만 보냅니다.

put-events.sh — 사용자 지정 이벤트를 사용자 지정 버스로 보냅니다 (버스 이름은 예시입니다)
aws events put-events --entries '[
  {
    "EventBusName": "orders-bus",
    "Source": "com.example.orders",
    "DetailType": "OrderCreated",
    "Detail": "{\"orderId\":\"1001\",\"amountTier\":\"large\"}"
  }
]'

스케줄

스케줄 표현식은 cron(0 20 * * ? *) 같은 cron 식과 rate(5 minutes) 같은 rate 식 두 가지입니다. "매일 새벽에 정리 작업을 돌린다"처럼 시각이 정해진 작업을 서버 없이 실행하는 표준 수단입니다.

정해진 시각에 Lambda를 실행하려고 EC2 인스턴스에 cron을 걸어 두는 선택지가 보이면 운영 부담이 큰 쪽이므로 소거합니다.

SNS · SQS · EventBridge — 무엇을 고를 것인가 SAA 빈출

SNS 팬아웃과 EventBridge 라우팅의 구조 차이 왼쪽은 Amazon SNS 의 팬아웃입니다. 게시자가 주제에 메시지를 한 번 올리면 그 주제를 구독한 모든 대상에게 같은 메시지가 그대로 밀려 갑니다. 구독마다 붙일 수 있는 필터 정책은 받을지 말지를 정할 뿐이고 대상마다 다른 내용을 보내지는 못합니다. 구독자 중 하나를 SQS 큐로 두면 팬아웃과 동시에 완충 효과를 얻습니다. 소비자가 잠시 느려지거나 죽어도 메시지가 큐에 남아 있기 때문입니다. 오른쪽은 Amazon EventBridge 의 라우팅입니다. 이벤트가 버스에 들어오면 규칙들이 이벤트 본문을 이벤트 패턴으로 검사하고, 패턴이 맞는 규칙에 연결된 대상에게만 이벤트가 전달됩니다. 즉 SNS 는 같은 것을 여러 곳에 뿌리는 구조이고 EventBridge 는 내용을 보고 갈 곳을 고르는 구조입니다. 같은 것을 여럿에게 vs 내용을 보고 갈 곳을 고르기 게시자 SNS 주제 구독자 전원에게 같은 메시지 SQS 큐 구독 Lambda 구독 HTTPS 구독 큐를 끼우면 완충까지 얻습니다 소비자가 느려져도 메시지가 큐에 남습니다 구독한 대상 전원에게 밀어 줍니다 필터 정책은 받을지 말지만 정합니다 대상마다 다른 내용을 보낼 수는 없습니다 이벤트 소스 이벤트 버스 규칙이 본문을 검사합니다 규칙 A detail-type = 주문 규칙 B detail-type = 환불 대상 — 주문 처리 대상 — 환불 처리 패턴이 맞는 규칙의 대상에만 갑니다 주문 이벤트 하나는 규칙 A 쪽으로만 흘러갑니다 브로드캐스트가 필요하면 SNS, 내용으로 갈 곳을 갈라야 하면 EventBridge SNS 주제에 SQS 큐를 구독시키면 팬아웃과 완충을 한 번에 얻습니다
Amazon SNS의 주제 구독 기반 팬아웃과 Amazon EventBridge의 규칙 기반 라우팅의 구조 차이 — SNS는 구독한 모든 대상에게 같은 메시지를 밀어 주고 필터 정책으로 받을지 말지만 정하는 반면, EventBridge는 이벤트 내용을 패턴으로 검사해 서로 다른 대상을 고릅니다. SNS 주제에 SQS 큐를 구독시키면 팬아웃과 완충을 함께 얻습니다
요구사항에서 서비스로 가는 판단 경로
문제문의 요구 선택 왜
작업을 접수해 두고 하나씩 처리한다 Amazon SQS 한 메시지를 한 소비자가 가져갑니다. 소비자를 늘리면 처리량이 늘어납니다
급증분을 잃지 않고 완충한다 Amazon SQS 메시지가 보존 기간 동안 남습니다. 소비자 속도와 생산 속도를 분리합니다
한 사건을 여러 시스템이 동시에 받는다 Amazon SNS 주제에 구독한 모두에게 복사해 전달합니다
여러 시스템이 받되 각자 완충도 필요하다 SNS + SQS 팬아웃 구독자마다 자기 큐를 갖습니다. 한 구독자의 지연이 다른 구독자에게 번지지 않습니다
이벤트 내용에 따라 다른 대상으로 보낸다 Amazon EventBridge 이벤트 패턴으로 규칙을 만들고 대상을 고릅니다
AWS 서비스의 상태 변화에 반응한다 Amazon EventBridge 기본 이벤트 버스가 AWS 서비스 이벤트를 이미 받고 있습니다
정해진 시각에 작업을 실행한다 Amazon EventBridge 스케줄 규칙(cron · rate)이 서버 없이 실행합니다
순서와 중복 제거가 필요하다 SQS FIFO 큐 메시지 그룹 안의 순서 보장과 중복 제거는 FIFO 큐만 제공합니다
여러 단계를 순서대로 실행하고 상태를 추적한다 AWS Step Functions 메시지 전달이 아니라 워크플로 오케스트레이션의 문제입니다
기존 애플리케이션이 JMS · AMQP · MQTT를 쓴다 Amazon MQ 메시징 코드를 다시 쓰지 않고 그대로 옮기기 위한 선택입니다

오케스트레이션과 그 밖의 통합 서비스

AWS Step Functions

Step Functions는 여러 단계를 상태 기계(state machine)로 정의해 실행합니다. 정의는 Amazon States Language라는 JSON 기반 언어로 쓰며, 작업을 수행하는 Task, 분기하는 Choice, 병렬 실행하는 Parallel, 배열을 반복하는 Map, 기다리는 Wait 같은 상태를 조합합니다.

Lambda 함수 안에 순서와 재시도와 오류 처리를 직접 짜 넣으면 어디까지 진행했는지 알 수 없고, 실패 지점부터 다시 시작하기도 어렵습니다. Step Functions는 진행 상태와 재시도 규칙을 서비스가 관리하므로, "여러 단계를 순서대로 실행하고 각 단계의 성공 여부를 추적해야 한다"는 요구의 표준 답입니다.

AWS Step Functions 상태 기계 유형
유형 성격 고르는 신호
표준(STANDARD, 기본값) 실행 이력을 남기며 오래 걸리는 워크플로에 적합합니다 "각 단계의 결과를 감사해야 한다", "실행이 길다"
Express(EXPRESS) 짧고 빈도가 높은 워크플로에 적합합니다 "초당 매우 많은 이벤트를 처리한다", "실행이 짧다"

유형은 생성 후 변경할 수 없습니다. 큐 타입과 같은 성격의 제약입니다.

AWS AppSync

AppSync는 관리형 GraphQL API를 제공합니다. 클라이언트가 필요한 필드만 한 번의 요청으로 가져가야 하거나, 여러 데이터 소스를 한 스키마로 묶어야 할 때 씁니다. 인증 방식으로 API 키 · IAM · Amazon Cognito 사용자 풀 · OpenID Connect · Lambda 권한 부여를 지원합니다.

REST 방식의 단순한 엔드포인트라면 API Gateway가 더 단순한 선택입니다. 모바일 앱의 오프라인 동기화나 실시간 구독처럼 GraphQL의 성질이 필요한 경우에 AppSync를 고릅니다.

Amazon MQ

Amazon MQ는 Apache ActiveMQ와 RabbitMQ를 관리형으로 제공하는 메시지 브로커입니다. SQS나 SNS와 달리 표준 메시징 프로토콜을 그대로 지원하므로, 기존 온프레미스 애플리케이션이 JMS · AMQP · MQTT · STOMP로 통신하고 있을 때 애플리케이션 코드를 다시 쓰지 않고 옮길 수 있습니다.

배포 모드는 단일 인스턴스(SINGLE_INSTANCE), 가용 영역을 나눈 액티브·스탠바이(ACTIVE_STANDBY_MULTI_AZ), 다중 가용 영역 클러스터(CLUSTER_MULTI_AZ)가 있습니다. 운영 환경이라면 단일 인스턴스는 단일 장애점이므로 다중 가용 영역 모드를 고릅니다.

서버리스가 비용을 줄이는 경우와 늘리는 경우

서버리스가 언제나 싸다는 것은 사실이 아닙니다. 과금 모델이 사용량 기반이라는 것은, 사용량이 낮으면 매우 유리하고 사용량이 일정하게 높으면 불리해질 수 있다는 뜻입니다.

서버리스 선택의 비용 관점 (구체적 금액은 리전과 시점에 따라 달라지므로 방향만 다룹니다)
상황 서버리스가 유리한가 근거
호출이 뜸하거나 변동이 크다 유리 대기 상태에 요금이 붙지 않습니다. 유휴 인스턴스를 띄워 둘 필요가 없습니다
개발과 스테이징 환경 유리 쓰지 않는 시간이 대부분입니다
일정하게 높은 부하가 24시간 지속된다 불리할 수 있음 약정 할인을 받을 수 있는 컴퓨팅이 대체로 유리합니다. Savings Plans와 예약 인스턴스는 5장에서 다룹니다
지연에 민감해 프로비저닝된 동시성이 필요하다 이점이 줄어듦 미리 띄워 둔 만큼 요금이 발생해 "쓴 만큼만" 모델에서 벗어납니다
운영 인력이 적다 유리 패치 · 용량 계획 · 스케일링 설정이 사라집니다. 총소유비용에서 운영 비용은 큰 항목입니다

흔한 오해

시험 포인트 정리

확인 문제

이 장의 핵심을 확인하는 10문항입니다. 키보드만으로 완주할 수 있습니다 — 숫자키 1~5로 선택지를 고르고 Enter로 제출합니다.

학습용 유형 연결형과 순서 배열 문항이 섞여 있을 수 있습니다. 이 두 유형은 암기 확인에 효율이 좋아 기본개념 확인 문제에만 넣었으며, 실제 SAA-C03 시험에는 출제되지 않습니다.

이어서 볼 곳

공식 문서 출처

이 장의 수치와 동작 서술은 모두 아래 문서에서 확인했습니다.