현록

HTTP 상태 코드 정리

HTTP 상태 코드는 서버가 클라이언트의 요청을 처리한 결과를 세 자리 숫자로 알려 주는 값이다.

브라우저나 앱은 이 코드를 보고 페이지를 표시할지, 다른 주소로 이동할지, 재로그인을 요청할지, 오류 화면을 보여 줄지 판단합니다.

개발자는 모든 번호를 외우기보다 첫 번째 숫자가 나타내는 범주부터 이해하는 편이 좋습니다.

HTTP 상태 코드의 구조

상태 코드는 100부터 599까지 다섯 범주로 나뉩니다.

  • 1xx: 요청 처리가 계속되고 있음을 알리는 정보 응답
  • 2xx: 요청을 성공적으로 처리한 응답
  • 3xx: 다른 위치로 이동하거나 캐시를 사용해야 하는 응답
  • 4xx: 요청 형식, 인증, 권한, 대상 등 클라이언트 측에서 확인할 문제가 있는 응답
  • 5xx: 서버가 올바른 요청을 처리하지 못한 응답

첫 번째 숫자는 큰 방향을 알려 주고, 뒤의 두 숫자는 구체적인 상태를 구분합니다.

1xx 정보 응답

1xx 응답은 요청이 끝나지 않았고 중간 단계를 지나고 있음을 알립니다.

일반적인 웹 개발에서는 2xx부터 5xx보다 직접 다룰 일이 적습니다.

100 Continue은 클라이언트가 요청 본문을 계속 보내도 된다는 중간 응답입니다.

101 Switching Protocols는 서버가 프로토콜 변경 요청을 수락했을 때 사용합니다.

2xx 성공 응답

2xx는 요청을 성공적으로 받고 처리했다는 범주입니다.

다만 성공했다는 결과가 항상 200 OK를 의미하지는 않습니다.

200 OK

200 OK는 요청이 성공했고 응답 본문에 결과를 담아 보낼 때 가장 널리 사용합니다.

HTTP/1.1 200 OK
Content-Type: application/json

{
  "id": 42,
  "title": "HTTP 상태 코드 정리"
}

GET으로 데이터를 조회하거나 기존 데이터를 수정한 결과를 보낼 때 자주 볼 수 있습니다.

201 Created

201 Created는 요청 결과로 새 리소스가 생성되었음을 알립니다.

게시글, 회원, 주문을 새로 만드는 POST 요청에 잘 어울립니다.

생성된 리소스의 URL을 Location 헤더로 알려 줄 수 있습니다.

HTTP/1.1 201 Created
Location: /posts/42
Content-Type: application/json

{
  "id": 42
}

204 No Content

204 No Content는 요청은 성공했지만 보낼 응답 본문이 없을 때 사용합니다.

삭제가 성공했고 추가 데이터가 필요하지 않은 경우가 대표적입니다.

204 응답에는 본문을 넣지 않아야 합니다.

200, 201, 204의 차이

세 코드는 모두 성공을 나타내지만 결과의 성격이 다릅니다.

  • 200 OK: 성공 결과를 응답 본문으로 보내는 경우
  • 201 Created: 새 리소스를 생성한 경우
  • 204 No Content: 성공했지만 보낼 본문이 없는 경우

성공이라는 이유만으로 모두 200을 반환하면 클라이언트가 결과의 의미를 추가로 해석해야 합니다.

3xx 리다이렉션과 캐시

3xx는 요청을 완료하기 위해 다른 위치로 이동하거나 캐시된 결과를 사용해야 함을 나타냅니다.

301 Moved Permanently

301 Moved Permanently는 리소스의 URL이 영구적으로 바뀌었음을 알립니다.

새 URL은 보통 Location 헤더에 담깁니다.

검색 엔진과 브라우저는 앞으로 새 주소를 사용해야 한다고 해석할 수 있습니다.

302 Found

302 Found는 리소스가 일시적으로 다른 URL에 있음을 알립니다.

원래 URL을 계속 사용할 예정이라면 301보다 302가 어울립니다.

304 Not Modified

304 Not Modified는 리소스가 이전 요청 이후 바뀌지 않았으므로 이미 저장한 캐시를 사용해도 된다는 뜻입니다.

서버가 같은 본문을 다시 보내지 않아도 되므로 네트워크 사용량을 줄일 수 있습니다.

304는 보통의 성공 본문 대신 캐시 재사용을 지시하는 응답입니다.

4xx 클라이언트 오류

4xx는 서버가 요청을 처리하기 어려운 이유가 요청 측에 있음을 나타냅니다.

클라이언트는 같은 요청을 반복하기보다 파라미터, 인증 정보, 권한, URL을 먼저 확인해야 합니다.

400 Bad Request

