HTTP 응답 코드 완전 정리 — 개발자가 반드시 알아야 할 상태 코드 가이드

@JavaPark · April 04, 2026 · 15 min read

HTTP 응답 코드 완전 정리 — 개발자가 반드시 알아야 할 상태 코드 가이드

안녕하세요, 자바파커입니다.

"API에서 에러가 났는데 500만 던지고 있습니다. 어떤 코드를 써야 하나요?"

솔직히 개발 초반에는 200(성공)과 500(에러), 이 두 개만 쓰는 경우가 많습니다. 하지만 적절한 상태 코드를 사용하면 프론트엔드가 에러를 자동으로 처리할 수 있고, 디버깅 시간이 절반으로 줄어듭니다.

결론부터 말씀드리면 — 실무에서 자주 쓰는 코드는 15개 정도입니다. 오늘은 전체 구조를 훑은 뒤, 실무에서 꼭 알아야 할 코드를 중심으로 정리하겠습니다.


HTTP 응답 코드 구조 — 첫 숫자가 핵심

HTTP 상태 코드는 3자리 숫자이고, 첫 번째 숫자가 응답의 종류를 결정합니다.

범위 분류 의미 비유
1xx 정보 "알겠습니다, 계속하세요" 전화 연결 중 "잠시만요"
2xx 성공 "요청을 정상 처리했습니다" "주문 완료되었습니다"
3xx 리다이렉션 "다른 곳으로 가세요" "매장이 이전했습니다"
4xx 클라이언트 에러 "당신의 요청에 문제가 있습니다" "주문서를 잘못 작성하셨습니다"
5xx 서버 에러 "서버에서 문제가 발생했습니다" "주방에서 불이 났습니다"

핵심: 4xx는 클라이언트(요청자)의 잘못, 5xx는 서버의 잘못입니다. 이 구분을 명확히 하는 것만으로도 디버깅 시간이 크게 줄어듭니다.


1xx — 정보 응답

실무에서 직접 다룰 일은 거의 없지만, 알아두면 로그 분석 시 도움이 됩니다.

코드 이름 설명
100 Continue 요청 헤더를 받았으니 본문을 보내도 됩니다
101 Switching Protocols WebSocket 전환 시 사용
103 Early Hints 브라우저가 리소스를 미리 로드할 수 있도록 힌트 제공

실무에서 만나는 경우

# WebSocket 연결 시 101 응답
GET /chat HTTP/1.1
Upgrade: websocket
Connection: Upgrade

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade

2xx — 성공

가장 많이 보고 싶은 응답 코드입니다.

코드 이름 설명 사용 시점
200 OK 요청 성공 GET 조회, PUT 수정 후 결과 반환
201 Created 리소스 생성 완료 POST로 새 데이터 생성
202 Accepted 요청 접수됨 (아직 처리 중) 비동기 작업 요청
204 No Content 성공했지만 응답 본문 없음 DELETE 완료

실무 포인트

# 200 — 조회 성공
GET /api/users/1
→ 200 OK
→ {"id": 1, "name": "김철수", "role": "개발자"}

# 201 — 생성 성공 (Location 헤더에 새 리소스 경로)
POST /api/users
→ 201 Created
→ Location: /api/users/42
→ {"id": 42, "name": "이영희"}

# 202 — 비동기 작업 접수 (이메일 발송, 대용량 처리 등)
POST /api/reports/generate
→ 202 Accepted
→ {"jobId": "abc123", "status": "processing"}

# 204 — 삭제 성공 (본문 없이 응답)
DELETE /api/users/1
→ 204 No Content

흔한 실수: 리소스를 생성(POST)하고 200을 반환하는 경우가 많습니다. 201을 쓰는 게 표준이고, 프론트엔드가 "새로 만들어졌구나"라고 인지할 수 있습니다.


3xx — 리다이렉션

URL이 바뀌었거나, 다른 곳으로 안내할 때 사용합니다.

코드 이름 설명 사용 시점
301 Moved Permanently 영구 이동 도메인 변경, URL 구조 변경
302 Found 임시 이동 로그인 후 원래 페이지로
304 Not Modified 변경 없음 (캐시 사용) 브라우저 캐싱
307 Temporary Redirect 임시 이동 (메서드 유지) HTTPS 강제 리다이렉트
308 Permanent Redirect 영구 이동 (메서드 유지) API URL 영구 변경

301 vs 302 — 실무에서 가장 중요한 차이

# 301 — 영구 이동: 검색 엔진이 새 URL을 인덱싱
# SEO에서 매우 중요. 잘못 쓰면 검색 순위가 사라질 수 있음
HTTP/1.1 301 Moved Permanently
Location: https://new-domain.com/page

