장애 시에도 앱이 동작하도록 하는 안전한 서드파티 API 통합. 타임아웃, 재시도, 서킷 브레이커와 빠른 점검 방법을 배우세요.

서드파티 API는 명확한 "다운" 상태처럼 보이지 않는 방식으로 실패할 수 있습니다. 가장 흔한 문제는 느려지는 것입니다. 요청이 멈추고 응답이 늦게 도착하며 앱이 계속 기다립니다. 이러한 호출이 중요 경로에 놓이면 외부의 작은 문제도 내부 시스템에서 쌓입니다.
이렇게 해서 지역적 지연이 전체 장애로 번집니다. 스레드나 워커가 기다리며 막히고, 큐가 커지고, 데이터베이스 트랜잭션은 더 오래 열려 있으며 새 요청들이 타임아웃되기 시작합니다. 얼마 지나지 않아 외부 API를 사용하지 않는 페이지까지 대기 작업으로 인해 제대로 동작하지 않게 됩니다.
영향은 구체적입니다. 불안정한 신원 제공자는 가입과 로그인을 막습니다. 결제 게이트웨이 타임아웃은 결제를 멈춰 사용자가 과금 여부를 알 수 없게 만듭니다. 메시지 지연은 비밀번호 재설정과 주문 확인을 멈추게 하고, 이는 추가 재시도와 지원 티켓의 두 번째 물결을 촉발합니다.
목표는 간단합니다: 외부 실패를 격리해 핵심 워크플로가 계속 움직이게 하는 것. 예를 들어 결제는 나중에 확인하고 사용자가 주문을 완료하게 하거나, 환영 이메일이 실패해도 가입을 허용하는 식입니다.
실무적 성공 지표는 명확합니다: 제공자가 느리거나 다운일 때도 앱은 빠르고 분명하게 응답해야 하며 영향 범위는 작아야 합니다. 예를 들어 대부분의 핵심 요청은 정상 지연 예산 내에서 완료되고, 실패는 해당 API에 실제로 의존하는 기능에만 국한되며, 사용자에게는 명확한 상태(대기중, 보류, 나중에 다시 시도 등)가 표시되고, 제공자가 복구하면 자동으로 회복됩니다.
대부분의 실패는 시기를 예측할 수는 없지만 유형은 예측 가능합니다. 미리 이름을 붙이면 무엇을 재시도하고 무엇을 중단하며 사용자에게 무엇을 보여줄지 결정할 수 있습니다.
일반적인 분류:
모든 오류가 같은 의미는 아닙니다. 일시적 문제는 재시도할 만한 가치가 있는 경우가 많습니다(네트워크 블립, 타임아웃, 502/503, 일부 429는 대기 후 재시도). 영구적인 문제는 저절로 고쳐지지 않습니다(잘못된 자격 증명, 잘못된 엔드포인트, 권한 거부 등).
모든 오류를 똑같이 처리하면 작은 사고가 다운타임으로 커집니다. 영구적인 실패를 재시도하면 시간 낭비이고 속도 제한에 더 빨리 걸리며 백로그가 쌓여 다른 것들이 느려집니다. 반대로 일시적 실패를 절대 재시도하지 않으면 사용자가 동작을 반복해야 하고, 잠시 후에 완료될 수 있는 작업을 놓칩니다.
결제, 로그인, 비밀번호 재설정, 알림(이메일/SMS/푸시)처럼 잠시 멈추는 것이 큰 문제로 느껴지는 워크플로에 특히 주의하세요. 마케팅 API에서 2초 지연은 성가실 뿐이지만, 결제 승인에서 2초 지연은 수익을 막습니다.
유용한 테스트 질문은: "이 호출이 지금 사용자의 주요 작업을 완료하는 데 반드시 필요한가?" 입니다. 예이면 타이트한 타임아웃, 신중한 재시도, 명확한 실패 경로가 필요합니다. 아니면 큐로 옮겨 앱을 반응형으로 유지하세요.
타임아웃은 더 이상 기다리지 않고 멈추는 최대 시간입니다. 명확한 한계가 없으면 한 공급자의 느려짐이 기다리는 요청을 쌓아 중요한 작업을 차단합니다.
두 가지 대기 유형을 구분하는 것이 도움이 됩니다:
숫자를 고르는 것은 완벽함의 문제가 아닙니다. 사람의 인내심과 워크플로에 맞추는 문제입니다.
타임아웃을 정하는 실무적 방법은 경험에서 역산하는 것입니다:
트레이드오프는 현실적입니다. 너무 길면 스레드, 워커, DB 연결을 묶어두고, 너무 짧으면 거짓 실패를 만들어 불필요한 재시도를 유발합니다.
재시도는 실패가 일시적일 가능성이 클 때 도움이 됩니다: 짧은 네트워크 문제, DNS 일시적 오류, 일회성 500/502/503 등. 이런 경우 두 번째 시도로 성공할 수 있고 사용자는 알아차리지 못할 수 있습니다.
위험은 재시도 폭풍입니다. 많은 클라이언트가 동시에 실패하고 함께 재시도하면 공급자(그리고 여러분의 워커)를 과부하 시킬 수 있습니다. 백오프와 지터(jitter)가 이를 방지합니다.
재시도 예산을 두면 현실적인 수준을 유지할 수 있습니다. 시도 횟수를 낮게 유지하고 총 시간을 제한해 핵심 워크플로가 다른 서비스에 걸려 멈추지 않게 하세요.
400/422 같은 예측 가능한 클라이언트 오류, 401/403 인증 문제, 404 같은 경우는 재시도하지 마세요. 이런 오류는 다시 시도해도 거의 실패합니다.
또 한 가지 방어책: POST/PUT 같은 쓰기 작업은 멱등성이 확보되어 있을 때만 재시도하세요. 그렇지 않으면 중복 청구나 중복 레코드가 발생할 수 있습니다.
멱등성은 같은 요청을 여러 번 실행해도 최종 결과가 동일하도록 하는 것을 의미합니다. 네트워크가 끊기고 서버가 재시작하며 클라이언트가 타임아웃되는 상황에서 재시도는 정상입니다. 멱등성이 없으면 "도와주려는" 재시도가 중복을 만들고 실제 금전 문제를 야기합니다.
예를 들어 체크아웃: 결제 API가 느려 앱이 타임아웃되고 재시도하면, 첫 번째 호출이 이미 성공했더라도 재시도로 인해 두 번째 청구가 발생할 수 있습니다. 주문 생성, 구독 시작, 이메일/SMS 발송, 환불 처리, 지원 티켓 생성 같은 작업에도 동일한 위험이 존재합니다.
해결책은 멱등성 키(또는 요청 ID)를 모든 "무언가를 수행하는" 호출에 붙이는 것입니다. 이 키는 시도별이 아니라 사용자 행동별로 고유해야 합니다. 제공자(또는 자체 서비스)는 그 키를 사용해 중복을 감지하고 같은 결과를 반환하도록 해야 합니다.
멱등성 키를 헤더에 붙이는 것처럼 대충 다루지 말고 데이터 모델의 일부로 취급하세요.
사용자가 액션을 시작할 때(예: Pay 버튼 클릭) 하나의 키를 생성하고 로컬 기록과 함께 저장하세요.
각 시도에서:
내부 호출의 "제공자(provider)" 역할을 한다면 서버 측에서도 동일한 동작을 강제하세요.
서킷 브레이커는 안전 스위치입니다. 외부 서비스가 실패를 시작하면 일정 기간 호출을 차단해 더 많은 요청이 쌓여 타임아웃되는 것을 막습니다.
서킷 브레이커는 보통 세 가지 상태를 가집니다:
브레이커가 열려 있을 때 앱은 예측 가능한 동작을 해야 합니다. 가입 중 주소 검증 API가 다운이면 주소를 받아두고 나중에 검토하도록 표시하세요. 결제 위험 점검이 다운이면 주문을 수동 검토 큐로 보내거나 해당 옵션을 임시로 비활성화하고 이유를 설명하세요.
임계값은 사용자 영향에 맞춰 선택하세요:
쿨다운은 짧게(몇 초에서 1분) 유지하고 반열림 테스트는 제한적으로 하세요. 목표는 먼저 핵심 워크플로를 보호하고 빠르게 복구하는 것입니다.
외부 API가 느리거나 다운되었을 때 목표는 사용자가 계속 진행할 수 있게 하는 것입니다. 즉 솔직한 플랜 B를 준비해야 합니다.
API가 제시간에 응답하지 못할 때 앱이 하는 일입니다. 캐시된 데이터 사용, 저하된 모드로 전환(비핵심 위젯 숨김, 선택적 동작 비활성화), API 호출 대신 사용자 입력 요청(수동 주소 입력), 또는 명확한 다음 단계 메시지 표시 등이 포함됩니다.
정직하게 처리하세요: 완료되지 않은 작업을 완료되었다고 표시하지 마세요.
작업이 사용자 요청 내에서 반드시 끝나야 하지 않다면 큐로 밀고 빨리 응답하세요. 일반적인 후보: 이메일 발송, CRM 동기화, 보고서 생성, 분석 이벤트 포스트 등.
핵심 작업은 빠르게 실패(fail fast)하세요. API가 체크아웃(또는 계정 생성)을 완료하는 데 필요 없다면 요청을 차단하지 마세요. 주문을 수락하고 외부 호출을 큐에 넣어 나중에 조정하세요. 만약 API가 필수(예: 결제 승인)라면 사용자를 오래 기다리게 하지 말고 빠르게 실패하며 명확한 메시지를 제공하세요.
사용자가 보는 것과 내부에서 일어나는 일이 일치해야 합니다: 명확한 상태(완료, 보류, 실패), 약속(영수증 즉시, 확인은 나중에), 재시도 방법, UI상의 가시적 기록(활동 로그, 보류 배지).
속도 제한은 제공자가 "너희는 호출할 수 있지만 너무 자주 하지 마"라고 말하는 방식입니다. 생각보다 빨리 걸립니다: 트래픽 급증, 동시에 실행되는 백그라운드 잡, 또는 오류를 반복하는 버그 등.
먼저 생성하는 요청 수를 제어하세요. 가능한 경우 배치 처리하고, 안전하다면 30~60초 정도 응답을 캐시하며, 클라이언트 측에서 스로틀링해 앱이 공급자보다 빠르게 폭발적으로 호출하지 않도록 하세요.
429 Too Many Requests를 받으면 속도를 늦추라는 신호로 처리하세요.
Retry-After가 제공되면 존중하세요.동시성도 제한하세요. 하나의 워크플로(예: 연락처 동기화)가 모든 워커 슬롯을 소모해 로그인이나 체크아웃 같은 핵심 흐름을 고갈시키면 안 됩니다. 별도 풀이나 기능별 캡을 사용하는 것이 도움이 됩니다.
모든 서드파티 호출에는 실패에 대한 계획이 필요합니다. 완벽할 필요는 없습니다. 공급자가 안 좋은 날을 보낼 때 예측 가능한 동작이면 됩니다.
호출이 지금 실패하면 무엇이 일어나는지 결정하세요. 체크아웃 중 세금 계산은 필수일 수 있고, 마케팅 연락처 동기화는 보통 기다릴 수 있습니다. 이 선택이 나머지를 좌우합니다.
호출 유형별로 타임아웃을 정하고 일관되게 유지하세요. 그런 다음 재시도 예산을 정해 느린 API를 계속 두드리지 않게 하세요.
작업이 무언가를 생성하거나 금전을 청구할 수 있으면 멱등성 키를 추가하고 요청 기록을 저장하세요. 결제 요청이 타임아웃되면 재시도가 중복 청구를 만들어선 안 됩니다. 추적은 지원팀이 "처리되었나요?"라고 물을 때도 도움이 됩니다.
오류가 급증하면 공급자 호출을 짧게 중단하세요. 필수 호출에는 명확한 "다시 시도" 경로를 보여주고, 기다릴 수 있는 호출은 큐에 넣어 나중에 처리하세요.
지연, 오류율, 브레이커 열림/닫힘 이벤트를 추적하세요. 단일 일시적 이상 현상이 아니라 지속적인 변화에 대해 알림을 설정하세요.
대부분의 API 장애는 처음부터 크지 않습니다. 앱이 최악의 방식으로 반응하기 때문에 커집니다: 너무 오래 기다리고, 과도하게 재시도하며, 다른 모든 것을 유지하는 동일한 워커를 묶어둡니다.
이 패턴들이 연쇄 반응을 일으킵니다:
작은 수정으로 큰 장애를 예방할 수 있습니다: 일시적일 가능성이 있는 오류(타임아웃, 일부 429, 일부 5xx)만 재시도하고 백오프와 지터로 시도 횟수 제한; 타임아웃은 짧고 의도적으로 설정; 생성/청구 작업엔 멱등성 요구; 부분적 장애를 염두에 둔 설계.
통합을 프로덕션에 배포하기 전에 실패 관점으로 빠르게 점검하세요. 핵심 워크플로(가입, 체크아웃, 메시지 전송)와 관련된 항목에 대해 "예"로 대답할 수 없다면 릴리즈 차단으로 간주하세요.
결제 공급자가 타임아웃되기 시작하면 올바른 동작은 "체크아웃이 여전히 로드되고, 사용자에게 명확한 메시지가 표시되며, 무한히 대기하지 않는다"이지, "모든 것이 타임아웃될 때까지 멈춘다"가 아닙니다.
체크아웃이 세 가지 서비스를 호출한다고 가정해보세요: 카드를 청구하는 결제 API, 세금을 계산하는 세금 API, 영수증을 보내는 이메일 API.
결제 호출만 동기화가 필수입니다. 세금이나 이메일의 문제로 구매가 막혀선 안 됩니다.
세금 API가 가끔 8~15초 걸린다면 체크아웃이 기다리면 사용자가 장바구니를 버리고 앱은 워커를 묶습니다.
더 안전한 흐름:
결과: 세금 공급자가 느릴 때 장바구니 포기와 주문 정체가 줄어듭니다.
영수증 이메일은 중요하지만 결제 캡처를 막아선 안 됩니다. 이메일 API가 실패하면 몇 번의 빠른 실패 후 서킷 브레이커가 열리고 짧은 쿨다운 동안 호출을 중단해야 합니다.
이메일을 인라인으로 보내지 말고 멱등성 키(예: order_id + email_type)와 함께 "영수증 전송" 작업을 큐에 넣으세요. 공급자가 다운되면 큐가 백그라운드에서 재시도하고 고객은 여전히 성공적으로 구매한 것을 보게 됩니다.
결과: 누락된 확인으로 인한 지원 티켓이 줄고, 비결제 이유로 체크아웃이 실패해 수익을 잃지 않습니다.
가장 문제가 되는 워크플로우 하나(체크아웃, 가입, 청구)를 선택해 기준 통합으로 삼고 같은 기본값을 복사해 다른 곳에 적용하세요.
간단한 롤아웃 순서:
기본값을 문서화하고 단순하게 유지하세요: 하나의 연결 타임아웃, 하나의 요청 타임아웃, 최대 재시도 횟수, 백오프 범위, 브레이커 쿨다운, 재시도 가능 규칙 등.
다음 워크플로로 확장하기 전에 실패 드릴을 실행하세요. 테스트 환경에서 타임아웃을 강제로 발생시키거나 공급자를 차단하고 사용자가 유용한 메시지를 보는지, 폴백 경로가 작동하는지, 큐에 쌓인 재시도가 영원히 쌓이지 않는지 확인하세요.
신제품을 빠르게 만드는 팀이라면 이 신뢰성 기본값을 재사용 가능한 템플릿으로 만드는 것이 가치가 있습니다. Koder.ai (koder.ai)를 사용하는 팀의 경우, 타임아웃, 재시도, 멱등성, 브레이커 규칙을 한 번 정의한 뒤 새 서비스 전반에 동일한 패턴을 적용하는 경우가 많습니다.