REST API 상태 코드 올바르게 고르기

REST API에서 200/201/204, 400/401/403/404/409/422/429, 500/503을 언제 써야 하는지와 일관성·멱등성 키 설계를 설명합니다.

REST API를 설계할 때 상태 코드는 '문서를 읽지 않고도 응답을 이해하게 해 주는 계약'입니다. 잘 고른 코드는 클라이언트가 재시도할지, 사용자에게 무엇을 보여줄지, 캐시할지를 정확히 판단하게 해 줍니다. 반대로 무엇이든 200으로 내려보내고 본문에 성공 여부 플래그를 숨기는 API는 클라이언트가 매번 본문을 파싱해야 하고, HTTP 인프라(캐시·프록시·모니터링)의 이점을 잃습니다.

성공 응답: 200 / 201 / 204

POST /v1/users HTTP/1.1
Content-Type: application/json

{"email":"a@example.com"}

HTTP/1.1 201 Created
Location: /v1/users/u_92311
Content-Type: application/json

{"id":"u_92311","email":"a@example.com"}

클라이언트 오류: 400 / 401 / 403 / 404 / 409 / 422 / 429

400과 422의 구분은 실무에서 자주 헷갈립니다. 관례적으로 400은 '서버가 요청을 파싱조차 못 함'(깨진 본문)이고, 422는 '파싱은 됐지만 필드 값이 검증 규칙을 위반'하는 경우입니다. 팀 안에서 규칙을 한 번 정하고 API 전체에서 일관되게 지키는 것이 개별 코드 선택보다 중요합니다.

서버 오류와 재시도: 500 / 503

500 Internal Server Error는 예상치 못한 서버 예외를 가리키는 포괄 코드입니다. 503 Service Unavailable은 과부하나 점검 등으로 일시적으로 처리할 수 없음을 뜻하며, Retry-After로 복구 예상 시각을 알릴 수 있습니다. 5xx와 429는 클라이언트가 지수 백오프(exponential backoff)로 재시도해도 되는 대표적 코드입니다.

멱등성 키로 안전하게 재시도하기

네트워크가 불안정하면 POST로 결제나 주문을 만들 때 '요청은 도달했는데 응답을 못 받은' 상황이 생깁니다. 이때 그냥 재시도하면 중복 생성 위험이 있습니다. 해결책은 클라이언트가 Idempotency-Key 헤더에 고유 값을 실어 보내고, 서버가 같은 키의 요청을 한 번만 실행한 뒤 이후 동일 키 요청에는 저장해 둔 원래 응답을 그대로 돌려주는 것입니다.

POST /v1/charges HTTP/1.1
Idempotency-Key: 3f9c1b7a-2e5d-4a11-9c8e-77b0a1d2e4f6
Content-Type: application/json

{"amount":5000,"currency":"krw"}

일관성은 상태 코드 설계의 최종 목표입니다. 같은 종류의 상황에는 항상 같은 코드를 쓰고, 오류 본문 형식(예: error 객체 안에 code·message 필드를 두는 형태)을 표준화하며, 문서에 코드별 의미를 명시하세요. 그러면 클라이언트 개발자가 방어 코드를 예측 가능하게 작성할 수 있습니다.