Sync call vs async (queue/event)

judgment
상황

주문이 들어오면 재고 차감, 알림 발송, 정산 기록까지 해야 한다. 이걸 API 한 번의 동기 호출 체인으로 다 처리할지, 일부를 큐에 던져 비동기로 뺄지 정해야 한다.

😴 Boring — Synchronous HTTP call (동기 직접 호출)

언제: 호출한 쪽이 결과를 바로 봐야 할 때 — 결제 승인, 재고 차감처럼 실패하면 그 자리에서 사용자에게 알려야 하는 작업. 트래픽이 수백 TPS 이하이고 호출 체인이 2~3단계 이내면 이걸로 충분하다.

비용: 상대 서비스가 느려지면 나도 같이 느려지고, 상대가 죽으면 나도 같이 죽는다. 타임아웃과 재시도 설정이 그대로 내 장애 반경이 된다.

✨ Novel — Async via queue/event (Kafka, SQS)

언제: 호출자가 결과를 안 봐도 되는 작업(알림, 정산 기록, 로그 적재)일 때. 피크 트래픽이 평시의 몇 배로 튀거나, 받는 쪽 처리 속도가 들쭉날쭉해서 완충이 필요할 때.

비용: 큐 인프라에 더해 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…

⚔️ Defend it — AI 시니어가 반박한다. 영어로 버텨라.

첫 메시지로 네 입장을 영어로 선언해라 — 예: “I'd start with Synchronous HTTP call here, and only switch when …” 그러면 상대가 반박을 시작한다. 근거·숫자·트립와이어로 받아쳐라.

두어 번은 치고받은 뒤에 채점