💡 Tech Note

🌐HTTP Status Code Deep Dive

this.Serena 2026. 3. 23. 03:56

HTTP 상태 코드는 RFC 9110 (HTTP Semantics)에 정의된 3자리 정수 응답 코드로, 서버가 클라이언트의 요청을 어떻게 처리했는지를 나타낸다.

상태 코드의 첫 번째 자릿수는 응답의 클래스(class) 를 구분하며, 총 5개의 클래스로 나뉜다.

Class 범위 의미
1xx 100–199 Informational — 요청 수신, 처리 계속
2xx 200–299 Successful — 요청 정상 처리
3xx 300–399 Redirection — 추가 동작 필요
4xx 400–499 Client Error — 클라이언트 측 오류
5xx 500–599 Server Error — 서버 측 오류

 


1. 1xx — Informational

Code Reason Phrase 설명
100 Continue 클라이언트가 Expect: 100-continue 헤더와 함께 요청을 보낸 경우,
서버가 본문 전송을 계속해도 좋다고 알림
101 Switching Protocols Upgrade 헤더를 통한 프로토콜 전환 수락 (e.g. HTTP → WebSocket)
102 Processing 요청 처리 중임을 알림 (WebDAV, RFC 2518)

💡 포인트

  • 100 Continue는 대용량 파일 업로드 시 불필요한 본문 전송을 방지하는 데 사용된다. 서버가 헤더만으로 요청을 거부할 수 있어 네트워크 비용을 절감할 수 있다.
  • 101은 WebSocket 핸드셰이크에서 확인할 수 있다.

2. 2xx — Successful

Code Reason Phrase 설명 주요 사용 메서드
200 OK 요청 성공. 응답 본문에 결과 포함 GET, PUT, PATCH
201 Created 리소스 생성 성공.
Location 헤더에 새 리소스 URI 포함 권장
POST
202 Accepted 요청 접수 완료, 비동기 처리 시작 POST (비동기 작업)
204 No Content 요청 성공, 응답 본문 없음 DELETE, PUT

💡 포인트

200 vs 201 vs 204 선택 기준

POST   /api/users       → 201 Created + Location: /api/users/42
GET    /api/users/42    → 200 OK + Body
PUT    /api/users/42    → 200 OK (본문 반환 시) 또는 204 No Content
DELETE /api/users/42    → 204 No Content

202 Accepted 활용 패턴

비동기 작업(이메일 발송, 대용량 CSV Export 등)에서는 202를 반환하고, 작업 상태를 조회할 수 있는 Polling Endpoint를 함께 제공하는 것이 일반적이다.

POST /api/reports/export → 202 Accepted
Location: /api/jobs/abc-123

GET /api/jobs/abc-123 → 200 OK
{ "status": "processing", "progress": 65 }

3. 3xx — Redirection

Code Reason Phrase 영구/임시 메서드 변경 설명
301 Moved Permanently 영구 ⚠️ 가능 리소스가 영구적으로 새 URI로 이동.
일부 클라이언트가 POST → GET으로 변경할 수 있음
302 Found 임시 ⚠️ 가능 임시 리다이렉트. 마찬가지로 메서드 변경 가능성 있음
303 See Other ✅ GET 강제 리다이렉트 시 반드시 GET으로 변경
304 Not Modified 조건부 요청에서 캐시 유효함을 알림
307 Temporary Redirect 임시 ❌ 유지 임시 리다이렉트. 메서드 변경 금지
308 Permanent Redirect 영구 ❌ 유지 영구 리다이렉트. 메서드 변경 금지

💡 포인트

리다이렉트 코드 선택 플로우

영구 이동인가?
├── Yes → 메서드를 유지해야 하는가?
│         ├── Yes → 308 Permanent Redirect
│         └── No  → 301 Moved Permanently
└── No  → 메서드를 유지해야 하는가?
          ├── Yes → 307 Temporary Redirect
          └── No  → 302 Found (또는 303 See Other)

⚠️ 주의: 301/302는 역사적 이유로 일부 클라이언트가 POST를 GET으로 바꾸는 동작이 있다.
메서드 보존이 중요한 API에서는 307/308을 사용해야 한다.

304 Not Modified와 캐시 전략

Client → If-None-Match: "etag-value"
Server → 304 Not Modified (본문 없이 응답)

ETag / Last-Modified 기반의 조건부 요청은 API 응답 트래픽을 대폭 줄일 수 있다. CDN 환경에서는 필수적으로 고려해야 할 전략이다.


4. 4xx — Client Error

4-1. 요청 형식 오류

Code Reason Phrase 설명
400 Bad Request 요청 구문 오류, 유효하지 않은 파라미터, 잘못된 JSON 형식 등
405 Method Not Allowed 해당 리소스에서 지원하지 않는 HTTP 메서드. Allow 헤더에 허용 메서드 목록 포함 필수
406 Not Acceptable Accept 헤더에 서버가 제공 불가한 미디어 타입만 명시된 경우
413 Content Too Large 요청 본문이 서버 허용 크기 초과
414 URI Too Long URI 길이 초과
415 Unsupported Media Type Content-Type이 서버가 처리할 수 없는 미디어 타입
422 Unprocessable Content 구문은 유효하나 비즈니스 로직상 처리 불가 (e.g. 이미 존재하는 이메일로 가입 시도)

4-2. 인증 / 인가 오류

Code Reason Phrase 설명
401 Unauthorized 인증(Authentication) 실패. 유효한 자격 증명이 없음
403 Forbidden 인가(Authorization) 실패. 인증은 완료되었으나 해당 리소스에 대한 권한 없음

