이전 글에서 예외 처리와 ErrorCode를 설계하면서
HTTP 상태코드를 함께 사용했다.
그런데 개발을 하면서 한 가지 고민이 생겼다.
👉 “HTTP 상태코드는 도대체 어떤 기준으로 써야 할까?”
처음에는 단순하게 생각했다.
- 에러면 그냥 400
- 서버 문제면 500
하지만 프로젝트를 진행하면서 느낀 건
👉 상태코드를 제대로 쓰지 않으면 API 의미가 흐려진다는 것이었다.
왜 HTTP 상태코드가 중요한가?
HTTP 상태코드는 단순한 숫자가 아니다.
👉 클라이언트와 서버가 약속하는 “의미 있는 신호”다.
예를 들어,
- 400 → 요청이 잘못됨
- 404 → 리소스 없음
- 409 → 충돌 발생
이걸 제대로 구분하지 않으면
- 프론트에서 분기 처리가 어려워지고
- API 문서의 신뢰도가 떨어지고
- 디버깅이 힘들어진다
내가 사용한 상태코드 기준
프로젝트에서는 상태코드를 다음 기준으로 나눴다.
1. 400 Bad Request – “요청 자체가 잘못된 경우”
👉 사용 기준
- 입력값이 잘못된 경우
- 필수 값이 없는 경우
- Validation 실패
예를 들어
INVALID_KEYWORD(400,"POST_003","검색 키워드는 공백일 수 없습니다.")
👉 핵심
“요청을 수정하면 해결되는 문제”
2. 401 / 403 – 인증과 권한 문제
이 둘은 항상 헷갈리는데 기준을 명확히 잡았다.
✔️ 401 Unauthorized
👉 인증 자체가 안 된 경우
- 로그인 안 함
- 토큰 없음 / 만료
AUTH_REQUIRED(401,"USER_002","인증이 필요합니다.")
✔️ 403 Forbidden
👉 인증은 되었지만 권한이 없는 경우
- 다른 사람 리소스 접근
- 접근 권한 없음
CATEGORY_FORBIDDEN(403,"CATEGORY_003","해당 카테고리에 대한 권한이 없습니다.")
👉 정리하면
- 401 → “누군지 모름”
- 403 → “누군지는 알지만 권한 없음”
3. 404 Not Found – 리소스를 찾을 수 없는 경우
👉 사용 기준
- 존재하지 않는 데이터 조회
- 이미 삭제된 데이터
USER_NOT_FOUND(404,"USER_001","유저를 찾을 수 없습니다.")
👉 핵심
“요청은 정상인데 대상이 없음”
4. 409 Conflict – 데이터 충돌
이건 초반에 많이 고민했던 부분이다.
처음에는 중복도 400으로 처리했었는데
의미적으로 맞지 않다고 느꼈다.
그래서 이렇게 분리했다.
DUPLICATED_EMAIL(409,"USER_004","이미 사용 중인 이메일입니다.")
👉 409를 쓰는 이유
- 중복은 “요청이 잘못된 게 아니라”
- “현재 상태와 충돌하는 것”
👉 그래서 400이 아니라 409가 더 적절하다.
5. 500 Internal Server Error – 서버 문제
👉 사용 기준
- 예상하지 못한 예외
- 시스템 오류
INTERNAL_SERVER_ERROR(500,"COMMON_500","서버 오류가 발생했습니다.")
👉 핵심
“클라이언트가 해결할 수 없는 문제”
설계하면서 중요하게 생각한 기준
상태코드를 선택할 때 항상 이 질문을 했다.
👉 “이 문제를 누가 해결할 수 있는가?”
- 클라이언트가 수정 가능 → 400
- 인증 문제 → 401 / 403
- 리소스 없음 → 404
- 데이터 충돌 → 409
- 서버 문제 → 500
이 기준 하나로 대부분 정리가 됐다.
적용 후 느낀 점
처음에는 상태코드를 이렇게까지 나눌 필요가 있나 싶었다.
하지만 적용하고 나서 확실히 달라졌다.
- API 의미가 훨씬 명확해졌고
- 프론트와의 협업이 쉬워졌고
- 디버깅이 빨라졌다
특히
👉 상태코드만 보고도 상황을 어느 정도 파악할 수 있게 된 점이 가장 컸다.