Blog

외부 API 연동 유실과 재시도 폭주를 막는 메시지 큐 도입 전 필수 점검 체크리스트

2026-10-28

A congested highway with cars and trucks in a traffic jam during daylight.

Photo by Pixabay on Pexels

외부 연동 장애가 내 시스템으로 전파되는 원인과 메시지 큐 전환의 필요성

외부 PG사 결제 승인 알림, 카카오 알림톡 발송, 물류 ERP 시스템과의 재고 연동 등 현대의 백엔드 시스템은 수많은 제3자(Third-party) API 및 웹훅(Webhook)에 의존하고 있습니다. 하지만 외부 시스템은 우리의 통제 권한 밖에 있습니다. 상대방 서버의 점검, 정전, 순간적인 트래픽 폭주, 네트워크 지연이 발생할 때 동기식(Synchronous) HTTP 요청 구조를 그대로 사용하고 있다면 외부의 장애가 곧바로 내 서비스 전체의 마비로 이어집니다.

실제 가상 사례로 쇼핑몰을 운영하는 A사의 정기 프로모션 상황을 들 수 있습니다. A사는 주문 완료 시 외부 알림톡 발송 API를 동기 방식으로 호출하도록 구현되어 있었습니다. 이벤트 당일 알림톡 서비스 제공사의 서버 응답 시간이 평소 200ms에서 8,000ms(8초)로 대폭 증가했습니다. 그 결과 A사 API 게이트웨이와 웹 서버의 작업 쓰레드(Worker Thread)가 외부 응답을 기다리며 대기 상태에 빠졌고, 불과 3분 만에 모든 커넥션 풀이 고갈되어 주문은 물론 상품 조회조차 불가능한 전체 장애로 확산되었습니다.

이러한 결합도를 낮추고 데이터 유실을 방지하기 위해 도입하는 솔루션이 메시지 큐(Message Queue) 기반의 비동기 메시징 아키텍처입니다. 요청을 받는 즉시 메시지 브로커에 저장하고 빠르게 응답을 반환한 뒤, 백그라운드 컨슈머(Consumer)가 차근차근 이벤트를 처리하는 방식입니다. 동기 방식과 비동기 메시지 큐 방식의 주요 차이점은 아래 표와 같습니다.

구분 동기식 Direct API 호출 비동기 메시지 큐(MQ) 방식
연결 결합도 강한 결합 (상대방 응답 대기 필요) 느슨한 결합 (메시지 브로커가 중간 매개)
장애 전파 외부 지연 시 내 시스템 쓰레드 고갈 외부 장애 시 큐에 메시지 대기, 내 시스템 보호
트래픽 제어 최고 피크 트래픽이 그대로 타겟에 전달 컨슈머 처리 속도에 맞게 유연한 버퍼링(Buffering)
데이터 보장 타임아웃 발생 시 데이터 유실 위험 높음 디스크 지속성(Persistence) 및 재시도로 유실 예방

하지만 단순히 메시지 큐(예: AWS SQS, RabbitMQ, Apache Kafka 등)를 설치하고 연결하는 것만으로는 문제를 해결할 수 없습니다. 도입 전후로 재시도 정책, 중복 처리 방지, DLQ 처리 절차를 명확히 정의하지 않으면 오히려 메시지 폭주나 데이터 불일치라는 더 큰 장애를 초래합니다. 현업 담당자가 실무 배포 전 운영 환경에서 반드시 점검해야 할 4가지 영역의 체크리스트를 상세히 살펴보겠습니다.

[체크리스트 1] 메시지 유실 없는 재시도(Retry) 및 지연 백오프 설정

외부 API 호출이 일시적 네트워크 흔들림이나 상대 서버의 503 Service Unavailable 오류로 실패했을 때, 가장 단순한 접근은 즉시 재시도(Immediate Retry)를 수행하는 것입니다. 그러나 100개의 요청이 동시에 실패하여 즉시 재시도를 3회 연속 수행하면, 상대 서버에는 순간적으로 300개의 폭발적인 요청이 몰리는 '재시도 폭주(Thundering Herd Problem)'가 발생합니다. 이는 상대 서버의 복구를 방해하고 장애를 장기화시킵니다.

