거래소 백엔드에서 외부 거래소 API 클라이언트를 Spring 의 RestTemplate 위에 만들면서 공식 문서를 처음부터 끝까지 읽었다. 그것만으로는 부족해서 질의응답 게시판을 뒤졌고 이미 아는 것과 겹치지 않는 항목만 추렸다.
Table of contents
Open Table of contents
배경 — 문서를 다 읽고도 남은 것
공식 문서는 대체로 무엇을 할 수 있는지를 설명하는 데 지면을 쓴다. 어떻게 하면 왜 안 되는지는 대체로 질의응답 쪽에 흩어져 있었다.
겹치지 않는 것을 추려 보니 네 가지가 나왔고 넷 다 문서에는 없었다. 문서만 보고 만들었으면 넷을 전부 밟았을 것이다.
원인 — 사라진 헤더와 자체 계수
한도 잔여량을 X-RateLimit-Remaining 응답 헤더로 주던 것이 정책 변경으로 더 이상 오지 않는다는 답변이 있었다. 그런데 공식 문서에는 그 헤더에 대한 설명이 그대로 남아 있었다.
그 헤더를 보고 속도를 조절하도록 만든 구현은 그대로 무력해진다. 두 가지 방법이 있었다. 헤더 없이 429 를 받으면 그때 물러서는 것과, 상대가 주는 정보에 의존하지 않고 우리 쪽 RateLimiter 가 토큰 버킷으로 직접 세는 것이다. 뒤쪽을 골랐다. 이유는 RFC 6585 가 429 에 Retry-After 를 붙이는 것을 MAY 로만 두고 있어서, 앞쪽은 얼마나 물러설지를 매번 추측해야 해서다. 대신 상대가 한도를 올려도 우리 버킷 값을 손으로 맞추기 전까지는 그 여유를 못 쓰는 것을 감수했다.
두 한도 체계가 함께 돈다
한도 체계가 API 키 단위와 초당 구간 단위 두 가지로 함께 운영되고 있었다. 구간 단위의 초과는 내 쪽 잔여량이 충분해도 그와 무관하게 발생한다.
내 사용량을 아무리 줄여도 그 구간이 혼잡하면 429 가 온다. 우리 쪽 조절로는 못 피하는 종류이므로 429 를 받았을 때의 처리를 ActiveMQ 재시도 큐로 따로 만들어야 했다.
필드별로 다른 생략 동작
PATCH 로 주문을 수정할 때 필드를 빼면 어떻게 되는지가 필드마다 달랐다. price 는 기존 값이 그대로 유지되고 clientOrderId 는 빈 값으로 지워졌다.
RFC 5789 는 PATCH 를 「요청 본문에 적힌 변경 집합을 적용한다」고만 정하고 빠진 필드를 어떻게 볼지는 패치 문서 형식에 맡긴다. 그러니 안 보낸 필드는 안 바뀐다는 가정으로 부분 수정을 하면 다른 값을 지운다. 일괄로 적용되는 규칙이 없으므로 필드별로 확인해 표로 만들어 두는 수밖에 없었다.
제약은 사고가 나야 드러난다
체결 내역 조회가 since 커서 기준 이동식 윈도우라, 상태가 바뀔 때마다 기준이 갱신되고 지나친 시점은 다시 조회할 수 없다는 것도 답변에 있었다. 조회 주기를 상태 전이 간격보다 짧게 두어야 한다는 설계 제약이 거기서 나왔다.
넷 다 문서에 없었던 것은 제약이 사고가 나야 드러나고 그때 문서가 아니라 답변에 적히기 때문이다. 각 항목을 요약하지 않고 원문 인용과 함께 docs/external-api-notes.md 에 남겼는데 문서에 없는 정보는 출처가 유일한 근거이기 때문이다.
판단 기준 — 되돌릴 조건
자체 계수로 간 결정에는 되돌릴 조건을 붙였다. 상대가 잔여량 헤더를 다시 주기 시작하고 그 값이 한 달 동안 우리 RateLimiter 의 계산과 어긋나지 않으면, 헤더 기준으로 되돌린다.
반대로 하루에 429 가 손으로 센 기준치를 넘으면 버킷 값을 낮추는 쪽으로 간다. 어느 쪽이든 판단 자료는 429 건수 하나라서 그 건수를 매일 남기게 했다.
정리
- 공식 문서에 없고 답변에만 있는 제약이 있다
- 문서는 무엇을 할 수 있는지를 말하고 답변은 왜 안 되는지를 말한다
- 문서에 남아 있는
X-RateLimit-Remaining이 실제로는 없을 수 있다 - 상대 정보에 의존하는 구현은 정책 변경에 무력해지므로
RateLimiter로 직접 센다 - 한도 체계가 둘이면 우리 사용량 조절로 못 피하니
429처리를 따로 둔다 PATCH의 필드별 생략 동작을 개별로 확인해 표로 만든다- 이동식 윈도우는 놓치면 복구가 안 되므로 주기를 더 짧게 잡는다
- 문서에 없는 정보는 원문 인용과 함께 남긴다
- 되돌릴 조건은
429건수 하나로 판단한다