400 Bad Request는 요청 문법이 틀렸거나 필수 값이 없는 등 요청을 이해하기 어려울 경우에 사용합니다.

깨진 JSON, 잘못된 파라미터 형식, 필수 헤더 누락이 예시입니다.

401 Unauthorized

401 Unauthorized는 현재 요청을 보낸 사용자를 올바르게 인증하지 못했다는 뜻입니다.

로그인하지 않았거나 토큰이 없고, 만료되었거나, 잘못된 경우에 주로 반환합니다.

403 Forbidden

403 Forbidden은 서버가 요청을 이해했지만 사용자에게 해당 작업을 수행할 권한이 없음을 나타냅니다.

일반 회원이 관리자 페이지에 접근하려는 경우가 예시입니다.

404 Not Found

404 Not Found는 요청한 리소스를 찾을 수 없음을 나타냅니다.

URL이 틀렸거나 대상이 삭제된 경우에 주로 발생합니다.

리소스의 존재 자체를 노출하지 않기 위해 권한이 없는 사용자에게 404를 반환하는 설계도 있습니다.

기타 4xx 코드

405 Method Not Allowed는 URL은 있지만 해당 HTTP 메서드를 허용하지 않는 경우입니다.

409 Conflict는 현재 리소스 상태와 요청이 충돌하는 경우에 사용합니다.

이미 존재하는 아이디로 회원을 생성하거나 낮은 버전의 데이터로 수정하려는 경우가 예시입니다.

422 Unprocessable Content는 요청 형식은 올바르지만 내용의 의미를 검증하는 단계에서 처리할 수 없는 경우에 사용합니다.

이메일 형식, 비밀번호 길이, 수량 범위 같은 검증 실패를 표현할 때 쓸 수 있습니다.

429 Too Many Requests는 짧은 시간에 너무 많은 요청을 보내 제한에 걸렸음을 나타냅니다.

서버는 Retry-After 헤더로 다시 시도할 시점을 알려 줄 수 있습니다.

401과 403의 차이

401403은 모두 접근 실패와 관련되지만 확인할 지점이 다릅니다.

  • 401 Unauthorized: 누구인지 올바르게 인증되지 않음
  • 403 Forbidden: 누구인지 알지만 해당 작업을 할 권한이 없음

로그인이 필요하면 401, 로그인한 사용자의 역할이 부족하면 403으로 생각하면 구분하기 쉽습니다.

5xx 서버 오류

5xx는 요청 자체보다 서버의 처리 과정에서 문제가 발생했음을 나타냅니다.

클라이언트가 요청을 수정해도 바로 해결되지 않을 수 있습니다.

500 Internal Server Error

500 Internal Server Error는 서버가 예상하지 못한 오류로 요청을 처리하지 못했을 때 사용하는 일반적인 서버 오류 코드입니다.

처리하지 않은 예외, 데이터베이스 오류, 설정 누락 등 원인은 다양할 수 있습니다.

502 Bad Gateway

502 Bad Gateway는 게이트웨이나 프록시 서버가 뒷단 서버로부터 올바른 응답을 받지 못했을 때 발생합니다.

역방향 프록시, CDN, 로드 밸런서 뒤에 있는 원본 서버의 응답에 문제가 있을 때 볼 수 있습니다.

503 Service Unavailable

503 Service Unavailable은 서버가 현재 요청을 처리할 수 없지만 나중에는 복구될 수 있음을 나타냅니다.

서버 점검, 일시적인 과부하, 자원 부족이 대표적인 원인입니다.

복구 예정 시간을 알 수 있다면 Retry-After 헤더를 함께 보낼 수 있습니다.

504 Gateway Timeout

504 Gateway Timeout은 게이트웨이나 프록시 서버가 뒷단 서버의 응답을 제한 시간 안에 받지 못했을 때 발생합니다.

502가 잘못된 응답을 받은 경우라면 504는 응답을 기다리다가 시간이 끝난 경우에 가깝습니다.

404와 500의 차이

404는 요청한 대상을 찾을 수 없다는 클라이언트 오류입니다.

500은 서버 내부에서 예상하지 못한 문제로 처리에 실패했다는 서버 오류입니다.

존재하지 않는 게시글을 조회했다면 404가 자연스럽습니다.

게시글은 있지만 조회 중 데이터베이스 예외가 발생했다면 500이 자연스럽습니다.

모든 결과를 200으로 반환하는 문제

다음처럼 실패한 요청도 200 OK로 반환하는 API를 볼 수 있습니다.

HTTP/1.1 200 OK
Content-Type: application/json

{
  "success": false,
  "error": "post not found"
}

응답 본문을 읽으면 실패이지만 HTTP 상태 코드만 보면 성공입니다.

