프로젝트/기술 면접 복습 플랫폼

OpenAI 429 Too Many Requests 에러

yoon4360 2025. 4. 30. 17:33

OpenAI API를 본격적으로 사용하면서 가장 먼저 마주친 문제는 바로 429 Too Many Requests 에러였다.

분명히 정상적으로 요청을 보냈는데 왜 에러가 나지 싶었는데, 알고 보니 OpenAI는 생각보다 빡빡한 요청 속도 제한 정책을 가지고 있었다.
이번 포스팅에서는 이 문제를 어떻게 해결했는지, 시행착오와 함께 정리해보려 한다.

 

429 에러

처음에 WebClient를 이용해서 OpenAI에 요약과 임베딩 요청을 연속으로 보내다 보니, 가끔 이런 에러가 터졌다

429 Too Many Requests

 

찾아보니 OpenAI는 사용자마다 시간당, 분당 호출 횟수 제한이 있었다.

특히 무료 플랜이나 초기 설정에서는 이 제한이 꽤 낮은 편이었다.
WebClient는 비동기로 빠르게 요청을 날려버리기 때문에, 서버 입장에서는 너무 많은 요청을 한꺼번에 보낸 것으로 인식하게 된 것이다.

 

요약해보자면, 요청이 너무 빠르면 OpenAI가 막아버리고 연속 호출할 때 더 쉽게 터진다.

 

해결 방법 고민

1. 요청 간 딜레이 추가

가장 단순한 방법은 모든 요청 사이에 딜레이를 주는 것이었다.

예를 들면, 요청을 보내기 전에 1초 정도 기다리는 방식이다.

 

장점은 과도한 요청을 방지해준다.

단점은모든 요청이 느려지고 429가 안 날 때도 느려진다.

 

2. 재시도 로직 추가

429가 발생했을 때만 자동 재시도하도록 설정하는 방법도 생각해봤다.

 

장점은평소에는 빠르게 요청하고 문제가 생길 때만 딜레이가 적용된다.

단점은약간 복잡한 로직 필요하다.

 

3. OpenAI에 Rate Limit 상향 요청

OpenAI에 요청해서 사용량 제한을 높이는 방법도 있었다.

하지만 이건 더 높은 유료 플랜을 써야 하고, 승인에도 시간이 걸렸다.

당장 급한 문제를 해결하는 데는 부적절했다.

 

내가 선택한 해결책 - 재시도 + 기본 딜레이 병행

결국 나는 재시도 로직과 기본 딜레이를 같이 적용하는 전략을 선택했다.

  • 기본적으로 요청마다 1초 딜레이를 걸어 과속을 방지한다.
  • 만약 429 에러가 발생하면, 최대 3번까지 2초 간격으로 재시도한다.

이렇게 하면 평소에는 빠르고, 문제가 생겨도 자동 복구할 수 있다.

 

적용한 코드

Spring WebClient를 이렇게 구성했다.

webClient.post()
    .uri(url)
    .header(HttpHeaders.AUTHORIZATION, "Bearer " + openAiApiKey)
    .contentType(MediaType.APPLICATION_JSON)
    .bodyValue(requestBody)
    .retrieve()
    .onStatus(HttpStatus.TOO_MANY_REQUESTS::equals, clientResponse -> {
        return Mono.error(new RuntimeException("429 Too Many Requests"));
    })
    .bodyToMono(Map.class)
    .delaySubscription(Duration.ofSeconds(1)) // 요청 간 기본 1초 딜레이
    .retryWhen(Retry.fixedDelay(3, Duration.ofSeconds(2)) // 429 시 최대 3번, 2초 간격 재시도
        .filter(throwable -> throwable instanceof RuntimeException &&
                             throwable.getMessage().contains("429")))
    .block();

 

코드 설명

  • delaySubscription(1초): 모든 요청 전에 1초 기다린다.
  • onStatus(429): 429 상태 코드가 오면 에러로 전환한다.
  • retryWhen(최대 3회 재시도, 2초 간격): 429 에러가 발생했을 때만 재시도한다.

핵심은 딜레이를 기본으로 주되, 재시도는 "오류가 났을 때만" 한다는 것이다.

 

결과

이렇게 적용하고 나니까 확실히 달라졌다.

  • OpenAI API 요청 중 429 에러 빈도가 급감했다.
  • 아주 가끔 429가 발생해도 자동 재시도로 문제 없이 넘어갔다.
  • 요청 속도와 안정성 둘 다 확보했다.

특히 사용자 경험(UX) 측면에서도 큰 문제 없이, 빠르고 안정적인 API 호출이 가능해졌다.

 

마무리

Qdrant 컬렉션 생성 문제에서는 단순 예외 처리로 대응했지만,
이번 OpenAI 429 문제는 효율적인 요청 관리가 중요했기 때문에 재시도 + 딜레이 전략을 선택했다.

단순 무식하게 느리게 하는 대신, 필요할 때만 느리게 했다.

 

추후에는 더 정교한 Rate Limiter를 적용해서, 현재 트래픽 상황에 따라 딜레이를 동적으로 조정하는 것도 시도해볼 생각이다.

예를 들어, API 호출량이 많아질 조짐이 보이면 알아서 요청 간격을 늘리는 방식이다.
또 OpenAI의 Rate Limit 상향도 검토 중이다. 다만 돈이 문제이다...