서비스 운영 ·
API 호출 제한에서 재시도와 중복 처리를 안전하게 나누는 법
먼저 답부터
API 호출 제한을 만나면 즉시 같은 요청을 반복하지 말고 응답 상태와 제한 헤더를 기록한 뒤 기다리는 시간을 점차 늘려 재시도해야 합니다. 읽기 요청과 결제·메일·생성 요청을 구분하고, 변경 작업에는 고유한 작업 식별자와 결과 조회를 둬 중복 처리를 막아야 합니다.
AI나 결제 서비스를 연결하면 어느 순간 요청이 너무 많다는 응답을 만납니다. 이때 버튼을 여러 번 누르거나 코드를 즉시 반복하면 잠깐의 혼잡이 더 큰 장애가 되고, 결제나 메일처럼 되돌리기 어려운 작업은 두 번 처리될 수도 있습니다.
API 호출 제한은 외부 서비스가 일정 시간에 받을 요청량을 정해 둔 경계입니다. 붐비는 은행 창구가 번호표를 나눠 주는 것과 비슷합니다. 번호표가 늦다고 창구 문을 계속 두드리는 대신 언제 다시 부를지 확인하고, 이미 접수된 업무인지 영수증 번호로 찾아야 합니다.
호출 제한 응답에서 먼저 사실을 읽는다
HTTP 429는 너무 많은 요청을 보냈다는 뜻으로 널리 쓰이는 상태 코드입니다. 다만 모든 서비스가 같은 제한 단위와 초기화 시간을 쓰는 것은 아닙니다. 분당 요청 수, 분당 처리 글자 수, 프로젝트별 한도처럼 기준이 다를 수 있으므로 이용 중인 API의 현재 문서를 확인해야 합니다.
OpenAI API 공식 문서는 응답 헤더에서 요청과 토큰의 한도, 남은 양, 초기화 시점을 확인할 수 있다고 설명합니다. header는 응답 본문과 함께 오는 부가 설명입니다. 비밀키나 사용자 입력 원문 대신 상태 코드, 요청 ID, 제한 관련 헤더, 발생 시간을 운영 기록에 남기면 어느 요청이 막혔는지 좁힐 수 있습니다.
재시도는 실패한 즉시 같은 일을 반복하는 것이 아니다
retry는 실패한 요청을 다시 시도하는 행동입니다. 일시적인 혼잡이나 네트워크 끊김에는 도움이 되지만, 잘못된 비밀키나 권한 부족처럼 기다려도 바뀌지 않는 오류에는 쓸모가 없습니다. 먼저 다시 시도해도 되는 오류인지 나눠야 합니다.
exponential backoff는 재시도할수록 기다리는 시간을 늘리는 방식입니다. 예를 들어 1초, 2초, 4초처럼 간격을 넓히되 실제 숫자는 서비스 지침과 사용자 대기 시간을 보고 정합니다. 여러 사용자가 정확히 동시에 다시 몰리지 않게 작은 무작위 간격을 더하기도 합니다.
첫 실패 → 응답과 요청 ID 기록
429 확인 → 안내된 초기화 시점까지 기다림
다시 실패 → 더 길게 기다림
최대 횟수 → 자동 재시도 중단 후 사용자에게 상태 안내
무한 반복은 호출 제한을 회복시키지 못하고 비용과 로그만 늘릴 수 있습니다. 최대 시도 횟수와 전체 대기 시간을 먼저 정하고, 넘으면 대기열에 남기거나 사람이 확인하게 합니다.
같은 요청을 다시 보내도 결과가 하나여야 한다
idempotency는 같은 변경 요청을 여러 번 보내도 의도한 최종 결과가 한 번 보낸 것과 같게 만드는 성질입니다. 엘리베이터 호출 버튼을 여러 번 눌러도 엘리베이터를 여러 대 주문하지 않는 것과 비슷합니다.
MDN은 GET, PUT, DELETE 같은 일부 HTTP 메서드가 정의상 멱등하다고 설명하지만 실제 서버 구현이 그 의미를 지켜야 한다고 덧붙입니다. POST로 결제나 생성 요청을 보낼 때는 메서드 이름만 믿을 수 없습니다. 클라이언트가 만든 고유 작업 ID를 서버가 저장하고, 같은 ID가 다시 오면 새 작업을 만들지 않고 앞선 결과를 돌려주는 구조가 필요합니다.
읽기와 변경 요청의 위험을 따로 본다
상품 목록을 읽는 요청은 반복돼도 서버 자료를 직접 바꾸지 않는 경우가 많습니다. 반면 결제 승인, 이메일 발송, 파일 삭제, 이미지 생성은 한 번 더 실행될 때 실제 결과가 늘거나 사라질 수 있습니다. 재시도 규칙을 한 덩어리로 만들지 않는 이유입니다.
- 읽기 요청은 짧은 재시도와 캐시 가능성을 검토합니다.
- 생성 요청은 고유 작업 ID와 결과 조회 주소를 둡니다.
- 결제 요청은 결제사의 공식 중복 방지 기능과 서버 거래 기록을 사용합니다.
- 이메일은 발송 상태를 저장해 같은 알림이 반복되지 않게 합니다.
- 삭제는 자동 재시도 전에 대상과 현재 상태를 다시 확인합니다.
- 시간 초과가 나도 서버가 요청을 받았을 가능성을 남겨 둡니다.
화면에는 실패보다 다음 행동을 보여 준다
사용자에게 “429”만 보여 주면 무엇을 해야 할지 알 수 없습니다. “요청이 잠시 몰렸습니다. 30초 뒤 다시 확인해 주세요”처럼 현재 상태와 다음 행동을 설명합니다. 실제 초기화 시점을 모르면 근거 없는 시간을 만들지 말고 작업이 접수됐는지 확인할 경로를 제공합니다.
버튼을 비활성화하는 것은 반복 클릭을 줄이는 화면 장치일 뿐 서버의 중복 방지 장치가 아닙니다. 새로고침이나 다른 기기 요청도 같은 작업인지 서버에서 판단해야 합니다. 완료 여부를 모르는 상태에서 “다시 결제” 버튼을 먼저 보여 주지 않습니다.
재시도 표를 만들면 AI 요청도 구체해진다
AI에게 “오류 나면 재시도해 줘”라고만 말하면 모든 실패를 같은 방식으로 반복할 수 있습니다. 다음처럼 상태별 행동을 먼저 적습니다.
429: 서버 안내 시간 확인, 최대 3회 대기 재시도
500~503: 짧게 대기 후 최대 2회, 계속 실패하면 중단
400·401·403: 자동 재시도 금지, 입력과 권한 확인
결제 POST: 작업 ID로 기존 결과 조회 후에만 새 요청 판단
숫자는 설명용 예시입니다. 사용 중인 제공사의 현재 문서, 서비스 중요도, 처리 비용에 맞춰 정해야 합니다. 요청 ID를 로그에 남기되 API 키, 카드 정보, 사용자 원문은 함께 기록하지 않습니다.
오늘 바로 해볼 작은 실습
실제 API를 호출하지 않고 종이에 재시도 판단표를 만듭니다.
- 내 서비스의 읽기 요청 한 개와 변경 요청 한 개를 적습니다.
- 각 요청이 두 번 처리됐을 때 생길 결과를 적습니다.
- 429, 401, 500 세 상태에서 자동 재시도 여부를 나눕니다.
- 변경 요청에 붙일 작업 ID 이름을 하나 정합니다.
- 사용자가 완료 여부를 확인할 화면이나 주소를 적습니다.
- 최대 재시도 횟수와 그 뒤의 중단 문장을 씁니다.
모르는 명령이나 예제 코드를 운영 서버에서 실행하지 마세요. 비밀키를 브라우저, 문서, 로그, AI 채팅에 붙이지 않고 결제·발송·삭제는 실제 결과를 조회하기 전 다시 요청하지 않습니다. 제공사의 재시도 보장과 작업 ID 저장 방식까지 설명할 수 없으면 자동 반복을 켜지 않는 것이 안전합니다.
확인한 자료
- API Overview — OpenAI Developers
- Idempotent — MDN Web Docs
함께 읽기
- 오류와 로그를 진료 기록처럼 읽고 원인을 좁히는 순서
디버깅은 오류 문장을 지우는 일이 아니라 같은 문제를 다시 만들 수 있는 조건을 기록하고 첫 실패 지점을 찾는 과정입니다. 화면 증상, 입력, 시간, 파일과 줄 번호, 기대 결과를 분리해 적은 뒤 한 번에 한 가지만 바꾸면 로그의 결과와 실제 원인을 혼동할 가능성이 줄어듭니다.
- 저장과 빌드와 배포를 포장 배송 공개처럼 구분하기
저장은 작업 파일을 남기는 일, 빌드는 소스 코드를 서비스용 결과물로 만드는 일, 배포는 그 결과물을 서버에 올리는 일입니다. 배포 성공 표시만으로 실제 공개 주소가 새 결과물을 제공한다고 단정할 수 없으므로 도메인, 번들, 핵심 기능과 메타데이터를 공개 환경에서 다시 확인해야 합니다.
- GitHub Actions로 커밋마다 테스트가 도는 흐름 이해하기
GitHub Actions의 CI는 push나 pull request 같은 사건이 생기면 별도 실행 컴퓨터인 러너에서 설치와 테스트 단계를 자동 수행하는 방식입니다. 워크플로 파일의 실행 조건, 권한, 비밀값 사용, 통과한 정확한 커밋을 확인해야 하며 초록 표시가 실제 배포나 모든 기능의 정상 동작까지 보장하지는 않습니다.