이러한 설계는 브라우저, CDN, 모니터링 도구, API 클라이언트가 요청 결과를 정확히 판단하기 어렵게 만듭니다.

HTTP 상태 코드는 요청의 결과를 나타내고, JSON 본문은 결과의 상세 정보를 제공하는 역할로 나누는 편이 좋습니다.

fetch의 상태 코드 처리

브라우저의 fetch()404500 응답을 받았다는 이유만으로 Promise를 reject하지 않습니다.

서버와 HTTP 응답을 주고받는 데 성공했기 때문에 Response 객체로 resolve됩니다.

따라서 response.okresponse.status를 직접 확인해야 합니다.

const response = await fetch('/api/posts/42')

if (!response.ok) {
  throw new Error(`HTTP error: ${response.status}`)
}

const post = await response.json()

response.ok는 상태 코드가 200에서 299 사이일 때 true입니다.

response.status를 사용하면 401에서는 로그인 화면으로 이동하고, 404에서는 대상이 없다는 UI를 보여 주는 식으로 분기할 수 있습니다.

재시도 기준

오류가 발생했다고 모든 요청을 즉시 다시 보내면 서버 부하를 더 키울 수 있습니다.

400, 401, 403, 404, 422 같은 대부분의 4xx 응답은 요청을 그대로 반복해도 해결되지 않습니다.

요청 내용, 인증 정보, 권한을 먼저 바꿔야 합니다.

429, 502, 503, 504처럼 일시적일 수 있는 오류는 시간 간격을 늘려 재시도할 수 있습니다.

서버가 Retry-After를 보냈다면 그 값을 우선해야 합니다.

재시도 간격은 1초, 2초, 4초처럼 점진적으로 늘리는 방식을 사용할 수 있습니다.

주문 생성처럼 반복 시 같은 작업이 중복 실행될 수 있는 요청은 멱등성을 보장하는 장치가 필요합니다.

상태 코드를 고르는 순서

먼저 요청이 성공했는지 확인합니다.

성공했다면 기존 결과 반환은 200, 새 리소스 생성은 201, 본문 없는 성공은 204를 검토합니다.

요청 측 문제라면 인증 여부, 권한, 대상 존재, 요청 검증 순으로 4xx 코드를 고릅니다.

서버 측 문제라면 자체 처리 오류인지, 뒷단 서버의 잘못된 응답인지, 일시적 사용 불가인지, 응답 시간 초과인지로 5xx 코드를 나눕니다.

상태 코드는 오류 메시지를 대신하는 값이 아닙니다.

클라이언트가 해결 방법을 판단할 수 있도록 적절한 상태 코드와 읽을 수 있는 오류 본문을 함께 제공하는 편이 좋습니다.

HTTP 요청 방식은 HTTP GET과 POST 차이에서, 브라우저 요청 도구는 fetch와 axios 차이에서 이어서 볼 수 있습니다.

다른 출처의 API를 호출할 때 발생하는 문제는 CORS 에러와 해결 기준에서, URL에 값을 담는 방법은 URL path parameter와 query string 차이에서 정리했습니다.

참고 자료

