[STK6-02] 5개 Toss Gateway에 RateLimiter + CircuitBreaker 적용

작업 내용 (설계 의도)

변경 사항

STK6-01의 TossRateLimiterFacade 를 5개 Gateway에 주입하고 각 API 호출을 래핑한다. 기존 retryOn401 패턴은 유지하되 그 외곽을 facade.execute(group) { } 로 감싼다. 401 재시도 자체는 RateLimiter 허용 범위에서 동작하므로 (1회 재시도) 별도 처리 불필요.

변경 대상:

Gateway대상 메서드API 그룹
TossStockPriceGatewaygetPricesMARKET_DATA
TossStockGatewayImplsearchStocks, fetchPricesSTOCK, MARKET_DATA
TossHoldingGatewayfetchHoldings, fetchTradeHistoryASSET
TossOrderGatewayplaceOrder, correctOrder, cancelOrderORDER
TossAccountGatewayfetchAccountsACCOUNT

TossAuthClient 의 토큰 발급은 캐싱되므로 AUTH 그룹 적용은 선택 사항으로 한다 (실제 HTTP 호출은 드물게 발생).

다이어그램

처리 흐름

sequenceDiagram
    participant S as Scheduler/UseCase
    participant G as TossXxxGateway
    participant F as TossRateLimiterFacade
    participant T as Toss Open API

    S->>G: fetchXxx()
    G->>F: execute(API_GROUP) { retryOn401 { ... } }
    alt 한도 초과
        F-->>G: TossRateLimitException
        G-->>S: TossRateLimitException
    else Circuit OPEN
        F-->>G: TossCircuitOpenException
        G-->>S: TossCircuitOpenException
    else 정상
        F->>T: RestClient 호출
        T-->>F: 200 OK
        F-->>G: result
        G-->>S: result
    end

테스트 케이스

  • TossStockPriceGatewayTossRateLimiterFacadeMARKET_DATA 그룹으로 호출한다
  • TossStockGatewayImpl.searchStocksTossRateLimiterFacadeSTOCK 그룹으로 호출한다
  • TossStockGatewayImpl.fetchPricesTossRateLimiterFacadeMARKET_DATA 그룹으로 호출한다
  • TossHoldingGateway.fetchHoldingsTossRateLimiterFacadeASSET 그룹으로 호출한다
  • TossAccountGateway.fetchAccountsTossRateLimiterFacadeACCOUNT 그룹으로 호출한다
  • TossOrderGateway.placeOrderTossRateLimiterFacadeORDER 그룹으로 호출한다
  • TossRateLimiterFacadeTossRateLimitException 을 throw하면 Gateway도 그대로 전파한다
  • TossRateLimiterFacadeTossCircuitOpenException 을 throw하면 Gateway도 그대로 전파한다
  • 기존 401 재시도 로직이 RateLimiter 허용 범위 내에서 정상 동작한다