백엔드
웹 API
HTTP 상태 코드 정리: 401·403부터 409·422, 재시도 기준까지

HTTP 상태 코드 정리: 401·403부터 409·422, 재시도 기준까지

들어가며

API 가 실패한 요청에 모두 400 을 돌려준다고 해 보겠습니다. 클라이언트는 주소를 잘못 쓴 건지, 로그인이 풀린 건지, 잠깐 기다렸다 다시 보내면 되는 건지 알 수 없습니다. 결국 응답 본문의 문장을 읽어 분기하는 코드가 생깁니다.

상태 코드는 결과를 알리는 동시에 클라이언트가 다음에 할 일을 알려 주는 신호입니다. 이번 글에서는 HTTP 메서드의 의미를 짧게 짚고, 자주 헷갈리는 401·403, 400·422, 409, 5xx 와 재시도 기준을 정리합니다.


1. 메서드는 의도, 상태 코드는 결과

요청의 메서드는 무엇을 하려는지를 나타냅니다. GET 은 조회, POST 는 생성이나 처리 실행, PUT 은 자원 전체 교체, PATCH 는 일부 수정, DELETE 는 삭제입니다.

PUT 과 PATCH 는 자주 비교됩니다. PUT 은 보낸 본문으로 자원을 통째로 바꾸므로 빠진 필드는 지워질 수 있고, 같은 요청을 여러 번 보내도 결과가 같은 멱등 메서드입니다. PATCH 는 바꿀 부분만 보내며, "1 증가" 같은 내용이면 멱등이 아닙니다.

응답의 상태 코드는 그 의도가 어떻게 끝났는지를 세 자리 숫자로 알립니다. 첫 자리가 큰 갈래를 정합니다.

2. 다섯 갈래의 상태 코드

1xx 는 처리 중이라는 정보성 응답이라 API 에서 직접 다룰 일은 드뭅니다. 실제로 자주 쓰는 것은 나머지 네 갈래입니다.

상태 코드는 점원의 대답을 첫 마디로 먼저 나눈 분류표예요.

상태 코드 세 자리 숫자
2xx 성공
200 OK
201 Created
3xx 추가 조치
304 캐시 사용
4xx 클라이언트
401 인증 실패
403 권한 없음
404 자원 없음
409 상태 충돌
422 검증 실패
5xx 서버
500 서버 예외
503 일시 불가
첫 자리만 봐도 성공·추가 조치·클라이언트 문제·서버 문제가 갈린다. 세부 코드는 그 안에서 다음 행동을 정해 준다.

2xx 는 성공입니다. 조회는 200 OK, 생성은 201 Created 로 답하고 새 자원 주소를 Location 헤더에 담습니다. 돌려줄 본문이 없으면 204 No Content 를 씁니다.

3xx 는 클라이언트가 추가로 해야 할 일이 있다는 뜻입니다. 301 은 영구 이동, 302 는 임시 이동이고, 304 Not Modified 는 바뀐 게 없으니 가지고 있는 캐시를 쓰라는 뜻입니다.

4xx 는 클라이언트가 고쳐야 하는 문제, 5xx 는 서버 쪽 문제입니다. 같은 요청을 그대로 다시 보내서 나아질 가능성이 있는 것은 5xx 쪽입니다.

3. 401 과 403

이름 때문에 가장 많이 헷갈리는 쌍입니다. 401 Unauthorized 는 이름과 달리 인증 실패, 즉 "누구인지 모른다" 입니다. 403 Forbidden 은 누구인지는 알지만 권한이 없다는 뜻입니다.

401은 "누구세요?", 403은 "누군지는 알지만 들어오시면 안 돼요"예요.

401 인증 실패재인증
📱클라이언트앱·브라우저🖥️API서버① GET 토큰 없음② 401 인증 요구로그인·토큰 갱신③ GET 토큰 포함④ 200 OK

누구인지 밝히면 풀린다. 응답에 `WWW-Authenticate` 로 인증 방식을 알린다.

403 권한 없음재시도 무의미
📱클라이언트앱·브라우저🖥️API서버① GET 남의 자원인증 OK · 권한 없음② 403 Forbidden

다시 로그인해도 같다. 권한을 받아야 풀린다.

401 은 다시 인증하면 풀리지만 403 은 다시 로그인해도 결과가 같다. 존재 자체를 숨기려면 403 대신 404 를 쓰기도 한다.

401 을 받은 클라이언트는 토큰을 갱신하거나 다시 로그인한 뒤 재요청하면 됩니다. 명세상 401 응답에는 어떤 방식으로 인증해야 하는지 알리는 WWW-Authenticate 헤더를 함께 보냅니다. 403 은 다시 로그인해도 결과가 같으므로 권한을 요청하거나 포기해야 합니다.

남의 자원에 403 대신 404 를 주는 서비스도 많습니다. 403 은 "그런 자원이 있긴 하다" 는 사실을 드러내기 때문에, ID 를 하나씩 대 보며 존재 여부를 캐는 열거 공격에 단서가 됩니다.

4. 4xx 를 이유별로 나누기

같은 거절이라도 이유가 다르면 클라이언트가 할 일이 달라집니다.

4xx 세분은 점원이 "안 돼요" 대신 안 되는 이유를 말해 주는 거예요.

