~/infra-dev.md
INFRA_DEV

이벤트는 어디서 사라졌을까: EventBridge 로깅으로 보는 비동기 장애 추적

10분 읽기·2026.06.18·출처 4·00
오늘의 질문

비동기 이벤트가 실패했을 때 우리는 “어디서, 왜, 몇 번 재시도하다가, 어디에 남았는지”를 설명할 수 있는가?

infra-dev.md
SURVIVE.exe

이벤트는 어디서 사라졌을까: EventBridge 로깅으로 보는 비동기 장애 추적

1. 왜 지금 봐야 하나

이벤트 기반 아키텍처는 작은 팀에게도 매력적이다. 주문 생성, 결제 완료, 이메일 발송, 검색 색인 갱신, AI 작업 요청처럼 “지금 당장 응답하지 않아도 되는 일”을 이벤트로 분리하면 HTTP 요청은 빨라지고 서비스 간 결합도는 낮아진다. 문제는 장애가 났을 때다. 동기 호출은 500 응답과 stack trace라도 남지만, 비동기 이벤트는 종종 이렇게 보인다.

“분명 이벤트를 발행했는데, 어디선가 사라진 것 같다.”

AWS는 2025년 7월 Amazon EventBridge의 enhanced logging을 발표했다. 공식 블로그와 문서에 따르면 EventBridge는 이벤트가 event bus에 들어오고, rule과 매칭되고, target 호출을 시작하고, 성공·실패·재시도·DLQ로 이동하는 단계를 로그로 남길 수 있다. 로그 목적지는 CloudWatch Logs, Amazon S3, Data Firehose를 선택할 수 있고, 로그 레벨은 ERROR, INFO, TRACE로 조절한다.

이 기능 자체보다 중요한 학습 포인트는 비동기 시스템에서 관측성은 “로그를 많이 남기기”가 아니라 이벤트의 생애주기를 상태 전이로 추적하는 일이라는 점이다. 이벤트가 실패했을 때 운영자는 다음 네 질문에 답해야 한다.

  • 이벤트가 bus에 실제로 들어왔는가?
  • 어떤 rule과 매칭되었는가, 아니면 매칭되지 않았는가?
  • target 호출은 시작되었고, 어떤 오류로 실패했는가?
  • 재시도 후 버려졌는가, 아니면 DLQ에 남았는가?

오늘 글은 EventBridge 로깅을 계기로 queue/stream/worker 시스템 전반에 적용할 수 있는 비동기 장애 추적 원리를 정리한다.

2. 핵심 개념

비동기 이벤트 처리의 핵심은 “발행 성공”과 “업무 완료”가 다르다는 것이다. PutEvents 호출이 성공했다는 말은 event bus가 이벤트를 받았다는 뜻이지, 최종 target이 업무를 끝냈다는 뜻이 아니다. 그 사이에는 최소 네 단계가 있다.

첫째, ingestion이다. 이벤트가 bus에 들어오는 단계다. 여기서 실패하면 애플리케이션은 이벤트를 보냈다고 생각하지만 플랫폼은 받지 못했을 수 있다.

둘째, rule matching이다. 이벤트의 source, detail-type, detail payload가 rule 조건과 맞는지 판단한다. 조건이 너무 좁거나 schema가 바뀌면 이벤트는 아무 target에도 가지 않는다. EventBridge 문서는 로그가 rule matched, no rules matched 같은 처리 단계를 보여줄 수 있다고 설명한다.

셋째, invocation이다. 매칭된 target을 호출하는 단계다. Lambda, API Destination, SQS, Step Functions 같은 target마다 실패 이유가 다르다. 권한이 없으면 NO_PERMISSIONS, target이 사라졌으면 NO_RESOURCE, HTTP API 인증이 틀리면 client error가 될 수 있다.

넷째, retry와 terminal state다. EventBridge 문서에 따르면 retriable error가 발생하면 기본적으로 최대 24시간, 최대 185회까지 exponential backoff와 jitter로 재시도한다. 하지만 모든 오류가 재시도되는 것은 아니다. 권한 누락이나 존재하지 않는 target처럼 사람이 고쳐야 하는 영구 오류는 재시도 없이 DLQ로 보내지거나 드롭될 수 있다.

