주문이 들어오면 재고 차감, 알림 발송, 정산 기록까지 해야 한다. 이걸 API 한 번의 동기 호출 체인으로 다 처리할지, 일부를 큐에 던져 비동기로 뺄지 정해야 한다.
언제: 호출한 쪽이 결과를 바로 봐야 할 때 — 결제 승인, 재고 차감처럼 실패하면 그 자리에서 사용자에게 알려야 하는 작업. 트래픽이 수백 TPS 이하이고 호출 체인이 2~3단계 이내면 이걸로 충분하다.
비용: 상대 서비스가 느려지면 나도 같이 느려지고, 상대가 죽으면 나도 같이 죽는다. 타임아웃과 재시도 설정이 그대로 내 장애 반경이 된다.
언제: 호출자가 결과를 안 봐도 되는 작업(알림, 정산 기록, 로그 적재)일 때. 피크 트래픽이 평시의 몇 배로 튀거나, 받는 쪽 처리 속도가 들쭉날쭉해서 완충이 필요할 때.
비용: 큐 인프라에 더해 DLQ 재처리, 멱등 키로 중복 방어, 순서 보장, 발행 유실 방지(outbox) 같은 새 문제가 따라온다. '이 메시지 지금 어디까지 갔지?'를 추적하는 비용이 코드보다 운영에서 더 크다.
기본은 동기 호출로 시작한다. 호출자가 결과를 안 쓰는 작업인데 그 대기 시간이 p99를 끌어올리거나, 다운스트림 장애가 내 API 에러로 번지기 시작하면 그 작업만 큐로 뺀다. 처음부터 전부 이벤트로 가는 건 풀어야 할 문제보다 운영 부담을 먼저 사는 일이다.
- API p99가 500ms를 넘는데, 그 대부분이 호출자가 결과를 안 쓰는 다운스트림(알림·로그) 대기 시간일 때
- 다운스트림 한 곳 장애로 내 서비스 에러율이 같이 1% 이상 뛸 때 — 장애가 전파되고 있다는 신호
- 타임아웃과 재시도가 겹쳐 다운스트림 트래픽이 원래의 2~3배로 증폭될 때
- 피크가 평시의 5배 이상인데 완충이 없어서 요청을 그냥 버리고 있을 때
🔧REST/HTTP with explicit timeout + retry budgetKafkaAWS SQS + DLQTransactional outboxGrafana p99 / queue-depth dashboards
“I start with a plain sync call, and only reach for a queue when p99 or cascading failures tell me to.”
checking microphone…