따라서 메시지 큐 도입 시 지수 백오프(Exponential Backoff)와 무작위 분산(Jitter) 알고리즘이 적용되었는지 반드시 점검해야 합니다.

1. 지수 백오프 및 Jitter 적용 확인

재시도 간격을 단순 고정 값(예: 3초 간격)으로 두지 않고, 실패 횟수가 증가함에 따라 재시도 대기 시간을 2의 거듭제곱으로 늘려야 합니다. 여기에 동일한 시점에 재시도가 몰리지 않도록 무작위 오차 범위(Jitter)를 추가합니다.

  • 1차 재시도 대기 시간: 1초 + Jitter(±0.2초)
  • 2차 재시도 대기 시간: 2초 + Jitter(±0.4초)
  • 3차 재시도 대기 시간: 4초 + Jitter(±0.8초)
  • 4차 재시도 대기 시간: 8초 + Jitter(±1.6초)
  • 5차(최종) 재시도 대기 시간: 16초 + Jitter(±3.2초)

2. Maximum Redelivery Count 및 Visibility Timeout 산정

메시지 브로커에서 메시지를 컨슈머가 가져간 뒤, 제한 시간 내에 성공 응답(Ack)을 보내지 못하면 해당 메시지는 다시 큐로 돌아가 다른 컨슈머에게 할당됩니다. 이 시간을 '가시성 타임아웃(Visibility Timeout)'이라고 합니다.

만약 외부 API 타임아웃을 10초로 설정했다면, 메시지 큐의 Visibility Timeout은 최소 외부 API 타임아웃의 2~3배 이상(예: 30초 이상)으로 설정해야 합니다. 컨슈머가 외부 API 호출을 마치기도 전에 Visibility Timeout이 만료되어 다른 컨슈머가 동일한 메시지를 이중으로 처리하는 사고를 방지하기 위함입니다.

[체크리스트 2] 처리 실패 메시지 격리: DLQ(Dead Letter Queue) 운영 프로세스

최대 재시도 횟수(예: 5회)를 초과했음에도 불구하고 처리에 실패한 메시지는 일반 큐에 계속 남겨두어서는 안 됩니다. 실패한 메시지가 큐의 맨 앞에 머물며 컨슈머의 자원을 지속적으로 소모하는 '독약 메시지(Poison Pill)' 현상이 발생하기 때문입니다.

이러한 실패 메시지를 별도로 격리하여 보관하는 전용 큐가 바로 데드 레터 큐(Dead Letter Queue, DLQ)입니다. DLQ 구축 시에는 단순 생성에 그치지 않고 구체적인 수집, 감지, 재처리 프로세스가 수립되어 있어야 합니다.

1. DLQ 감지 및 알림(Alerting) 임계치 설정

DLQ에 메시지가 1건이라도 인입되는 순간은 시스템에 비정상적인 유실 위험이 발생했다는 신호입니다. 실무에서는 모니터링 도구(DataDog, AWS CloudWatch, Prometheus 등)를 통해 다음과 같은 알림 조건을 설정해야 합니다.

  • 즉시 알림 조건: DLQ 메시지 수지(ApproximateNumberOfMessagesVisible) > 0 건 발생 시 즉시 운영팀 Slack 채널 및 PagerDuty 알림 발송
  • 로그 기록 의무화: DLQ로 이동된 메시지의 헤더에 '최초 발생 시각', '최종 에러 코드(HTTP Status)', '재시도 횟수', '원본 메시지 Body'를 반드시 남기도록 개발 코드 구현

2. DLQ 메시지 수동/자동 재처리(Replay) 시스템 구축