관련 포스트
HTML id와 class 차이 thumbnail
HTML id와 class 차이
HTML id와 class의 차이를 문서 내 유일성, 여러 값 사용, CSS 선택자, JavaScript 탐색, fragment 링크와 이름 작성 기준으로 정리합니다.
HTML button과 a 태그 차이 thumbnail
HTML button과 a 태그 차이
HTML button과 a 태그의 차이를 이동과 동작의 의미, href, type 속성, 폼 제출, 키보드 접근성, 잘못된 사용 패턴으로 정리합니다.
HTTP GET과 POST 차이 thumbnail
HTTP GET과 POST 차이
HTTP GET과 POST의 차이를 초보자 기준으로 정리합니다. 조회와 변경, query string과 request body, 캐시, safe, idempotent, form과 fetch 사용 기준을 함께 봅니다.
npm outdated와 npm update 차이 thumbnail
npm outdated와 npm update 차이
npm outdated와 npm update의 차이를 초보자 기준으로 정리합니다. Current, Wanted, Latest의 의미와 semver 범위 안에서 업데이트되는 방식, package-lock.json 변화까지 함께 봅니다.
npm install -g와 npx 차이 thumbnail
npm install -g와 npx 차이
npm install -g와 npx의 차이를 초보자 기준으로 정리합니다. 전역 설치, 로컬 설치, 일회성 실행, 프로젝트 scripts에 넣는 기준까지 함께 봅니다.
package.json에서 ^와 ~ 차이 thumbnail
package.json에서 ^와 ~ 차이
package.json 의존성 버전 앞에 붙는 ^와 ~의 차이를 초보자 기준으로 정리합니다. semantic versioning, 버전 범위, 0.x 예외, package-lock.json과의 관계까지 함께 봅니다.
npx와 npm exec 차이 thumbnail
npx와 npm exec 차이
npx와 npm exec가 어떤 명령어인지 초보자 기준으로 정리합니다. 로컬 패키지 실행, 원격 패키지 임시 실행, --package 옵션, -- 인자 전달 차이까지 함께 봅니다.
npm scripts와 npm run 정리 thumbnail
npm scripts와 npm run 정리
npm scripts란 무엇인지, package.json scripts와 npm run의 관계를 초보자 기준으로 정리합니다. npm run dev, npm start, npm test, node_modules/.bin, -- 인자 전달 방식까지 함께 봅니다.
dependencies와 devDependencies 차이 thumbnail
dependencies와 devDependencies 차이
package.json의 dependencies와 devDependencies 차이를 초보자 기준으로 정리합니다. npm install과 npm install -D, 배포 환경 설치, package-lock.json과의 관계까지 함께 봅니다.
npm ci와 npm install 차이 thumbnail
npm ci와 npm install 차이
npm ci와 npm install의 차이를 초보자 기준으로 정리합니다. clean install의 의미, package-lock.json 조건, CI에서 npm ci를 쓰는 이유, .npmrc 플래그 주의점까지 함께 봅니다.
.npmrc 파일이란? thumbnail
.npmrc 파일이란?
storybook을 사용해보려고 하다 마주한 이슈의 해결법을 알아보다가 등장한 .npmrc라는 파일에 대해 공부해보았다. .npmrc 파일이란? .npmrc 파일은 npm에 대한 config 파일이다. (npm에 대한 rc 파일) 프로젝트별 registry, install 옵션, 인증 토큰처럼 npm CLI가 읽는 설정을 관리할 때 사용한다.
스토리북이란? thumbnail
스토리북이란?
Storybook은 UI 컴포넌트를 독립적으로 개발하고, 문서화하고, 테스트하기 위한 프론트엔드 워크샵입니다. Storybook 10.4 기준 설치 흐름과 stories, 문서화, 테스트 활용 방식을 정리합니다.
TTV와 TTI 차이 thumbnail
TTV와 TTI 차이
TTV와 TTI의 차이를 초보자 기준으로 정리합니다. 사용자가 화면을 보는 시점, 상호작용 가능한 시점, FCP, LCP, INP, Core Web Vitals와의 관계까지 함께 봅니다.
Maria DB 외부 접속 설정하기 thumbnail
Maria DB 외부 접속 설정하기
안녕하세요. 오늘은 Maria DB 초기 세팅 시, 외부에서 접속이 안될 때 매뉴얼을 작성해보겠습니다. dotenv 패키지를 통해서 환경변수로 관리한다면, 별도의 추가 작업을 할 일이 없으실 겁니다.
package-lock.json 파일의 역할 thumbnail
package-lock.json 파일의 역할
안녕하세요. 오늘은 node 환경의 개발자라면 한번쯤 궁금했을만한 package-lock.json의 역할에 대해 알아보겠습니다. 우리는 node 환경에서 개발을 할 때 다양한 패키지들을 설치하여 활용하곤 합니다. 우리가 설치하는 패키지 또한 다른 npm 패키지를 활용하여 만든 패키지들이고 이들 또한 설치를 하게 됩니다. 이렇게 직간접적으로 설치된 패키지들은 대부분 호환성을 "^1.1.5"와 같이 표현하여, 범위로 지정해두고 있습니다.
Linux 환경 배포 자동화 체험해보기 thumbnail
Linux 환경 배포 자동화 체험해보기
안녕하세요. 요즘 포트폴리오를 만들면서 서버 상에 자주 반영할 일이 생겼는데, 매번 명령어들을 타이핑하는 것이 비효율적이라 생각이 들어 배포 자동화를 생각해보게 되었습니다. 현재 레벨에서는 단순히 명령어들만 단축시켜도 효율적이라 생각이 들어 간단한 쉘 스크립트만 작성하였습니다. 정말 간단하니 여러분도 도전해보시길 바랍니다.
협업 필수품. Prettier thumbnail
협업 필수품. Prettier
안녕하세요. 오늘은 Prettier이라는 도구에 대해 알려드리고자 합니다. 개발자는 각자의 코딩스타일이 존재합니다. 그러다보니 같은 프로젝트에서도 작성하는 소스마다 스타일이 제각기 다르기 일쑤입니다. 그럴 때 도입하면 좋은 것이 Prettier입니다. 프로젝트 root 폴더에 .prettierrc 라는 파일을 생성한 뒤, 위 예시와 같이 원하는 옵션을 JSON 형식으로 작성해주면 됩니다.