전부 400다음 행동 모름
  • 깨진 JSON → 400형식 오류
  • 잘못된 값 → 400입력 오류인지 모름
  • 이름 중복 → 400재조회해도 되는지 모름
  • 없는 ID → 400주소 문제인지 모름
이유별 코드바로 분기
  • 깨진 JSON → 400형식 수정
  • 잘못된 값 → 422입력값 수정
  • 이름 중복 → 409상태 재조회
  • 없는 ID → 404주소·ID 확인
코드를 나누면 클라이언트가 본문을 읽지 않고도 다음 행동을 정한다. 대신 서버는 실패 원인을 끝까지 구분해 응답해야 한다.
POST /projects        (JSON 문법이 깨짐)
→ 400 Bad Request

POST /projects        {"name": 123}
→ 422 Unprocessable Content

POST /projects        {"name": "alpha"}   # 이미 있음
→ 409 Conflict

DELETE /health
→ 405 Method Not Allowed
Allow: GET

GET /projects/999
→ 404 Not Found

400 은 요청 자체를 해석할 수 없을 때, 422 는 해석은 되지만 값이 규칙에 맞지 않을 때입니다. FastAPI 처럼 본문 검증 실패를 기본으로 422 로 돌려주는 프레임워크도 있습니다. 409 는 요청은 멀쩡하지만 현재 자원 상태와 충돌할 때라, 클라이언트는 상태를 다시 조회한 뒤 판단할 수 있습니다.

405 는 자원은 있지만 그 메서드를 받지 않을 때이며, Allow 헤더로 허용 메서드를 알려야 합니다. 화면 분기처럼 세밀한 처리는 상태 코드만으로는 부족하므로, 본문에 "code": "NAME_TAKEN" 같은 오류 코드를 함께 두는 편이 좋습니다.

5. 5xx 와 재시도

500 Internal Server Error 는 서버 코드의 예외처럼 예상하지 못한 실패입니다. 503 Service Unavailable 은 과부하나 점검으로 잠시 처리할 수 없다는 뜻이고, 429 Too Many Requests 는 요청 한도를 넘었다는 뜻입니다.

자동 재시도는 GET·PUT·DELETE 같은 멱등 메서드에, 503·429 처럼 일시적인 응답일 때만 겁니다. 서버가 Retry-After 헤더를 주면 그 시간만큼 기다립니다. POST 를 그대로 재시도하면 같은 자원이 두 번 생길 수 있으므로 멱등 키 같은 장치가 먼저 있어야 합니다.

한눈에 비교

코드뜻클라이언트의 다음 행동
400요청을 해석할 수 없음요청 형식 수정
401인증 실패토큰 갱신·재로그인 후 재요청
403권한 없음권한 요청, 재시도 의미 없음
404자원 없음주소·ID 확인
405메서드 불허Allow 헤더의 메서드로 변경
409현재 상태와 충돌상태 재조회 후 판단
422내용 검증 실패입력값 수정
429·503일시적 거절Retry-After 만큼 기다린 뒤 재시도

헷갈리기 쉬운 점

  • 401 을 "권한 없음" 으로 읽는 실수. 401 은 인증 실패이고, 권한 없음은 403 입니다.
  • 오류는 400 이나 500 하나로 충분하다는 오해. 409 를 400 으로 주면 클라이언트는 상태를 확인하고 다시 시도해도 되는지 알 수 없습니다.
  • 실패한 요청은 무조건 재시도해도 된다는 오해. 4xx 는 같은 요청을 다시 보내도 결과가 같고, POST 는 중복 생성될 수 있습니다.

면접에서 자주 받는 질문

400 과 422 는 어떻게 가르나요?

400 은 JSON 문법이 깨진 것처럼 요청 자체를 해석할 수 없을 때입니다. 422 는 해석은 되지만 문자열 자리에 숫자가 오는 등 값이 규칙에 맞지 않을 때입니다. 둘을 나누면 클라이언트가 "형식 오류" 와 "입력값 오류" 를 다르게 안내할 수 있습니다.

권한 없는 자원에 403 대신 404 를 주는 이유는요?

403 은 자원이 존재한다는 사실을 드러냅니다. ID 를 바꿔 가며 요청하면 어떤 자원이 있는지 알아낼 수 있으므로, 남의 자원은 없는 것처럼 404 로 답해 존재 여부를 숨깁니다.

301, 302, 304 는 각각 언제 쓰나요?

301 은 주소가 영구히 바뀌었을 때라 브라우저와 검색 엔진이 새 주소를 기억합니다. 302 는 임시 이동이라 다음에도 원래 주소로 요청합니다. 304 는 조건부 요청에서 자원이 바뀌지 않았으니 캐시를 쓰라는 응답이며 본문이 없습니다.

정리

  • 메서드는 요청의 의도, 상태 코드는 결과이며 첫 자리가 성공·추가 조치·클라이언트 문제·서버 문제를 가릅니다.
  • 401 은 인증 실패, 403 은 권한 없음이고, 4xx 는 이유별로 나눠야 클라이언트가 다음 행동을 정할 수 있습니다.
  • 자동 재시도는 멱등 메서드와 503·429 같은 일시적 응답에만 걸고 Retry-After 를 지킵니다.

4는 내 탓, 5는 서버 탓이라는 큰 갈래를 먼저 잡고, 그 안에서 코드가 알려 주는 다음 행동을 읽으면 됩니다.