외부 시스템의 장애가 복구된 후, DLQ에 쌓인 수천 건의 실패 메시지를 다시 원본 큐로 돌려놓거나 직접 소비해주는 재처리 도구(Replay Tool)가 구비되어 있는지 점검하세요. 운영 담당자가 CLI 명령어나 내부 관리자 Web UI의 кнопка 클릭 한 번으로 안전하게 재발행할 수 있어야 합니다.

재처리 시 주의할 점은 DLQ 메시지의 보관 기간(Retention Period)입니다. 일반 큐의 보관 기간이 1~2일이라도, DLQ의 보관 기간은 원인 분석 및 원복 작업을 감안하여 최소 7일에서 최대 14일까지 길게 설정해야 합니다.

[체크리스트 3] 메시지 순서 보장과 멱등성(Idempotency) 구현 수칙

메시지 큐 아키텍처에서 가장 자주 발생하는 착각은 "메시지는 발행된 순서대로 정확히 한 번만 처리될 것이다"라는 생각입니다. 대부분의 고성능 분산 메시지 큐(AWS SQS Standard, RabbitMQ 등)는 기본적으로 '최소 한 번 전달(At-Least-Once Delivery)'을 보장합니다. 즉, 네트워크 재시도나 컨슈머의 장애 복구 과정에서 동일한 메시지가 2회 이상 전달될 수 있습니다.

1. 멱등성(Idempotent) Consumer 구현

동일한 메시지가 두 번 수신되어도 시스템의 최종 상태가 동일하고 중복 결제나 중복 데이터 생성이 일어나지 않도록 컨슈머 로직을 작성해야 합니다. 이를 위해 메시지마다 고유한 비즈니스 키(Unique Transaction Key 또는 Event ID)를 포함시켜야 합니다.

컨슈머가 메시지를 처리할 때의 표준 SQL 구현 예시는 다음과 같습니다. 단순히 `INSERT`를 수행하는 것이 아니라, 이미 처리된 이력이 있는지 데이터베이스 제약 조건이나 조건부 업데이트를 활용합니다.

UPDATE orders SET order_status = 'PAID', updated_at = NOW() WHERE order_id = :orderId AND order_status = 'PENDING';

위 SQL과 같이 상태 변경 조건(`WHERE order_status = 'PENDING'`)을 명시하면, 동일한 결제 완료 메시지가 중복으로 도착하더라도 영향받은 행(Affected Rows)의 수가 0이 되어 중복 처리를 안전하게 건너뛸 수 있습니다.

2. 분산 락(Distributed Lock) 및 유니크 제약 조건 활용

트랜잭션 처리가 복잡하여 SQL 조건문만으로 처리하기 어렵다면, Redis 기반의 Redlock을 사용하여 메시지 ID를 Key로 하는 분산 락을 걸거나, DB의 Processed_Events 테이블에 `event_id`를 Primary Key로 저장하여 이미 처리된 이벤트인지를 먼저 조회하는 'Idempotent Consumer Pattern'을 적용해야 합니다.

[체크리스트 4] 컨슈머(Consumer) 스케일링과 장애 대응 임계치 산정

메시지가 큐에 쌓이는 속도(Publish Rate)보다 컨슈머가 소모하는 속도(Consume Rate)가 느리면 큐에 대기 메시지가 적체되는 '메시지 랙(Message Lag)' 현상이 발생합니다. 메시지 랙이 늘어나면 비동기 처리의 실시간성이 무너지며 사용자 경험에 악영향을 줍니다.

1. 필요 컨슈머 수량 및 용량 산정 공식

운영 환경에 필요한 적정 컨슈머 스레드 및 수량을 산정할 때는 피크 타임의 요청 수와 외부 API의 평균 응답 속도를 기준으로 계산해야 합니다.

  • 피크 타임 발생 메시지 수(Input TPS): 초당 500건
  • 외부 API 평균 처리 시간(Latency): 1건당 100ms (초당 1개의 컨슈머 스레드가 처리 가능한 TPS = 10건)
  • 필요 최소 컨슈머 스레드 수: 500 TPS / 10 TPS = 50개 스레드
  • 안전 여유율(20% 포함): 50 × 1.2 = 60개 스레드 필요

