API 호출 제한 장치 설계 - Hot-stock/backend GitHub Wiki

시나리오

사용자가 Search 서비스를 호출하면, 로그인된 사용자라면 Access Token을, 비로그인 사용자라면 IP 주소를 기반으로 Redis에 저장된 API 호출 제한 정보를 확인합니다. 해당 Bucket에 Token이 남아있으면 API 호출이 가능하며, 그렇지 않으면 상태 코드 429(Too Many Requests)를 반환합니다.

1. Search 서비스 호출

  • 사용자는 Search 서비스 API를 호출합니다. 이때 요청에는 사용자의 Access Token이 포함될 수 있습니다. 만약 사용자가 로그인되지 않은 상태라면, IP 주소를 통해 사용자 식별이 진행됩니다.

2. Redis에서 저장된 Bucket 획득

  • 서비스는 Redis에 저장된 사용자의 Rate Limiting Bucket을 조회합니다.
  • 로그인된 사용자의 경우, Access Token을 사용해 Redis에서 해당 사용자의 Bucket을 찾습니다.
  • 비로그인 사용자의 경우, IP 주소를 사용하여 해당 IP와 관련된 Bucket을 조회합니다.

3. Bucket에서 Token 확인

  • Redis에서 조회한 Bucket에 API 호출에 필요한 Token이 남아 있는지 확인합니다.
  • 만약 Token이 남아 있으면, API 호출이 허용되고 정상적인 응답을 반환합니다.
  • Token이 남아 있지 않다면, 서비스는 HTTP 429 Too Many Requests 상태 코드를 반환하며, 추가 API 호출이 제한됩니다.

프로세스


프로세스 다이어그램

flowchart TD
    A[사용자가 Search 서비스 호출] --> B{로그인 여부 확인}
    B -->|로그인 사용자| C[Access Token 기반 Key 생성]
    B -->|비로그인 사용자| D[IP 주소 기반 Key 생성]
    
    C --> E[Redis에서 Bucket 조회]
    D --> E[Redis에서 Bucket 조회]
    
    E -->|Bucket 존재| F{Token 남아있는지 확인}
    F -->|Token 있음| G[API 호출 허용, 정상 응답]
    F -->|Token 없음| H[Status Code 429 반환]
    
    E -->|Bucket 없음| I[새로운 Bucket 생성]
    I --> F

에러 처리 및 예외 상황

호출 제한

Search 서비스는 API 호출 제한을 통해 특정 사용자가 일정 시간 내에 과도한 요청을 보내지 않도록 보호합니다. 로그인된 사용자는 Access Token을 기반으로, 비로그인 사용자는 IP 주소를 기반으로 API 호출 제한을 적용받습니다.

예외 처리 흐름

  1. API 호출 제한 초과:

    • 사용자가 설정된 호출 제한을 초과할 경우, TooManyRequestsException 예외가 발생합니다.
    • 이 예외는 사용자가 일정 시간 동안 허용된 API 호출 횟수를 초과했음을 의미합니다.
  2. ExceptionHandler를 통한 예외 처리:

    • TooManyRequestsException이 발생하면 CommnonControllerException 클래스의 @ExceptionHandler가 이 예외를 처리합니다.
    • 호출 제한을 초과한 사용자에게는 **HTTP 상태 코드 429 (Too Many Requests)**와 함께 적절한 에러 메시지가 반환됩니다.
  3. 에러 메시지:

    • 예외 처리 후, 클라이언트는 다음과 같은 에러 메시지와 응답을 받게 됩니다:

      {
        "message": "API call limit exceeded"
      }
      
    • 이 응답은 클라이언트에게 API 호출이 제한되었음을 알려주며, 제한된 호출 횟수가 다시 초기화될 때까지 대기해야 합니다.

테스트 케이스

1. 비로그인 유저 호출 테스트

  • 목적: 로그인하지 않은 사용자가 API를 호출할 때, 요청자의 IP 주소를 기반으로 호출 제한이 적용되는지 확인.
  • Given: 사용자가 로그인하지 않고, API 호출을 시도합니다.
  • When: API 호출 시 요청자의 IP 주소를 획득하고, Redis에 저장한 후, 호출 제한을 적용합니다.
  • Then: 호출 제한 버킷이 생성되고, API 호출이 정상적으로 제한자가 작동하는지 확인합니다.

2. 로그인 유저 호출 테스트

  • 목적: 로그인된 사용자가 Access Token을 이용해 API를 호출할 때, 호출 제한이 정상적으로 동작하는지 확인.
  • Given: 사용자가 로그인한 상태에서 API 호출을 시도합니다.
  • When: Access Token을 기반으로 사용자 식별을 하고, Redis에 해당 사용자와 연관된 호출 제한 버킷을 조회합니다.
  • Then: 호출 제한이 정상적으로 적용되고, 사용자가 설정된 제한 내에서 API 호출을 성공할 수 있는지 확인합니다.

3. 토큰 리필 테스트

  • 목적: 사용자의 호출 제한 버킷에 있는 토큰이 리필되는지 확인.
  • Given: 사용자의 API 호출 제한이 다 소모된 상태입니다.
  • When: 일정 시간이 경과하여 호출 제한 토큰이 자동으로 리필됩니다.
  • Then: 사용자의 토큰이 다시 채워지고, API 호출이 정상적으로 허용되는지 확인합니다.

4. 호출 제한 초과 테스트

  • 목적: 사용자가 설정된 호출 횟수를 초과했을 때, 호출이 차단되는지 확인.
  • Given: 사용자가 정해진 호출 횟수를 초과한 상태입니다.
  • When: 사용자가 추가로 API 호출을 시도합니다.
  • Then: **HTTP 상태 코드 429 (Too Many Requests)**와 함께 호출이 차단됩니다.

5. 호출 제한 초기화 테스트

  • 목적: 호출 제한 기간이 만료된 후, 새로운 API 호출이 허용되는지 확인.
  • Given: 사용자가 호출 제한에 도달했으나, 제한 시간이 경과했습니다.
  • When: 제한 시간 후 API를 호출합니다.
  • Then: 호출 제한이 리셋되고, 새로운 호출이 허용되는지 확인합니다.