BTP API Management Rate Limit 모르면 큰일 #shorts #SAP #BTP

Moderator
BTP API Management 3대 트래픽 정책 비교

1. 왜 Rate Limit을 반드시 설정해야 하는가?

API를 외부에 공개했는데 Rate Limit을 걸지 않으면 어떤 일이 벌어질까요? 실제 현장에서 가장 많이 발생하는 장애 패턴 세 가지를 먼저 짚고 갑니다. 이 설정을 안 하면 정말로 새벽에 호출받습니다.

SAP BTP API Management(Apigee Edge 기반)는 Policy 단계에서 트래픽을 1차로 차단하므로 백엔드 비즈니스 로직보다 훨씬 가볍게 거절합니다. 일반적으로 백엔드 보호의 첫 번째 방어선으로 권장됩니다.

학습 체크리스트입니다.

2. SAP BTP API Management 3대 트래픽 정책 비교

세 가지 정책이 비슷해 보이지만 적용 목적이 완전히 다릅니다. 한 줄 비유부터 잡고 가겠습니다.

정책비유측정 단위주 목적
Spike Arrest고속도로 톨게이트 차단봉초/분 (smoothing)순간 폭주 방어
Rate Limit분당 입장 카운터분/시간 (윈도우)처리량 평탄화
Quota월 정액 요금제일/주/월비즈니스 라이선스

Spike Arrest는 "Rate 100ps"로 설정해도 100건을 1초 안에 받지 않습니다. 10ms 간격으로 균등 분산되어 들어와야 통과시킵니다. 즉 burst 자체를 거부하는 정책입니다. 반면 Rate Limit은 "1분에 1000건까지 OK, 1초 안에 다 들어와도 OK"라는 윈도우 카운팅 방식입니다. Quota는 개발자(API Key) 단위로 "이 앱은 하루 10000건"처럼 비즈니스 SLA를 강제할 때 씁니다.

실무 결론을 먼저 말씀드리면, 외부 공개 API에는 보통 Spike Arrest + Quota 조합을 PreFlow에 둡니다. Rate Limit은 두 정책 사이의 미세 조정 또는 내부 백엔드 보호용으로 사용하는 경우가 많습니다.

3. Rate Limit 정책 XML 설정 실전

API Management 콘솔의 API Proxy → Policies → Create Policy → "Rate Limit" 선택 후 다음 XML을 붙여넣습니다. PreFlow Request 단계에 attach 하는 것이 표준입니다.

<!-- 분당 1000건, 클라이언트 IP 기준 -->
<RateLimit async="false" continueOnError="false" enabled="true" type="HierarchicalRateLimit" xmlns="http://www.sap.com/apimgmt">
  <DisplayName>RL-PerClientIP-1000pm</DisplayName>
  <Identifier ref="client.ip" />
  <MessageWeight ref="request.header.weight" />
  <Rate>
    <Allow count="1000" />
    <Interval ref="request.header.interval">1</Interval>
    <TimeUnit ref="request.header.timeunit">minute</TimeUnit>
  </Rate>
</RateLimit>

핵심 속성을 짚어보면, Identifier는 카운팅 키입니다. client.ip 대신 verifyapikey.{policyName}.client_idrequest.header.X-Consumer-Id를 쓰면 앱 단위로 카운트됩니다. MessageWeight는 한 요청을 N건으로 셀 때 씁니다(예: 무거운 검색 API는 weight=5).

continueOnError="false"로 두면 한도 초과 시 즉시 거절합니다. 응답 헤더에 X-RateLimit-Remaining이 자동으로 실리므로 클라이언트가 백오프 로직을 짤 수 있습니다.

4. Quota 정책 설정 — 일/월 단위 호출 제한

Quota는 보통 API Product 단위 또는 Developer App 단위로 거는 비즈니스 정책입니다. "Free 플랜은 하루 1만 건, Premium은 하루 100만 건"을 구현하는 핵심 도구입니다.

<Quota async="false" continueOnError="false" enabled="true" type="calendar" xmlns="http://www.sap.com/apimgmt">
  <DisplayName>Quota-PerApp-DailyTier</DisplayName>
  <Identifier ref="verifyapikey.VerifyAPIKey.client_id" />
  <Allow countRef="verifyapikey.VerifyAPIKey.apiproduct.developer.quota.limit" count="10000" />
  <Interval ref="verifyapikey.VerifyAPIKey.apiproduct.developer.quota.interval">1</Interval>
  <TimeUnit ref="verifyapikey.VerifyAPIKey.apiproduct.developer.quota.timeunit">day</TimeUnit>
  <StartTime>2026-01-01 00:00:00</StartTime>
  <Distributed>true</Distributed>
  <Synchronous>false</Synchronous>
</Quota>

여기서 가장 중요한 패턴은 countRefAPI Product에 정의된 quota 값을 동적으로 읽어오는 것입니다. Product 정의에서 Free=10000, Premium=1000000을 설정해두면 같은 정책 XML 하나로 전 플랜을 처리할 수 있습니다.

Distributed=true는 멀티 게이트웨이 노드가 카운트를 공유하도록 합니다. 운영 환경에서는 반드시 켜야 하며, 그렇지 않으면 노드 수만큼 한도가 곱해져 들어옵니다. type="calendar"는 고정 시작점부터 카운팅하며, "rollingwindow"로 바꾸면 슬라이딩 윈도우로 동작합니다.