여기서 DLQ(dead-letter queue)는 실패를 숨기는 쓰레기통이 아니다. 재처리 가능한 실패 이벤트를 보존하고, 원인과 재시도 횟수를 기록하는 조사 대기열이다. EventBridge DLQ 문서는 failed event에 RULE_ARN, TARGET_ARN, ERROR_CODE, ERROR_MESSAGE, EXHAUSTED_RETRY_CONDITION, RETRY_ATTEMPTS 같은 속성이 포함될 수 있다고 설명한다.

3. 최신 이슈와 연결

EventBridge enhanced logging의 실용적인 변화는 이벤트 처리 과정을 “추측”에서 “조회”로 옮긴다는 것이다. AWS News Blog 예시는 로그가 EVENT_RECEIPT에서 INVOCATION_SUCCESS까지 이어지는 lifecycle을 보여주고, 실패 상황에서는 NO_PERMISSIONS, SDK_CLIENT_ERROR, HTTP status code, error message 같은 정보를 제공한다고 설명한다. 운영자가 CloudWatch Logs에서 이벤트가 어디까지 진행됐는지 확인할 수 있다는 뜻이다.

하지만 문서의 주의사항도 중요하다. EventBridge event bus log delivery는 best effort이며 완전성과 적시성이 보장되지 않는다. 따라서 이 로그를 회계 원장이나 정확한 처리 카운터처럼 사용하면 안 된다. 운영 판단은 EventBridge metrics, target 서비스 로그, DLQ depth, 애플리케이션 idempotency 기록과 함께 봐야 한다.

또 하나의 주의점은 실행 데이터 포함 옵션이다. EventBridge 로깅은 필요하면 event detail, target input, target request 정보를 더 자세히 남길 수 있다. 블로그도 execution data를 켤 때 payload에 민감한 정보가 포함될 수 있으니 조심하라고 안내한다. 결제 정보, 이메일 주소, 인증 토큰, 고객 식별자가 event detail에 들어간 팀이라면 TRACE + execution data는 강력한 디버깅 도구이면서 동시에 데이터 유출면을 넓히는 설정이다.

정리하면 최신 기능의 메시지는 “TRACE를 켜면 다 해결된다”가 아니다. 평소에는 비용과 보안을 고려해 ERROR/INFO 중심으로 운영하고, 장애 조사나 staging 재현에서 TRACE와 execution data를 제한적으로 사용하는 운영 정책이 필요하다는 뜻이다.

4. 개발자 관점 해석

개발자가 여기서 배워야 할 것은 EventBridge 콘솔 사용법보다 세 가지 설계 습관이다.

첫째, 이벤트마다 추적 가능한 식별자를 넣는다. 로그가 lifecycle을 보여줘도 애플리케이션의 주문 ID, 작업 ID, correlation ID가 없으면 여러 시스템의 기록을 연결하기 어렵다. 이벤트 detail에는 최소한 event_id, correlation_id, occurred_at, producer, schema_version 같은 필드를 두는 편이 좋다. 단, 개인정보나 secret을 그대로 넣지 말고 내부 ID나 해시처럼 조사에 필요한 최소 정보만 둔다.

둘째, 재시도는 성공 확률을 높이지만 중복 실행도 만든다. EventBridge가 최대 24시간/185회 재시도할 수 있다는 사실은 신뢰성의 장점이지만, target 함수가 멱등하지 않으면 같은 주문에 이메일이 여러 번 가거나 포인트가 중복 지급될 수 있다. target은 event_id 또는 비즈니스 키로 처리 완료 여부를 기록하고, 이미 처리한 이벤트는 안전하게 skip해야 한다.

셋째, DLQ는 알림과 runbook이 없으면 무덤이 된다. DLQ를 설정했다고 안심하면 실패 이벤트가 조용히 쌓인다. DLQ depth, InvocationsSentToDLQ, InvocationsFailedToBeSentToDLQ 같은 지표에 alert를 걸고, 누가 어떤 순서로 원인을 고치고 재처리할지 문서화해야 한다. 재처리는 “큐 메시지를 다시 넣기”가 아니라, target 권한·schema·idempotency 상태를 확인한 뒤 수행하는 운영 작업이다.

