API 제공업체와의 지갑 이체: 요청 큐가 막히지 않게
플랫폼과 API 제공업체 사이의 연쇄 이체 호출이 요청 큐를 넘치게 하는 방식, 그리고 큐·빠른 응답·결과 푸시로 제공업체가 느려도 플랫폼을 안정적으로 유지하는 방법.
플랫폼은 사용자의 지갑을 보관하고, API 제공업체는 플랫폼이 제공하는 서비스 뒤에서 서버를 운영합니다. 사용자가 어떤 제공업체의 서비스를 쓰기 전에 자금이 플랫폼에서 제공업체로 옮겨지고, 다 쓰면 다시 돌아옵니다. 대부분의 연동은 이 이체를 양방향으로 이어지는 동기 HTTP 호출 사슬로 만듭니다. 부하가 오르면 이 사슬이 양쪽의 요청 큐를 채우고, 느린 제공업체 하나가 플랫폼 전체를 끌어내릴 수 있습니다. 플랫폼의 안정성이 남의 서버에 달려 있어서는 안 됩니다.
이체 한 건이 요청 사슬이 됩니다
플랫폼의 worker는 제공업체가 플랫폼을 다시 호출하는 구간까지 포함해 왕복 내내 붙잡혀 있습니다. 제공업체의 worker는 그 콜백을 기다리는 동안 붙잡혀 있습니다. 서로가 서로를 기다리므로 트래픽이 오르면 양쪽 풀이 동시에 찹니다. 타임아웃이 나면 이체 상태를 알 수 없게 되고, 상태 폴링과 재시도가 뒤따릅니다. 멱등 키가 없으면 재시도로 돈이 두 번 옮겨질 수도 있습니다.
느린 제공업체 하나가 모두의 문제가 되는 과정
벤치마크가 아니라 계산 예시입니다(2026-11-09 작성). 초당 500건의 이체가 세 제공업체에 나뉘어 있고, 플랫폼의 풀은 worker 200개입니다. 사용 중인 worker 수는 리틀의 법칙으로 구합니다. 초당 이체 수 × 한 건이 worker를 붙잡는 초입니다.
| 연쇄 HTTP | 큐, 빠른 접수, 결과 푸시 | |
|---|---|---|
| 사용자 요청이 플랫폼 worker를 붙잡는 시간 | 20 ms + 제공업체 호출 150 ms = 170 ms | 20 ms |
| 초당 500건 이체 때 쓰이는 worker 수 | 500 × 0.17초 = 85 | 500 × 0.02초 = 10 |
| 세 제공업체 중 하나가 5초 타임아웃까지 멈춤 | 약 900: 200개 풀이 바닥나고 모든 제공업체가 실패 | 그대로 10: 그 제공업체의 이체만 기다림 |
| 결과를 기다리는 동안의 요청 | 대기 중인 이체마다 2초마다 상태 폴링 | 없음, 제공업체마다 몇 분에 한 번 일괄 확인 |
변경 후: 빨리 접수하고, 뒤에서 정산하고, 결과를 푸시
- 플랫폼이 먼저 사용자에게 응답합니다. 이체를 처리 중으로 원장에 기록하고 큐에 넣은 뒤 응답합니다. 사용자 화면에는 "처리 중"이 표시되고 이벤트가 오면 저절로 바뀝니다.
- 디스패처가 각 제공업체로 이체를 보냅니다. 멱등 키, 짧은 타임아웃, 제공업체마다 정해진 슬롯 수를 둡니다. 제공업체는 이체를 기록하자마자 202를 돌려주므로 같은 키로 재시도해도 항상 안전합니다.
- 콜백은 없습니다. 요청에 제공업체가 필요한 모든 것을 서명과 함께 담으므로, 제공업체가 확인하려고 플랫폼을 다시 호출할 필요가 없습니다.
- 결과는 푸시로. 제공업체는 그 플랫폼용 비공개 채널, 예를 들어
private-partner-acme에transfer.settled를 발행합니다. 플랫폼 백엔드는 WebSocket 연결 하나를 유지하며 제공업체의 인증 엔드포인트 승인을 받아 구독합니다. 원장을 갱신하고 사용자의 지갑 채널에balance.updated를 발행합니다. 제품 간 잔액 동기화와 같은 방식입니다. - 놓친 것은 한 번의 확인으로. 플랫폼은 몇 분마다 각 제공업체에 아직 처리 중인 이체를 일괄 요청 하나로 묻습니다. 이체마다 폴링하지 않습니다.
제공업체가 불안정해도 플랫폼은 안정적으로
- 제공업체마다 슬롯을 따로. 느려진 제공업체는 디스패처에서 자기 슬롯만 채웁니다. 로그인, 입금, 다른 제공업체의 worker는 그대로 남습니다.
- 응답하지 않는 제공업체에는 보내지 않습니다. 타임아웃이 이어지면 그 제공업체의 큐를 멈추고 서비스를 일시적으로 이용할 수 없다고 표시한 뒤, 잠시 후 다시 시도합니다. 이체는 붙잡힌 연결이 아니라 원장 안에서 처리 중으로 기다립니다.
- WebSocket이 끊겨도 막히는 것은 없습니다. 이체는 계속 접수됩니다. 연결이 돌아오면 일괄 확인이 그사이의 일을 정산합니다.
- 사용자는 제공업체의 응답 시간을 기다리지 않습니다. 화면은 플랫폼 자신의 속도로 응답하고, 결과는 준비되면 나타납니다.
자주 묻는 질문
느린 API 제공업체 하나가 왜 플랫폼 전체를 느리게 하나요?
플랫폼의 worker가 그 제공업체를 기다리기 때문입니다. 동기 호출은 제공업체가 응답할 때까지 worker 하나를 붙잡고, 그 worker는 다른 모든 기능과 공유됩니다. 한 제공업체가 느려지면 worker가 바닥나고, 그 제공업체와 무관한 요청까지 뒤에 줄을 섭니다.
WebSocket이 이체 API를 대체하나요?
아니요. 이체 자체는 멱등 키가 붙은 HTTP 요청으로 남고 양쪽 원장에 기록됩니다. WebSocket이 대체하는 것은 기다림, 즉 붙잡힌 연결, 상태 폴링, 콜백입니다. 결과는 이벤트로 도착합니다.
이체가 정산될 때 플랫폼 연결이 끊겨 있으면요?
잃는 것은 없습니다. 양쪽 원장에 이미 이체가 기록되어 있기 때문입니다. pusher-js와 다른 클라이언트 라이브러리는 스스로 재연결하고, 처리 중인 이체를 일괄 확인하면 놓친 것도 정산됩니다. 이미 적용한 이벤트는 이체 ID로 무시하세요.
연결은 몇 개가 필요한가요?
플랫폼 백엔드에서 각 제공업체 채널로 WebSocket 연결 하나면 그 제공업체와의 모든 이체를 처리할 수 있습니다. 사용자 수와는 상관없습니다. Hibira에서는 발행 한 번이 메시지 하나이고, 유료 플랜에서는 구독자에게 가는 전달을 세지 않습니다. 요금을 보세요.
제품명은 각 소유자의 상표입니다.