# 302 — 임시 이동: 검색 엔진이 기존 URL 유지
# 점검 페이지, 로그인 후 리다이렉트 등
HTTP/1.1 302 Found
Location: /login?redirect=/dashboard

304 — 캐시의 핵심

# 1차 요청
GET /api/data
→ 200 OK
→ ETag: "abc123"

# 2차 요청 (브라우저가 ETag를 보냄)
GET /api/data
If-None-Match: "abc123"
→ 304 Not Modified  ← 본문 없이 "캐시 쓰세요" 응답

301과 308의 차이: 301은 리다이렉트 시 POST → GET으로 바뀔 수 있습니다. 308은 원래 메서드를 유지합니다. API에서는 308이 더 안전합니다.


4xx — 클라이언트 에러

"당신이 잘못한 겁니다" — 요청 자체에 문제가 있을 때 사용합니다.

코드 이름 설명 사용 시점
400 Bad Request 요청 형식 오류 필수 파라미터 누락, JSON 파싱 실패
401 Unauthorized 인증 필요 로그인 안 됨, 토큰 만료
403 Forbidden 권한 없음 로그인은 됐지만 접근 권한 부족
404 Not Found 리소스 없음 존재하지 않는 URL/ID
405 Method Not Allowed 메서드 불허 GET만 허용되는 URL에 POST
408 Request Timeout 요청 시간 초과 클라이언트 응답이 너무 늦음
409 Conflict 충돌 중복 데이터, 동시 수정 충돌
413 Payload Too Large 요청 본문 초과 파일 업로드 크기 제한
415 Unsupported Media Type 미지원 타입 JSON 기대했는데 XML이 옴
422 Unprocessable Entity 처리 불가 형식은 맞지만 비즈니스 로직 위반
429 Too Many Requests 요청 과다 Rate Limit 초과

401 vs 403 — 가장 많이 혼동하는 코드

# 401 — "누구세요?" (인증 실패)
# 로그인하지 않았거나 토큰이 만료됨
GET /api/profile
→ 401 Unauthorized
→ {"error": "토큰이 만료되었습니다. 다시 로그인해주세요."}

# 403 — "당신은 안 됩니다" (인가 실패)
# 로그인은 했지만 해당 리소스에 접근 권한이 없음
GET /api/admin/settings
→ 403 Forbidden
→ {"error": "관리자 권한이 필요합니다."}
401 Unauthorized 403 Forbidden
인증(Authentication) 실패 성공
인가(Authorization) 확인 불가 실패
해결법 로그인/토큰 갱신 권한 요청

400 vs 422 — 미묘한 차이