5. Spike Arrest 정책 — 순간 폭주 차단

Spike Arrest는 가장 가벼운 첫 번째 방어선입니다. 분산 카운팅을 하지 않고 노드별로 즉시 판단하므로 응답 지연이 거의 없습니다.

<SpikeArrest async="false" continueOnError="false" enabled="true" xmlns="http://www.sap.com/apimgmt">
  <DisplayName>SA-Frontline-30ps</DisplayName>
  <Identifier ref="client.ip" />
  <MessageWeight ref="request.header.weight" />
  <Rate>30ps</Rate>
</SpikeArrest>

여기서 가장 많이 오해하는 부분 — 30ps는 "1초에 30건"이 아니라 "약 33ms당 1건"으로 균등 분산된 흐름만 통과시킵니다. 즉 1초 시작 시점에 30건이 한꺼번에 들이닥치면 첫 1건만 통과하고 나머지 29건은 모두 SpikeArrestViolation으로 거절됩니다.

이게 바로 Spike Arrest의 핵심 가치입니다. burst를 평탄화하는 것이 아니라 burst 자체를 거부해서 백엔드의 thread pool, DB connection이 갑자기 동나는 것을 막아줍니다. 정상적인 사용자라면 33ms 간격은 인지하지 못하므로 UX에 영향이 없습니다.

참고로 Rate30ps(per second) 또는 1800pm(per minute) 형식으로 지정할 수 있습니다. pm은 1초 단위로 균등 분산되므로 좀 더 관대한 통과 패턴이 됩니다.

6. 정책 조합 전략 — Spike Arrest + Quota 같이 쓰기

실전에서는 단일 정책으로는 부족합니다. 일반적으로 권장되는 다층 방어 패턴은 다음과 같습니다.

  1. 1차 (PreFlow 최상단) — Spike Arrest로 비정상 burst 즉시 차단. IP 또는 client_id 기준
  2. 2차 (VerifyAPIKey 직후) — Quota로 비즈니스 플랜 한도 체크. API Product 값 동적 참조
  3. 3차 (선택) — Rate Limit으로 분당 처리량 평탄화. 백엔드가 약한 경우만

순서가 중요합니다. Spike Arrest를 가장 먼저 배치해야 인증 모듈(VerifyAPIKey, OAuth2)이 폭주 트래픽에 노출되지 않습니다. 인증은 의외로 비싼 작업이라 여기서 막히면 의미가 없습니다.

<!-- PreFlow 흐름 (의사 코드) -->
<PreFlow name="PreFlow">
  <Request>
    <Step><Name>SA-Frontline-30ps</Name></Step>
    <Step><Name>VerifyAPIKey</Name></Step>
    <Step><Name>Quota-PerApp-DailyTier</Name></Step>
    <Step><Name>RL-PerClientIP-1000pm</Name></Step>
  </Request>
</PreFlow>

한 가지 팁 — Quota는 호출이 백엔드까지 도달했는지 여부와 무관하게 카운트됩니다. 만약 백엔드가 5xx로 실패한 호출은 Quota에서 제외하고 싶다면 Synchronous="false"로 두고 PostFlow에서 조건부 ResetQuota 정책으로 되돌리는 패턴을 씁니다.

7. 에러 핸들링 — 429 응답 커스터마이징

기본 거절 응답은 XML 또는 plain text로 내려와서 모바일/SPA 클라이언트가 파싱하기 불편합니다. RaiseFault 정책으로 JSON 응답을 표준화하는 것이 권장됩니다.

<!-- Fault Rule for SpikeArrestViolation / RateLimitViolation / QuotaViolation -->
<FaultRules>
  <FaultRule name="ThrottleFault">
    <Condition>(fault.name = "SpikeArrestViolation") or (ratelimit.RL-PerClientIP-1000pm.failed = "true") or (ratelimit.Quota-PerApp-DailyTier.exceeded = "true")</Condition>
    <Step><Name>RaiseFault-429</Name></Step>
  </FaultRule>
</FaultRules>
{
  "error": {
    "code": "TOO_MANY_REQUESTS",
    "status": 429,
    "message": "API call limit exceeded. Please retry after the indicated period.",
    "retryAfterSeconds": 60,
    "docUrl": "https://developer.example.com/limits"
  }
}

흔한 실수 FAQ

8. 핵심 한 줄 (요약)

Spike Arrest로 burst를 막고, Quota로 비즈니스 한도를 강제하고, Rate Limit으로 미세 조정한다 — 이 세 줄이 BTP API Management 트래픽 방어의 전부입니다.

다음 단계로는 ① Custom Attribute를 활용한 동적 Quota(Premium/Free 플랜 분기), ② Cloud Logging Service와 연동한 정책 위반 알람, ③ Generative AI Hub 앞단에 토큰 단위 weight를 적용한 비용 가드 패턴을 학습해보시기를 권장합니다. 특히 LLM API는 토큰당 과금이라 단순 호출 카운팅으로는 비용 통제가 안 됩니다 — MessageWeight를 입력 토큰 수에 비례시키는 것이 권장 패턴입니다.

참고 자료입니다.

Spike Arrest + Rate Limit XML 설정 Quota 설정 + 429 에러 커스터마이징