외부 API 제공업체가 제시하는 '초당 최대 호출 제한(Rate Limit)'이 존재하는 경우, 컨슈머의 총 스레드 수가 해당 제한을 초과하지 않도록 컨슈머 애플리케이션 레벨에서 속도 제한기(Rate Limiter)를 반드시 걸어야 합니다.

2. Graceful Shutdown(안전한 종료) 처리 점검

새로운 코드를 배포하거나 Auto-scaling에 의해 컨슈머 파드(Pod) 또는 서버가 종료될 때, 현재 메시지를 처리 중인 스레드가 강제 종료되면 작업이 중간에 끊기고 데이터 불일치가 발생합니다.

배포 스크립트 및 컨슈머 애플리케이션 설정에 `SIGTERM` 신호를 받았을 때 신규 메시지 수신을 즉시 중단하고, 현재 처리 중인 메시지의 처리가 완료(Ack)될 때까지 일정 시간(예: 30초) 대기 후 종료되는 Graceful Shutdown 로직이 구현되어 있는지 확인하세요.

[종합 체크리스트] 도입·운영 단계별 최종 점검표

운영 환경 배포 및 서비스 오픈 전, 개발자와 시스템 운영자가 함께 아래 표의 항목을 하나씩 확인하고 체크해야 합니다.

구분 점검 항목 세부 기준 및 권장 수치 확인
아키텍처 메시지 영속성 설정 디스크 Persistence 설정으로 브로커 재부팅 시 메시지 보존 여부 [ ]
재시도 정책 지수 백오프 및 Jitter 최대 재시도 횟수 3~5회, Backoff multiplier 적용 여부 [ ]
타임아웃 Visibility Timeout 외부 API Max Latency의 최소 2배 이상 설정 여부 [ ]
격리 및 보관 DLQ 및 보관 기간 DLQ 분리 생성 완료, 메시지 Retention Period 최소 7일 설정 [ ]
모니터링 메시지 랙 및 DLQ 알림 DLQ > 0 건 및 Message Lag 임계치 초과 시 즉시 알림 작동 여부 [ ]
데이터 정합성 멱등성(Idempotency) 동일 메시지 2회 수신 시 중복 처리 방지 (Unique Key/DB 락) [ ]
용량 산정 Rate Limit 준수 외부 API 호출 제한 수치를 넘지 않도록 컨슈머 Throttle 설정 [ ]
배포 안전성 Graceful Shutdown SIGTERM 수신 시 진행 중인 작업 완료 후 서버 종료 확인 [ ]

현업 담당자를 위한 핵심 실행 요약

메시지 큐를 활용한 비동기 연동 아키텍처 구축 시, 실무자가 즉시 적용해야 할 핵심 실행 항목 4가지는 다음과 같습니다.

  1. 동기식 호출 제거 및 비동기 분리: 외부 API 호출이 메인 트랜잭션을 블로킹하지 않도록 메시지 큐 기반으로 구조 전환.
  2. 재시도 폭주 방지: Exponential Backoff와 Jitter를 필수 적용하고, Visibility Timeout을 외부 API 타임아웃보다 충분히 길게 설정.
  3. DLQ 기반 감지 및 복구 체계 구축: 처리 실패 메시지는 즉시 DLQ로 격리하고, 수동/자동 Replay 도구와 알림 체계를 상시 가동.
  4. 컨슈머 멱등성 보장: 비즈니스 Unique Key를 활용하여 동일한 메시지가 중복 수신되어도 데이터베이스에 이상이 없도록 조건부 업데이트 로직 적용.

인클루드웹 솔루션이 궁금하신가요?

건설관리, 프로젝트관리, 영업관리 솔루션 도입을 무료로 상담해 드립니다.