⚠️ 401 vs 403: 네이밍이 혼동을 주지만, 401은 "너 누구야(인증)", 403은 "너인 건 알지만 안 돼(인가)"이다.

# 인증 실패
GET /api/admin/users
Authorization: (없음)
→ 401 Unauthorized

# 인가 실패
GET /api/admin/users
Authorization: Bearer <일반_유저_토큰>
→ 403 Forbidden

4-3. 리소스 상태 오류

Code Reason Phrase 설명
404 Not Found 리소스가 존재하지 않음. 보안상 403 대신 404를 반환하기도 함
409 Conflict 리소스의 현재 상태와 충돌 (e.g. 동시 수정, 중복 생성)
410 Gone 리소스가 영구적으로 삭제됨. 404와 달리 의도적 삭제를 명시

4-4. 기타

Code Reason Phrase 설명
408 Request Timeout 서버가 대기 시간 내에 완전한 요청을 수신하지 못함
429 Too Many Requests Rate Limit 초과. Retry-After 헤더에 재시도 가능 시점 포함 권장

💡 포인트

400 vs 422 선택 기준

상황 적절한 코드
JSON 파싱 실패, 필수 필드 누락 400
구문은 유효하나 비즈니스 규칙 위반 (e.g. 비밀번호 정책 미충족) 422

429 응답 시 에러 바디 설계 예시

{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "요청 한도를 초과했습니다. 60초 후 다시 시도해 주세요.",
    "retryAfter": 60
  }
}

보안 고려사항: 404 vs 403

리소스의 존재 여부 자체를 노출하고 싶지 않은 경우(e.g. 다른 사용자의 비공개 게시글), 403 대신 404를 반환하여 리소스 존재 자체를 숨기는 것이 일반적인 보안 패턴이다.


5. 5xx — Server Error

Code Reason Phrase 설명
500 Internal Server Error 예상치 못한 서버 에러. catch되지 않은 예외 등
501 Not Implemented 서버가 해당 요청 메서드를 인식하지 못하거나 구현하지 않음
502 Bad Gateway 게이트웨이/프록시가 업스트림 서버로부터 잘못된 응답 수신
503 Service Unavailable 일시적 과부하 또는 점검 중. Retry-After 헤더 포함 권장
504 Gateway Timeout 게이트웨이/프록시가 업스트림 서버로부터 응답을 받지 못함 (타임아웃)

💡 포인트

502 vs 504 트러블슈팅

[Client] → [Nginx (Reverse Proxy)] → [Application Server]

502: Nginx가 Application Server로부터 유효하지 않은 응답을 받음
     → App 크래시, 잘못된 응답 형식 등

504: Nginx가 Application Server의 응답을 시간 내에 받지 못함
     → 긴 처리 시간, App 행(hang), 네트워크 이슈 등

500 에러 응답 설계 원칙

프로덕션 환경에서 500 에러 발생 시, 내부 구현 세부사항(스택 트레이스, DB 쿼리 등)을 절대 클라이언트에 노출하지 않아야 한다.

// ❌ Bad — 내부 정보 노출
{
  "error": "NullPointerException at UserService.java:142",
  "query": "SELECT * FROM users WHERE id = 1"
}

// ✅ Good — 트래킹 ID만 제공
{
  "error": {
    "code": "INTERNAL_ERROR",
    "message": "서버 내부 오류가 발생했습니다.",
    "traceId": "abc-123-def-456"
  }
}

6. API 에러 응답 표준화 — RFC 9457 (Problem Details)

REST API에서 에러 응답 형식이 제각각이면 클라이언트 측 에러 핸들링이 복잡해진다. RFC 9457은 이를 위한 표준 에러 응답 형식을 정의한다.

{
  "type": "https://api.example.com/errors/insufficient-balance",
  "title": "잔액이 부족합니다.",
  "status": 422,
  "detail": "현재 잔액은 1,000원이며, 요청 금액은 5,000원입니다.",
  "instance": "/api/transactions/abc-123"
}
필드 설명
type 에러 유형을 식별하는 URI (문서 링크로 활용 가능)
title 사람이 읽을 수 있는 에러 요약
status HTTP 상태 코드
detail 에러에 대한 구체적 설명
instance 에러가 발생한 구체적 리소스 경로

7. 정리 — 상태 코드 선택 플로차트

요청 처리 성공?
├── Yes
│   ├── 리소스 생성? → 201 Created
│   ├── 비동기 처리? → 202 Accepted
│   ├── 응답 본문 있음? → 200 OK
│   └── 응답 본문 없음? → 204 No Content
│
└── No
    ├── 클라이언트 잘못?
    │   ├── 인증 실패? → 401 Unauthorized
    │   ├── 인가 실패? → 403 Forbidden
    │   ├── 리소스 없음? → 404 Not Found
    │   ├── 구문 오류? → 400 Bad Request
    │   ├── 비즈니스 규칙 위반? → 422 Unprocessable Content
    │   ├── 리소스 충돌? → 409 Conflict
    │   └── 요청 제한 초과? → 429 Too Many Requests
    │
    └── 서버 잘못?
        ├── 알 수 없는 오류? → 500 Internal Server Error
        ├── 기능 미구현? → 501 Not Implemented
        ├── 업스트림 잘못된 응답? → 502 Bad Gateway
        ├── 서버 과부하/점검? → 503 Service Unavailable
        └── 업스트림 타임아웃? → 504 Gateway Timeout

References