Toss Open API Rate Limiter 적용 PRD

배경 (Background)

백엔드 서버는 5개의 Toss Gateway(Account, Alert, Stock, Holding, Order)를 통해 Toss 증권 Open API를 호출한다. 현재 구현에는 429(Too Many Requests) 방어 로직이 없어, 폴링 주기 단축·동시 요청 증가 시 Toss API 제한에 그대로 노출된다. Toss Open API는 API 그룹별 TPS(초당 요청 수)를 명시하며, 초과 시 HTTP 429를 반환한다.

목표 (Goals)

번호목표측정 기준
G1Toss API 그룹별 TPS 한도 내로 자동 조절단위 테스트 시 RateLimiter 임계치 초과 요청이 RequestNotPermitted 를 throw
G2Toss API 연속 실패 시 Circuit 오픈으로 서버 부하 차단실패율 50% 초과 시 CircuitBreaker OPEN 상태 전이 확인
G3임계치 직전까지의 정상 흐름 검증통합 테스트 통과

사용자 시나리오

시나리오 1: 폴링 중 Rate Limit 초과

서비스 사용자가 관심 종목 알림을 다수 등록한 상태에서 폴링 주기가 단축되면, MARKET_DATA 그룹 TPS(10 req/s)가 초과된다. 기존에는 429 → 스택 트레이스 로그 → 정제되지 않은 5xx가 반환됐으나, 적용 후에는 TossRateLimitException으로 정제된 에러가 반환되고 운영자 로그에 [TOSS-429] 이벤트가 기록된다.

시나리오 2: Toss API 연속 장애 시 빠른 차단

Toss Open API가 연속으로 오류를 반환하면, CircuitBreaker가 실패율 50% 초과 시점에 OPEN으로 전이해 이후 30초간 Toss API 호출 자체를 차단한다. 운영자는 [CIRCUIT-OPEN] 로그로 즉시 인지하고, 30초 후 HALF_OPEN → 3건 PROBE → CLOSED 순서로 자동 복구된다.

요구사항

기능 요구사항

ID요구사항우선순위
R-01API 그룹별 RateLimiter를 설정하고 각 Gateway 호출 시 적용한다P0
R-02한도 초과 시 TossRateLimitException 을 throw한다 (즉시 실패, 큐잉 없음)P0
R-03Toss API 실패율 50% 초과 시 CircuitBreaker를 OPEN으로 전이한다P0
R-04OPEN 상태에서 30초 대기 후 HALF_OPEN → 3건 PROBE → 성공 시 CLOSED 전이P1
R-05429 응답 자체도 CircuitBreaker failure로 기록한다P1

API 그룹별 Rate Limit 설정 (Toss 공식 문서 기준)

그룹명대상 Gateway / 메서드TPS 한도
MARKET_DATA가격 조회 (/api/v1/prices)10 req/s
STOCK종목 검색 (/api/v1/stocks)5 req/s
ACCOUNT계좌 조회 (/api/v1/accounts)1 req/s
ASSET보유종목 (/api/v1/holdings)5 req/s
ORDER주문 접수 (/api/v1/orders)6 req/s
AUTH토큰 발급 (/oauth2/token)5 req/s

비기능 요구사항

ID요구사항우선순위
R-06RateLimiter 설정값을 application.yml 에서 관리 (코드 수정 없이 조정 가능)P1
R-07각 Gateway 구현에 AOP 없이 명시적 래핑 방식으로 적용P1
R-08기존 401 재시도 로직과 호환 (RateLimiter → 401 재시도 순서 유지)P1

사용자·운영자 영향

역할변경 전변경 후
서비스 사용자429 → 스택 트레이스 로그 노출TossRateLimitException 으로 정제된 에러 반환
운영자429 발생 추적 불가로그에 429-RATELIMIT, CIRCUIT-OPEN 구조화 이벤트 기록

운영: 모니터링·알림 요구사항

  • 429 수신 시 WARN 레벨로 [TOSS-429] group={group} url={url} 로그 출력
  • CircuitBreaker OPEN 전이 시 WARN 레벨로 [CIRCUIT-OPEN] group=toss 로그 출력
  • Actuator /actuator/circuitbreakerevents 엔드포인트 활성화로 상태 모니터링 가능

범위 외

  • Toss API 미확정 엔드포인트(거래내역·주문 정정·취소) 구현
  • 사용자 요청 레벨 Global Rate Limit (API Gateway 레이어 관심사)
  • Resilience4j Retry 도입 — 멱등 보장 불가 주문 API 포함이므로 범위 제외

완료 기준 (Acceptance Criteria)

  1. ./gradlew test 전체 통과 (기존 테스트 회귀 없음)
  2. 통합 테스트에서 MARKET_DATA 그룹 10 req 정상 → 11번째 TossRateLimitException throw 확인
  3. CircuitBreaker 실패율 50% 초과 시 OPEN 전이 확인
  4. application.yml 에서 rate limit 설정값 변경 후 재시작 없이 반영 확인 (Spring Actuator refresh)

관련 문서