작은 팀에서는 특히 “비동기 = 나중에 알아서 됨”이라는 착각을 경계해야 한다. 비동기는 사용자 요청을 빠르게 끝내지만, 운영 책임을 없애지 않는다. 오히려 실패가 눈에 덜 보이므로 로그, metrics, DLQ, idempotency를 더 의식적으로 설계해야 한다.

5. 내 프로젝트에 적용할 체크포인트

EventBridge를 쓰든, Kafka를 쓰든, Cloud Tasks나 BullMQ를 쓰든 아래 질문은 거의 그대로 적용된다.

  1. 이벤트 발행 성공과 target 처리 성공을 구분해서 기록하는가?
  2. 모든 이벤트에 correlation ID와 schema version이 있는가?
  3. rule/filter 조건 변경 시 “no match”를 감지할 방법이 있는가?
  4. target 호출 실패가 permission, throttling, timeout, client error 중 무엇인지 분류되는가?
  5. 재시도 횟수와 최대 이벤트 나이를 명시적으로 정했는가?
  6. target 작업은 같은 이벤트가 여러 번 와도 안전한가?
  7. DLQ가 설정되어 있고, DLQ depth에 alert가 있는가?
  8. DLQ 메시지의 error code와 retry attempts를 보는 절차가 문서화되어 있는가?
  9. TRACE 수준 로그나 payload 로그를 켤 때 개인정보·secret 노출 위험을 검토하는가?
  10. 로그가 best effort일 수 있다는 전제를 metrics와 애플리케이션 상태 테이블로 보완하는가?

이 중 하나만 고른다면 6번부터 보자. 비동기 시스템에서 멱등성이 없으면 재시도와 장애 복구가 모두 위험해진다.

6. 오늘 10분 액션

오늘 10분 동안 운영 중인 비동기 흐름 하나를 골라 아래 표를 채워보자.

항목현재 값비어 있으면 할 일
이벤트 이름예: order.created가장 중요한 이벤트 1개 선택
producer예: API 서버발행 위치 코드 링크
correlation ID있음/없음없으면 필드명 정하기
schema version있음/없음없으면 schema_version 추가 이슈 생성
matching rule/filter문서 링크조건 변경 테스트 추가
target예: email-worker Lambdatarget 로그 위치 기록
retry policy기본값/커스텀최대 시도·최대 나이 확인
DLQ있음/없음없으면 왜 없는지 기록
alertDLQ depth / failure metric없으면 최소 1개 alert 만들기
idempotency key예: event_id없으면 처리 완료 테이블 설계

시간이 남으면 실패 시나리오 하나를 일부러 만든다. 예를 들어 staging에서 target 권한을 제거하거나 잘못된 API key를 넣고, 로그가 어떤 ERROR_CODE와 lifecycle step을 남기는지 확인한다. 목표는 장애를 내는 것이 아니라 장애가 설명 가능한 형태로 남는지 확인하는 것이다.

7. 더 볼 자료

중복 회피 메모

로컬 content/generated와 Supabase 최근 infra_dev 글을 확인했다. 최근 글은 Cloud SQL 백업/DR, Lambda durable functions와 idempotency, GitHub Actions 장애, Cloudflare Queues backlog/DLQ, PostgreSQL 19 관측 지표, Docker 보안, GKE 스케일링, OpenTelemetry Collector 운영을 다뤘다. 이번 글은 큐나 DLQ를 다시 언급하지만 특정 큐 제품의 백로그 운영이나 장기 워크플로 설계가 아니라, EventBridge event bus logging을 중심으로 비동기 이벤트의 ingestion → rule matching → invocation → retry/DLQ 생애주기를 추적하는 관측성 관점에 집중해 중복을 피했다.

댓글 0

최신순 ▾
한 줄 남기기

혹시 이 글을 읽는 동료 개발자가 있다면, GitHub으로 로그인하고 한 줄 흔적을 남겨줘요. (스팸 방지용 로그인이에요)