요약
REST API는 HTTP 상태 코드를 올바르게 사용해야 합니다. 성공적인 GET 요청에는 200, 리소스 생성과 함께 성공적인 POST 요청에는 201, 성공적인 DELETE 요청에는 204, 클라이언트 오류에는 400, 인증 실패에는 401, 찾을 수 없을 때는 404, 서버 오류에는 500을 사용합니다. Modern PetstoreAPI는 적절한 의미론과 RFC 9457 오류 응답을 사용하여 모든 표준 HTTP 상태 코드를 구현합니다.
서론
귀하의 API가 모든 요청에 대해 200 OK를 반환합니다. 성공 시? 200 OK. 유효성 검사 오류? 본문에 오류 메시지와 함께 200 OK. 리소스를 찾을 수 없나요? `{"error": "not found"}`와 함께 200 OK. 인증 실패? 예상하셨겠지만 — 200 OK.
이것은 잘못된 방법입니다. HTTP 상태 코드는 그만한 이유로 존재합니다. 상태 코드는 응답 본문을 파싱하지 않고도 클라이언트에게 무슨 일이 일어났는지 알려줍니다. 캐시, 프록시, 모니터링 도구는 상태 코드에 의존합니다. 오류에 대해 200을 반환하면 전체 HTTP 생태계를 망가뜨리는 것입니다.
구형 Swagger Petstore는 상태 코드 오류를 범했습니다. 201을 반환해야 할 POST 요청에 200을 반환하고, 204를 반환해야 할 DELETE 작업에 200을 사용했으며, 중요한 오류 코드를 누락했습니다. Modern PetstoreAPI는 모든 엔드포인트에 걸쳐 적절한 HTTP 의미론을 구현하여 이를 수정합니다.
이 가이드에서는 REST API에 중요한 HTTP 상태 코드, 각 코드를 언제 사용해야 하는지, 그리고 Modern PetstoreAPI가 이를 어떻게 올바르게 구현하는지 알아볼 것입니다.
상태 코드 문제
많은 API는 상태 코드를 나중에 생각하는 것으로 취급합니다. 그 결과: 깨진 HTTP 의미론과 혼란스러운 클라이언트가 발생합니다.
"모든 경우에 200 OK" 안티 패턴
// 성공
GET /users/123
200 OK
{"id": 123, "name": "John"}
// 오류 (하지만 여전히 200!)
GET /users/999
200 OK
{"error": "User not found"}
// 유효성 검사 오류 (여전히 200!)
POST /users
200 OK
{"error": "Email is required"}
문제점:
- 클라이언트는 본문을 파싱하지 않고는 성공과 실패를 구별할 수 없습니다.
- HTTP 캐시는 오류 응답을 캐시합니다.
- 모니터링 도구는 잘못된 양성(false positives)을 보고합니다.
- 재시도 로직이 올바르게 작동하지 않습니다.
이런 현상이 발생하는 이유
개발자들이 200을 반환하는 이유는 다음과 같습니다.
- 다른 상태 코드를 모릅니다.
- 상태 코드가 선택 사항이라고 생각합니다.
- 클라이언트를 "망가뜨리는" 것을 피하고 싶어 합니다.
- 나쁜 예제(구형 Swagger Petstore와 같은)를 복사합니다.
REST API를 위한 필수 HTTP 상태 코드
모든 60개 이상의 HTTP 상태 코드를 알 필요는 없습니다. 다음 필수 코드에 집중하세요.
빠른 참조
성공 (2xx):
- 200 OK - 성공적인 GET, PUT, PATCH
- 201 Created - 리소스 생성과 함께 성공적인 POST
- 204 No Content - 성공적인 DELETE, 응답 본문 없는 PUT
클라이언트 오류 (4xx):
- 400 Bad Request - 잘못된 요청 형식 또는 유효성 검사 오류
- 401 Unauthorized - 누락되거나 유효하지 않은 인증
- 403 Forbidden - 인증되었으나 권한 없음
- 404 Not Found - 리소스가 존재하지 않음
- 409 Conflict - 리소스 충돌 (중복, 버전 불일치)
- 422 Unprocessable Entity - 유효한 형식이나 의미론적 오류
- 429 Too Many Requests - 비율 제한 초과
서버 오류 (5xx):
- 500 Internal Server Error - 예기치 않은 서버 오류
- 502 Bad Gateway - 상위 서비스 오류
- 503 Service Unavailable - 일시적인 서비스 중단
- 504 Gateway Timeout - 상위 서비스 시간 초과
성공 코드 (2xx)
성공 코드는 요청이 성공했음을 나타냅니다. 다른 코드는 다른 의미를 전달합니다.
200 OK
사용 사례: 데이터를 반환하는 성공적인 GET, PUT, PATCH 요청.
GET /pets/123
200 OK
{
"id": "019b4132-70aa-764f-b315-e2803d882a24",
"name": "Fluffy",
"species": "CAT"
}
사용하지 말아야 할 경우: 리소스를 생성하는 POST 요청 (201 사용), DELETE 요청 (204 사용).
201 Created
사용 사례: 새 리소스를 생성하는 성공적인 POST 요청.
POST /pets
201 Created
Location: https://petstoreapi.com/pets/019b4132-70aa-764f-b315-e2803d882a24
{
"id": "019b4132-70aa-764f-b315-e2803d882a24",
"name": "Fluffy",
"species": "CAT"
}
주요 사항:
- 새 리소스 URL을 포함하는
Location헤더를 포함합니다. - 응답 본문에 생성된 리소스를 반환합니다.
- 클라이언트는 단순히 업데이트된 것이 아니라 리소스가 생성되었음을 알게 됩니다.
Modern PetstoreAPI는 리소스를 생성하는 모든 POST 작업에 대해 201을 반환합니다.
204 No Content
사용 사례: 응답 본문이 없는 성공적인 DELETE, PUT 또는 PATCH 요청.
DELETE /pets/019b4132-70aa-764f-b315-e2803d882a24
204 No Content
주요 사항:
- 응답 본문 없음 (대역폭 절약)
- 성공을 나타냄
- DELETE 작업에 일반적
클라이언트 오류 코드 (4xx)
클라이언트 오류 코드는 클라이언트가 실수를 저질렀음을 나타냅니다. 요청은 수정 없이 재시도해서는 안 됩니다.
400 Bad Request
사용 사례: 잘못된 형식의 요청, 유효하지 않은 JSON, 누락된 필수 필드.
POST /pets
400 Bad Request
Content-Type: application/problem+json
{
"type": "https://petstoreapi.com/errors/validation-error",
"title": "Validation Error",
"status": 400,
"detail": "Request validation failed",
"invalid-params": [
{
"name": "name",
"reason": "Name is required"
}
]
}
Modern PetstoreAPI는 모든 오류 응답에 RFC 9457 형식을 사용합니다.
401 Unauthorized
사용 사례: 누락되거나 유효하지 않은 인증 자격 증명.
GET /pets
401 Unauthorized
WWW-Authenticate: Bearer realm="PetstoreAPI"
{
"type": "https://petstoreapi.com/errors/authentication-required",
"title": "Authentication Required",
"status": 401,
"detail": "Valid authentication credentials required"
}
주요 사항:
WWW-Authenticate헤더 포함- 클라이언트는 자격 증명 또는 토큰 갱신을 요청해야 합니다.
- 403 (권한 부여)와 혼동하지 마십시오.
403 Forbidden
사용 사례: 인증된 사용자가 권한이 없는 경우.
DELETE /pets/019b4132-70aa-764f-b315-e2803d882a24
403 Forbidden
{
"type": "https://petstoreapi.com/errors/insufficient-permissions",
"title": "Insufficient Permissions",
"status": 403,
"detail": "You don't have permission to delete this pet"
}
401과의 차이점:
- 401: "당신은 누구인가요?" (인증)
- 403: "나는 당신이 누구인지 알지만, 당신은 그것을 할 수 없습니다" (권한 부여)
404 Not Found
사용 사례: 리소스가 존재하지 않음.
GET /pets/nonexistent-id
404 Not Found
{
"type": "https://petstoreapi.com/errors/not-found",
"title": "Not Found",
"status": 404,
"detail": "Pet not found"
}
사용하지 말아야 할 경우: 권한 부여 실패 (403 사용), 유효성 검사 오류 (400 사용).
409 Conflict
사용 사례: 중복 또는 버전 불일치와 같은 리소스 충돌.
POST /pets
409 Conflict
{
"type": "https://petstoreapi.com/errors/duplicate-resource",
"title": "Duplicate Resource",
"status": 409,
"detail": "A pet with this microchip ID already exists"
}
422 Unprocessable Entity
사용 사례: 유효한 요청 형식이지만 의미론적 오류.
POST /pets
422 Unprocessable Entity
{
"type": "https://petstoreapi.com/errors/business-rule-violation",
"title": "Business Rule Violation",
"status": 422,
"detail": "Cannot adopt more than 5 pets per household"
}
400과의 차이점:
- 400: 잘못된 형식의 요청 (유효하지 않은 JSON, 잘못된 타입)
- 422: 잘 구성된 요청이지만 비즈니스 규칙을 위반함
429 Too Many Requests
사용 사례: 비율 제한 초과.
GET /pets
429 Too Many Requests
RateLimit-Limit: 100
RateLimit-Remaining: 0
RateLimit-Reset: 1678886400
{
"type": "https://petstoreapi.com/errors/rate-limit-exceeded",
"title": "Rate Limit Exceeded",
"status": 429,
"detail": "Rate limit of 100 requests per hour exceeded"
}
Modern PetstoreAPI는 IETF 비율 제한 헤더를 사용합니다.
서버 오류 코드 (5xx)
서버 오류 코드는 서버가 실패했음을 나타냅니다. 클라이언트는 이러한 요청을 재시도할 수 있습니다.
500 Internal Server Error
사용 사례: 예기치 않은 서버 오류.
GET /pets
500 Internal Server Error
{
"type": "https://petstoreapi.com/errors/internal-error",
"title": "Internal Server Error",
"status": 500,
"detail": "An unexpected error occurred"
}
포함하지 말아야 할 것: 프로덕션 환경에서 스택 트레이스, 내부 상세 정보, 데이터베이스 오류.
503 Service Unavailable
사용 사례: 일시적인 서비스 중단 (유지 보수, 과부하).
GET /pets
503 Service Unavailable
Retry-After: 3600
{
"type": "https://petstoreapi.com/errors/service-unavailable",
"title": "Service Unavailable",
"status": 503,
"detail": "Service temporarily unavailable for maintenance"
}
클라이언트에게 언제 재시도할지 알려주기 위해 Retry-After 헤더를 포함합니다.
Modern PetstoreAPI가 상태 코드를 사용하는 방법
Modern PetstoreAPI는 모든 엔드포인트에 걸쳐 적절한 HTTP 의미론을 구현합니다.
예시: 반려동물 관리
// 반려동물 목록 조회
GET /pets
200 OK
// 반려동물 생성
POST /pets
201 Created
Location: https://petstoreapi.com/pets/{id}
// 반려동물 조회
GET /pets/{id}
200 OK (찾음) 또는 404 Not Found
// 반려동물 업데이트
PUT /pets/{id}
200 OK (본문 포함) 또는 204 No Content
// 반려동물 삭제
DELETE /pets/{id}
204 No Content (성공) 또는 404 Not Found
오류 응답
모든 오류는 RFC 9457 형식을 사용합니다:
{
"type": "https://petstoreapi.com/errors/validation-error",
"title": "Validation Error",
"status": 400,
"detail": "Request validation failed",
"instance": "/pets",
"invalid-params": [
{
"name": "name",
"reason": "Name must be between 1 and 100 characters"
}
]
}
완전한 예시는 Modern PetstoreAPI 오류 처리 문서를 참조하십시오.
Apidog로 상태 코드 테스트하기
Apidog는 모든 시나리오에서 상태 코드 동작을 테스트하는 데 도움을 줍니다.
예상 상태 코드 정의
paths:
/pets:
post:
responses:
'201':
description: 반려동물 생성됨
'400':
description: 유효성 검사 오류
'401':
description: 인증 필요
'429':
description: 비율 제한 초과
모든 시나리오 테스트
다음과 같은 테스트 케이스를 생성합니다:
- 성공 시나리오 (200, 201, 204)
- 유효성 검사 오류 (400, 422)
- 인증/권한 부여 (401, 403)
- 찾을 수 없음 (404)
- 비율 제한 (429)
- 서버 오류 (500, 503)
자동화된 테스트
// Apidog 테스트 스크립트
pm.test("성공적인 생성에 대해 201을 반환합니다", () => {
pm.response.to.have.status(201);
pm.response.to.have.header("Location");
});
pm.test("필수 필드 누락에 대해 400을 반환합니다", () => {
pm.response.to.have.status(400);
pm.expect(pm.response.json().type).to.include("validation-error");
});
피해야 할 흔한 실수
실수 1: POST 요청에 200 사용
// 잘못됨
POST /pets
200 OK
// 올바름
POST /pets
201 Created
Location: https://petstoreapi.com/pets/{id}
실수 2: DELETE 요청에 200 사용
// 잘못됨
DELETE /pets/{id}
200 OK
{"message": "Deleted successfully"}
// 올바름
DELETE /pets/{id}
204 No Content
실수 3: 401과 403 혼동
// 잘못됨: 사용자는 인증되었지만 권한이 없음
401 Unauthorized
// 올바름
403 Forbidden
실수 4: 클라이언트 오류에 500 사용
// 잘못됨: 유효성 검사 오류가 500을 반환함
POST /pets
500 Internal Server Error
// 올바름
POST /pets
400 Bad Request
결론
HTTP 상태 코드는 선택 사항이 아닙니다. HTTP 사양의 일부이며 올바른 REST API를 구축하는 데 필수적입니다.
올바른 상태 코드를 사용하세요:
- 성공적인 읽기에는 200
- 성공적인 생성에는 201
- 성공적인 삭제에는 204
- 클라이언트 오류에는 400
- 인증에는 401
- 찾을 수 없을 때는 404
- 서버 오류에는 500
Modern PetstoreAPI는 모든 엔드포인트에 걸쳐 올바른 상태 코드 사용법을 보여줍니다. 적절한 구현을 위해 REST API 문서를 살펴보세요.
Apidog로 상태 코드를 테스트하여 API가 HTTP 표준을 따르는지 확인하세요.
자주 묻는 질문 (FAQ)
성공적인 DELETE 요청에는 200을 사용해야 하나요, 아니면 204를 사용해야 하나요?
204 No Content를 사용하세요. 응답 본문 없이 성공을 나타내어 대역폭을 절약합니다. 삭제된 리소스에 대한 정보를 반환해야 하는 경우에만 200을 사용하세요.
400과 422의 차이점은 무엇인가요?
400은 요청 형식이 잘못되었음(유효하지 않은 JSON, 잘못된 타입)을 의미합니다. 422는 요청이 잘 구성되었지만 비즈니스 규칙을 위반함을 의미합니다.
401과 403은 언제 사용해야 하나요?
401은 "자신을 인증하세요"(누락되거나 유효하지 않은 자격 증명)를 의미합니다. 403은 "인증되었지만 권한이 없습니다"(불충분한 권한)를 의미합니다.
사용자가 접근할 수 없는 리소스에 대해 404 또는 403을 반환해야 하나요?
리소스가 존재하지만 사용자에게 권한이 없는 경우 403을 반환하세요. 권한 없는 사용자에게 리소스의 존재를 숨기려면 404를 반환하세요.
모든 상태 코드 시나리오를 어떻게 테스트하나요?
Apidog를 사용하여 성공, 유효성 검사 오류, 인증 실패, 찾을 수 없음, 서버 오류에 대한 테스트 케이스를 생성하세요. CI/CD에서 자동화된 테스트를 실행하세요.
비율 제한에는 어떤 상태 코드를 사용해야 하나요?
RateLimit-* 헤더와 함께 429 Too Many Requests를 사용하세요. 클라이언트에게 언제 재시도할 수 있는지 알려주기 위해 Retry-After를 포함하세요.
모든 서버 오류에 500을 사용해야 하나요?
예기치 않은 오류에는 500을 사용하세요. 상위 서비스 실패에는 502, 일시적인 서비스 중단에는 503, 시간 초과에는 504를 사용하세요.
Modern PetstoreAPI는 오류를 어떻게 처리하나요?
모든 오류는 적절한 상태 코드와 함께 RFC 9457 형식을 사용합니다. 예시는 오류 처리 문서를 참조하십시오.
