500에서 599까지의 상태 코드는 서버가 유효한 것으로 보이는 요청을 수행하지 못했음을 의미합니다. 사용자의 요청은 문제가 되지 않으므로 다시 보내면 작동할 수 있습니다. 다시 보내야 하는지 여부는 상태 코드 및 요청이 무엇을 하는지에 따라 달라집니다. 정의는 RFC 9110 섹션 15.6을 참조하세요.
각 상태 코드의 의미
| 상태 코드 | 의미 | 다시 시도하시겠습니까? |
|---|---|---|
500 Internal Server Error |
서버가 예상하지 못한 상태가 발생했습니다. | API에 따라 달라집니다. Claude API와 같은 일부 API는 지수 백오프를 사용하여 500을 다시 시도하라고 지시합니다. API의 문서를 확인합니다. |
502 Bad Gateway |
게이트웨이 또는 프록시가 뒤에 있는 서버로부터 잘못된 응답을 받았습니다. | 예, 요청을 반복해도 안전한 경우입니다 |
503 Service Unavailable |
서버는 유지 관리를 위해 일시적으로 오버로드되거나 다운되며 일정 시간 후에 복구됩니다. 서버에서 Retry-After 헤더를 보낼 수 있습니다. |
예, 서버가 Retry-After 시간을 보낸 경우 그 이후입니다 |
504 Gateway Timeout |
게이트웨이 또는 프록시가 뒤에 있는 서버에서 시간 내 응답을 받지 못했습니다. | 예, 요청을 반복해도 안전한 경우입니다. 게이트웨이가 대기를 포기했으므로 서버가 작업을 수행했는지 여부를 알 수 없습니다. |
다시 시도해도 안전한 요청
RFC 9110은 동일한 요청을 여러 번 보내는 것이 한 번 보내는 것과 동일한 효과를 낼 때 해당 메서드를 멱등적이라고 합니다.
GET, HEAD, OPTIONS, TRACE, PUT, 및 DELETE는 idempotent입니다.
POST와 PATCH는 그렇지 않습니다. RFC에 따르면 클라이언트는 요청이 어쨌든 멱등하다는 것을 알거나 서버가 원래 요청을 적용하지 않았다고 알 수 있는 경우가 아니면 비멱등 메서드를 사용한 요청을 자동으로 다시 시도해서는 안 됩니다. 자세한 내용은 Idempotent 메서드를 참조하세요.
502 또는 504 후에 POST를 재시도하면 두 번째 주문을 만들거나 두 번째 전자 메일을 보낼 수 있습니다. 일부 재시도 라이브러리는 기본적으로 모든 메서드를 다시 시도합니다. 예를 들어 .NET 표준 복원력 처리기는 POST를 호출하지 않으면 options.Retry.DisableForUnsafeHttpMethods()를 다시 시도합니다. 자세한 내용은 복원력 있는 HTTP 앱 빌드를 참조하세요.
503 응답의 Retry-After
503에는 Retry-After 헤더가 포함될 수 있습니다. 해당 값은 몇 초(예: 120초) 또는 HTTP 날짜(예: Fri, 31 Dec 1999 23:59:59 GMT)입니다. 코드는 둘 다 처리해야 합니다. 자세한 내용은 Retry-After를 참조하세요.
계속 실패하는 API 호출을 중지하세요
재시도는 일시적인 오류에 도움이 됩니다. API가 몇 분 동안 다운되면 모든 요청을 다시 시도하면 이미 어려움을 겪고 있는 서버에 부하가 추가되고 사용자는 재시도할 때마다 실패하기를 기다리게 됩니다. 서킷 브레이커는 오류를 추적하고, 실패가 너무 많아지면 잠시 동안 API 호출을 중지하고 빠르게 실패합니다. 이 시간 후에는 API가 복구되었는지 여부를 확인하기 위해 몇 개의 요청을 통과시킵니다. 자세한 내용은 회로 차단기 패턴을 참조하세요. .NET 표준 복원력 처리기에는 100개 이상의 요청이 있는 30초 창에서 요청의 10% 이상이 실패하는 경우 5초 동안 열리는 회로 차단기가 포함됩니다.
5xx 오류를 처리하는 방법
- idempotent 요청에 대해서만 502, 503 및 504를 다시 시도합니다.
PATCH및POST의 경우, API 문서에서 이를 안전하게 재시도할 수 있는 방법을 명시한 경우에만 재시도하세요. - 다시 시도하기 전에 기다리세요. 서버에서 이를 보낼 때
Retry-After를 사용합니다. 그렇지 않으면 무작위 지터를 적용한 지수 백오프를 사용하고 몇 번 시도한 후 중지합니다. - 500 오류에 대한 API 문서를 읽습니다. API가 안전하다고 말하는 경우에만 다시 시도합니다.
- 계속 실패하는 API를 호출하지 마세요. API가 복구되는 동안 앱이 빠르게 실패할 수 있도록 회로 차단기를 사용합니다.
- 사용자에게 무슨 일이 있었는지 알려주세요. 일반 오류 또는 스택 추적 대신 "서비스에 문제가 있습니다. 나중에 다시 시도하세요"를 표시합니다.
const RETRYABLE_STATUS = new Set([502, 503, 504]);
const IDEMPOTENT_METHODS = new Set(["GET", "HEAD", "OPTIONS", "TRACE", "PUT", "DELETE"]);
function retryAfterMs(response) {
const value = response.headers.get("retry-after");
if (!value) return null;
const seconds = Number(value);
if (Number.isFinite(seconds)) return seconds * 1000;
const date = Date.parse(value);
return Number.isNaN(date) ? null : Math.max(0, date - Date.now());
}
export async function fetchWithRetry(url, options = {}, maxRetries = 3) {
const method = (options.method ?? "GET").toUpperCase();
const canRetry = IDEMPOTENT_METHODS.has(method);
for (let attempt = 0; ; attempt++) {
const response = await fetch(url, options);
if (!RETRYABLE_STATUS.has(response.status) || !canRetry || attempt === maxRetries) {
return response;
}
await response.body?.cancel();
const backoff = 2 ** attempt * 1000 + Math.random() * 1000;
const wait = retryAfterMs(response) ?? backoff;
await new Promise((resolve) => setTimeout(resolve, wait));
}
}
앱에서 5xx 오류를 처리하는지 테스트하는 방법
개발하는 동안 5xx가 거의 표시되지 않으며 원할 때 API가 실패하게 만들 수 없습니다. 테스트 방법은 여러분이 사용자보다 먼저 버그를 찾을지 여부를 결정합니다.
| Approach | 찾은 내용 | 당신이 놓친 것 |
|---|---|---|
| 프로덕션을 기다리세요 | 실제 장애 | 사용자가 그것을 누를 때까지 모든 것 |
| 테스트에서 API를 모의하거나 코딩 에이전트가 모의 개체를 작성하도록 허용하세요. | 사용자의 오류 분기 실행 여부 | 사용 중인 실제 HTTP 클라이언트와 재시도 라이브러리, 그리고 실제로 몇 번 재시도하는지. 또한 mock에 도달하려면 앱에 테스트 전용 스위치가 필요합니다. |
| 실제 API를 호출하고 실패할 때까지 기다리세요. | 실제 동작 | 원할 때 API가 실패하게 만들 수 없습니다. |
| 앱의 실제 트래픽을 가로채고 선택한 비율로 5xx 오류를 반환합니다. | 실제 HTTP 클라이언트, 재시도 라이브러리 및 서킷 브레이커 | 앱에서는 아무것도 변경되지 않으므로 코드가 격리된 상태로 테스트되지 않습니다. 그런 용도는 단위 테스트에 맡기세요. |
앱에서 사용해 보기
Dev Proxy는 앱의 요청을 가로채고 GenericRandomErrorPlugin을 사용하여 그중 일부를 정의한 오류로 실패시킵니다. 앱은 실제 URL을 계속 호출합니다. 구성 파일에 플러그인을 추가하고 해당 errorsFile이(가) 5xx 오류가 있는 파일을 가리키도록 합니다. 이 예제에서는 https://api.contoso.com를 사용합니다. 이를 앱이 호출하는 API의 URL로 바꾸세요.
파일: server-errors.json
{
"$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/genericrandomerrorplugin.errorsfile.schema.json",
"errors": [
{
"request": {
"url": "https://api.contoso.com/*"
},
"responses": [
{ "statusCode": 500 },
{ "statusCode": 502 },
{
"statusCode": 503,
"headers": [
{ "name": "Retry-After", "value": "10" }
]
},
{ "statusCode": 504 }
]
}
]
}
기본적으로 플러그인은 요청의 50%가 실패합니다. 개발자 프록시 출력에서 앱이 GET 요청을 다시 시도하고 각 POST은 한 번만 보내는지 확인합니다. 개발자 프록시는 앱이 503 응답 시 Retry-After를 기다리는지 여부를 확인하지 않으므로 요청 시간을 직접 비교하세요. 그런 다음 --failure-rate 100로 Dev Proxy를 시작하여 API가 계속 실패할 때 앱이 수행하는 작업을 확인합니다. 자세한 내용은 변경 요청 실패율을 참조하세요. Dev Proxy를 설치하려면 Dev Proxy 설정을 참조하세요.
다음 단계
또한, 다음을 참조하세요.
Dev Proxy