
Spring Boot에서 Claude API의 에러 및 Rate Limit에 강하게 만들기
요약
Spring Boot 환경에서 Claude API 호출 시 발생하는 Rate Limit(429) 및 서버 에러(5xx)에 대응하기 위한 재시도 로직 구현 방법을 다룹니다. 지수 백오프(Exponential Backoff) 전략을 사용하여 시스템의 안정성을 높이는 최소 구현 가이드를 제공합니다.
핵심 포인트
- 429 및 5xx 에러는 재시도 대상으로, 4xx 에러는 즉시 실패 대상으로 분류
- 재시도 판단 로직을 순수 함수로 분리하여 테스트 용이성 확보
- 지수 백오프를 통한 효율적인 재시도 간격 제어
- 무한 루프 방지를 위한 최대 시도 횟수(MAX_ATTEMPTS) 설정 필수
이 기사에 대하여
대상 독자: 첫 번째 글을 통해 Claude를 호출할 수 있게 되었고, 실운용을 위해 실패 시의 동작을 정비하고 싶은 사람 -
얻을 수 있는 것: Rate Limit (429)・서버 에러 (5xx)・네트워크 단절을 지수 백오프 (Exponential Backoff)로 재시도하고, 그 외에는 즉시 실패시키는 최소 구현 -
전제·환경: Java 21 / Spring Boot 3.5 / com.anthropic:anthropic-java 2.34.0
이 기사는 「Spring Boot에서 공식 Java SDK로 Claude API를 호출하는 최소 구현」의 후속편입니다. 클라이언트의 Bean화 및 설정 분리 (AnthropicClientConfig / AnthropicProperties)는 지난번 내용을 계승합니다.
버전 (SDK・Spring Boot) 및 모델명은 집필 시점 (2026년 6월) 기준입니다. 최신 정보는 공식 정보를 확인해 주세요.
결론 (먼저 전체상 파악)
에러는 「재시도로 해결되는 것」과 「해결되지 않는 것」으로 나누어 처리합니다.
재시도함: 429 (Rate Limit)・5xx (500・529 overloaded 등)・네트워크 단절 -
즉시 실패: 400・401・404 등 (재시도해도 해결되지 않음)
판정 로직을 SDK나 HTTP에 의존하지 않는 **순수 함수 (Pure Function)**로 분리하면, 그대로 테스트할 수 있습니다.
public static boolean isRetryable(int statusCode) {
return statusCode == 429 || statusCode >= 500;
}
공식 SDK도 집필 시점에는 기본적으로 수 회 리트라이를 수행하지만, 「업무에 맞춰 횟수나 백오프를 제어하고 싶다」거나 「로그를 남기고 싶다」면 직접 구현하는 것이 동작을 파악하기 쉽습니다.
단계 1: 재시도 가능 여부와 백오프를 순수 함수로 만들기
재시도의 「판단」과 「대기 시간」을 독립시킵니다. HTTP나 네트워크를 건드리지 않으므로 유닛 테스트 (Unit Test)를 작성하기 쉬워집니다.
public final class RetryPolicy {
private RetryPolicy() {}
// 429와 5xx는 일시적이므로 재시도. 4xx는 해결되지 않으므로 false.
...
class RetryPolicyTest {
@Test
void retriesOnRateLimitAndServerErrors() {
...
단계 2: 예외를 타입으로 받고, 상태 코드로 판단하기
공식 SDK의 예외는 타입별로 나누어져 있습니다. HTTP 응답이 있는 에러는 AnthropicServiceException (statusCode()를 가짐), 네트워크 단절은 AnthropicIoException입니다 (둘 다 com.anthropic.errors). 상태 코드를 RetryPolicy에 전달하여 재시도 여부를 결정합니다.
@Service
@RequiredArgsConstructor
@Slf4j
...
MAX_ATTEMPTS로 시도 상한을 설정하여 무한 루프를 방지합니다. 429의 경우 본래 retry-after 헤더를 따르는 것이 바람직하지만, 우선은 지수 백오프 (Exponential Backoff)부터 시작해도 충분할 것입니다.
주의할 점과 해결 방법
재시도를 직접 구현할 때 빠지기 쉬운 함정은 다음 4가지입니다.
무엇이든 재시도해 버림: 400・401을 재시도해도 해결되지 않습니다. isRetryable에서 429・5xx로 한정합니다. -
무한 루프: 상한 (MAX_ATTEMPTS)을 반드시 설정합니다. 초과하면 감싸서 던집니다. -
인터럽트 처리: InterruptedException을 무시하는 Thread.sleep의 인터럽트는 Thread.currentThread().interrupt()로 플래그를 복구한 뒤 예외를 발생시킵니다. -
판정 로직이 테스트하기 어려움: 재시도 판단을 RetryPolicy (순수 함수)로 분리하면, 네트워크 없이 실제 값 검증이 가능합니다.
요약
- 에러는 「재시도로 해결되는 것 (429・5xx・I/O)」과 「해결되지 않는 것 (4xx)」으로 나눈다
- 판단은
AnthropicServiceException.statusCode()를RetryPolicy
에 전달하여 결정한다 - 상한선(Upper bound)과 지수 백오프 (Exponential Backoff)로 안전하게 제어한다. 판정 로직은 순수 함수 (Pure Function)로 만들어 테스트로 견고하게 다진다.
여기서는 "실패에 강하게 만들기"의 최소 형태에 집중했지만, 사양 정의부터 구현·테스트·배포까지를 Claude Code와 일관되게 진행하는 흐름은 저서에 정리해 두었습니다. Spring Security / JPA / Flyway 및 운영 배포 (Railway)까지, AI와 대화하며 하나의 Web App을 완성하는 구성입니다.
📘 『Claude Code와 함께 만드는 Spring Boot 실전 개발 AI 일기 앱을 사양 정의부터 배포까지』
- Amazon (종이책): https://amzn.to/4gH5T35
- BOOTH (전자책·PDF): https://propagandist.booth.pm/items/8536937
- Google Play Books (전자책): https://play.google.com/store/books/details?id=p83tEQAAQBAJ
※ Amazon 링크에는 어필리에이트 링크가 포함되어 있습니다.
Discussion

AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기