# 400 — 요청 자체가 깨짐 (파싱 불가)
POST /api/users
Content-Type: application/json
Body: {invalid json...
→ 400 Bad Request
→ {"error": "JSON 파싱에 실패했습니다."}

# 422 — 형식은 맞지만 내용이 유효하지 않음
POST /api/users
Body: {"email": "not-an-email", "age": -5}
→ 422 Unprocessable Entity
→ {"errors": [
    {"field": "email", "message": "올바른 이메일 형식이 아닙니다"},
    {"field": "age", "message": "0 이상이어야 합니다"}
  ]}

429 — Rate Limiting

# API 호출 제한 초과
GET /api/search?q=test
→ 429 Too Many Requests
→ Retry-After: 30        ← 30초 후 재시도
→ X-RateLimit-Limit: 100
→ X-RateLimit-Remaining: 0
→ {"error": "요청 제한을 초과했습니다. 30초 후 다시 시도해주세요."}

5xx — 서버 에러

"우리가 잘못한 겁니다" — 서버 측 문제로 요청을 처리하지 못할 때 사용합니다.

코드 이름 설명 사용 시점
500 Internal Server Error 서버 내부 오류 예기치 않은 예외, 버그
502 Bad Gateway 게이트웨이 오류 업스트림 서버 연결 실패
503 Service Unavailable 서비스 불가 서버 점검, 과부하
504 Gateway Timeout 게이트웨이 시간 초과 업스트림 서버 응답 지연

502 vs 503 vs 504 — 어디서 문제인지 파악하기

클라이언트 → [Nginx/ALB] → [애플리케이션 서버] → [DB]
                 │
                 ├── 502: 앱 서버가 죽었거나 잘못된 응답 반환
                 ├── 503: 앱 서버가 점검 중이거나 과부하
                 └── 504: 앱 서버가 응답을 너무 늦게 줌
# 502 — 앱 서버 프로세스가 죽은 경우
→ 502 Bad Gateway
→ Nginx 로그: "upstream prematurely closed connection"

# 503 — 점검 모드 (Retry-After 헤더 포함)
→ 503 Service Unavailable
→ Retry-After: 3600
→ {"error": "서버 점검 중입니다. 1시간 후 다시 시도해주세요."}

# 504 — DB 쿼리가 30초 넘게 걸린 경우
→ 504 Gateway Timeout
→ Nginx 로그: "upstream timed out (110: Connection timed out)"

중요: 500 에러에 내부 스택 트레이스를 노출하지 마세요. 보안 취약점이 됩니다. 로그에는 상세 정보를 남기되, 응답에는 일반적인 메시지만 반환하세요.


REST API 설계 시 상태 코드 선택 가이드

어떤 상황에 어떤 코드를 써야 하는지 빠르게 참고할 수 있는 가이드입니다.

CRUD별 권장 코드

메서드 성공 실패 (클라이언트) 실패 (서버)
GET (조회) 200 404 (없음), 400 (파라미터 오류) 500
POST (생성) 201 400 (형식 오류), 409 (중복), 422 (유효성) 500
PUT (전체 수정) 200 404 (없음), 400, 422 500
PATCH (부분 수정) 200 404, 400, 422 500
DELETE (삭제) 204 404 (없음) 500

인증·인가 흐름

요청 →  토큰 없음?         → 401 Unauthorized
    →  토큰 만료?          → 401 Unauthorized
    →  토큰 유효, 권한 없음? → 403 Forbidden
    →  토큰 유효, 권한 있음  → 200/201/204...

에러 응답 본문 표준 형식

{
  "status": 422,
  "error": "Unprocessable Entity",
  "message": "입력 데이터가 유효하지 않습니다.",
  "details": [
    { "field": "email", "message": "올바른 이메일 형식이 아닙니다" },
    { "field": "age", "message": "0 이상이어야 합니다" }
  ],
  "timestamp": "2026-04-04T09:30:00Z",
  "path": "/api/users"
}

실무에서 꼭 기억할 코드 TOP 10

모든 코드를 외울 필요는 없습니다. 이 10개만 정확히 사용해도 대부분의 상황을 커버합니다.

순위 코드 한 줄 요약
1 200 성공
2 201 생성 성공
3 204 성공, 본문 없음 (삭제)
4 400 잘못된 요청
5 401 인증 필요 (로그인 안 됨)
6 403 권한 없음 (로그인은 됨)
7 404 찾을 수 없음
8 409 충돌 (중복)
9 422 유효성 검증 실패
10 500 서버 에러

FAQ — 자주 묻는 질문

Q. 200과 204 중 어떤 걸 써야 하나요?

응답 본문이 있으면 200, 없으면 204입니다. DELETE 후 삭제된 데이터를 반환하고 싶으면 200, 그냥 "삭제됨"만 알리려면 204를 사용합니다. 팀 내 컨벤션을 정하고 일관되게 사용하는 게 가장 중요합니다.

Q. 에러 시 200 + 에러 메시지를 반환하면 안 되나요?

기술적으로는 가능하지만 안티패턴입니다. HTTP 클라이언트, 프록시, 모니터링 도구는 모두 상태 코드를 기준으로 성공/실패를 판단합니다. 200으로 에러를 반환하면 모니터링에서 에러가 잡히지 않고, 프론트엔드도 매번 본문을 파싱해야 합니다.

Q. 404와 410의 차이는?

404는 "없음 (있었는지도 모름)", 410 Gone은 "있었는데 삭제됨 (다시 안 돌아옴)"입니다. 검색 엔진에 "이 URL은 영구 삭제됐으니 인덱스에서 빼라"고 알리고 싶을 때 410을 사용합니다. 일반 API에서는 404면 충분합니다.


마무리

HTTP 상태 코드는 서버와 클라이언트가 대화하는 공통 언어입니다. 적절한 코드를 사용하면 프론트엔드 개발자가 별도 문서 없이도 에러를 처리할 수 있고, 모니터링 도구가 자동으로 문제를 감지할 수 있습니다.

처음에는 TOP 10만 정확히 사용하는 것부터 시작하세요. 그것만으로도 API 품질이 크게 올라갑니다.

여러분의 API에서는 어떤 상태 코드를 주로 사용하고 계신가요? 실무에서 겪은 재미있는 에러 코드 경험이 있다면 댓글로 공유해주세요!

@JavaPark
AI 시대의 개발자 도구, 실전 경험을 공유합니다