iamkanguk.dev

[Network] HTTP 상태코드 본문

CS지식/Network

[Network] HTTP 상태코드

iamkanguk 2024. 1. 2. 12:11
해당 포스팅은 김영한님의 모든 개발자를 위한 HTTP 웹 지식 강의를 토대로 작성된 글입니다.

HTTP 상태코드 소개

상태코드는 클라이언트가 보낸 요청의 처리 상태를 응답에서 번호로 알려주는 것이다. 크게 5개의 상태코드가 있다.

 

- 1XX (Informational): 요청이 수신되어 처리중 (거의 사용하지 않기 때문에 생략한다)

- 2XX (Successful): 요청이 정상처리 되었음

- 3XX (Redirection): 요청을 완료하려면 추가적인 행동이 필요하다

- 4XX (Client Error): 클라이언트 오류. 잘못된 문법 등으로 서버가 요청을 수행할 수 없음

- 5XX (Server Error): 서버 오류. 서버가 정상 요청을 처리하지 못함

 

앞으로 시간이 흐르면서 새로운 상태코드가 나올 수도 있다. 만약에 모르는 상태코드가 나타나면 어떻게 해야할까?

 

클라이언트가 인식할 수 없는 상태코드를 서버에서 반환하게 되면 클라이언트는 상위 상태코드로 해석해서 처리하면 된다. 즉, 미래에 새로운 상태 코드가 추가되어도 클라이언트를 변경하지 않아도 된다는 의미이다.

 

예를 들어 298번 상태코드가 들어오면 200번대 상태코드이기 때문에 성공 코드라고 생각하면 된다.

 

2XX - Successful (성공)

클라이언트의 요청을 성공적으로 처리했다는 것을 의미한다.

(1) 200 OK

 

흔히 볼 수 있는 상황이다. GET을 통해 Member의 정보를 가져오라고 요청을 하게 되면 서버에서는 정보를 줄 것이다. 정상적으로 정보를 주었고 받았기 때문에 200번 코드를 사용했다.

(2) 201 Created

 

POST 메서드를 통해 새로운 Member를 등록했다. 이 처럼 신규 리소스를 생성할 때 주로 사용하는 상태코드이다. 201로 보내주게 되면 Location 헤더가 추가되는데 클라이언트에서는 이로 식별할 수 있다.

(3) 202 Accepted

요청이 접수가 되었는데 처리는 되지 않은 경우를 의미한다. 대표적으로는 배치 작업에서 사용할 수 있는 코드이다. 예를 들어, 클라이언트 요청을 접수했는데 1시간 뒤에 처리를 해야 하는 경우이다.

(4) 204 No Content

서버가 요청을 정상적으로 수행했지만 응답에 보낼 데이터가 없다는 것을 의미한다. 예를 들어 웹 문서 편집기에서 Save 버튼이 있다고 했을 때 해당 버튼을 누르게 되면 결과로 아무 내용이 없어도 사실 무관하다. 그렇기 때문에 클라이언트에서는 204 상태코드를 받게 되면 그 코드만으로도 요청이 성공했구나 라고 알 수 있을 것이다.

 

이 외에도 200번대에는 다양한 상태코드들이 있는데 수많은 상태코드를 사용하는 것이 바람직하지는 않다고 말씀하신다. 실제 회사에서도 200만 사용하는 곳도 많고, 그렇지 않은 회사들도 많다고 한다. 필자는 과거에는 200만 무지성으로 때려 박았는데 최근에는 201, 204까지 사용을 하고 있다. 개인적으로는 200, 201, 204까지는 사용하는 것이 바람직하다고 생각한다.

 

3XX - Redirection (PART1)

요청을 완료하기 위해 유저 에이전트의 추가 조치가 필요하다는 의미이다. 유저 에이전트는 클라이언트 프로그램을 의미하는데 주로 웹 브라우저를 말한다.

 

- 300 Multiple Choices (거의 사용하지 않음)

- 301 Moved Permanently

- 302 Found

- 303 See Other

- 304 Not Modified

- 307 Temporary Redirect

- 308 Permanent Redirect

Redirection 이란?

웹 브라우저에서는 300번대 응답의 결과에 Location 헤더가 있으면 그 Location 위치로 자동 이동을 해준다. 이 현상을 Redirection이라고 한다.

 

예를 들어, 과거 이벤트 페이지 경로인 "/event"를 더이상 사용하지 않고, 새로운 이벤트 페이지 경로인 "/new-event" 를 사용한다고 가정해보자. 유저가 웹 브라우저에 /event를 입력해서 요청한 경우 서버에서는 "이 경로는 이제 사용하지 않고 다른 경로로 변경됐다." 라는 의미로 301 응답 코드와 Location 위치를 신규 경로로 지정해서 응답하게 된다.

 

그렇게 되면 웹 브라우저에서는 신규 경로로 이동하고, 서버에 다시 요청을 하게 된다. 이를 자동 리다이렉트라고 부른다.

Redireciton의 종류

- 영구 리다이렉션: 특정 리소스의 URI가 영구적으로 이동한 것

   -- /members ==> /users

   -- /event ==> /new-event

- 일시 리다이렉션: 일시적인 변경 (일시적으로 잠깐 이동시킬 때 사용한다)

   -- 주문 완료 후 주문 내역 화면으로 이동하는 것

   -- PRG: Post / Redirect / Get

- 특수 리다이렉션

   -- 결과 대신 캐시를 사용하는 것

영구 리다이렉션: 301 Moved Permanently, 308 Permanent Redirect

리소스의 URI가 영구적으로 이동했다는 것을 의미한다. 원래의 URL을 사용하지 않는다.

 

(1) 301 Moved Permanently

- 리다이렉트 시 요청 메서드가 GET으로 변하고 본문이 제거될 수 있다 (MAY 거의 이렇게 동작한다)

 

웹 브라우저에서 POST를 통해 /event 경로에 데이터를 넣고 서버에 요청했다. 서버에서는 해당 경로를 사용하지 않기 때문에 301 코드 + 새로운 Location 정보를 제공한다.

 

클라이언트에서는 서버에서 제공받은 /new-event로 경로를 변경해서 요청한다. 이 때 대부분의 브라우저들은 GET 메서드로 변경하고 메세지 바디 부분을 제거해서 요청한다. 최초 요청시에는 입력을 다 하고 POST 요청을 했지만 다시 특정 새로운 입력 폼 페이지로 이동한다. 

 

우리가 가끔 겪는 현상 중 하나인 등록하려고 했던 것을 처음부터 다시 입력하는 현상이 이런 현상이다.

 

(2) 308 Permanent Redirect

- 301과 기능은 같지만 리다이렉트 요청 메서드와 본문을 유지한다 (POST로 보내면 리다이렉트도 POST를 유지한다)

 

마찬가지로 웹 브라우저에서 POST 메서드로 /event 경로에 데이터를 넣고 서버에 요청했다. 서버에서는 마찬가지로 해당 경로를 사용하지 않기 때문에 308 응답코드와 함께 Location 정보를 반환한다.

 

클라이언트는 제공받은 새로운 경로인 /new-event로 요청을 하게 된다. 이 때 301과는 차이점으로 클라이언트에서는 POST 방식과 메세지 본문을 그대로 유지한다.

 

그리고 정상적으로 자원 등록이 완료되는 것이다. 실무에서는 308보다 301를 선호하는데 사실 301도 그렇게 많이 사용하지는 않는다고 말씀해주셨다. 경로가 변경되는 경우 내부적으로 전달해야 하는 데이터 자체도 달라졌을 확률도 높기 때문에 POST로 요청이 오더라도 GET으로 리다이렉션 하는 경우가 많다고 하신다.

 

3XX - Redirection (PART2)

일시적인 Redirection (302, 303, 307)

리소스의 URI가 일시적으로 변경되는 것이다. 따라서 검색 엔진 등에서 URL을 변경하면 안된다.

 

(1) 302 Found

리다이렉트 시 요청 메서드가 GET으로 변하고, 본문이 제거될 수 있다. 거의 이렇게 동작하지만 모든 브라우저가 이에 따랐는지 명확하지 않다. 따라서 반드시 GET으로 메서드를 변경해야 하는 경우에는 303 상태코드를 사용한다. 하지만 실무에서는 302를 많이 쓴다고 한다. 명확하지는 않지만 거의 GET으로 변경이 되기 때문에 걱정 안해도 된다고 한다.

 

(2) 303 See Other

302와 기능은 동일하다. 하지만 차이점은 리다이렉트 요청 메서드가 GET으로 변경되는 것이 보장된다. (302에서는 명확하지는 않다)

 

(3) 307 Temporary Redirect

마찬가지로 302와 기능은 동일하다. 하지만 리다이렉트 시 요청 메서드와 본문을 유지한다. (요청 메서드를 변경하면 안된다)

 

PRG: Post / Redirect / Get

예를 들어, POST로 주문을 했는데 웹 브라우저에서 새로고침을 하게 되면 어떻게 될까? 새로고침은 다시 요청을 한다는 의미인데 이는 중복 주문을 야기할 수 있다.

 

<PRG를 사용하기 전>

 

- 웹 브라우저에서 /order 경로에 POST 메서드 + 주문 데이터를 함께 서버로 요청을 보낸다.

- 서버는 이를 받아서 처리하고 주문 데이터베이스에 정보를 저장한다.

- 성공적으로 처리 후 성공 응답코드와 함께 주문완료 HTML 페이지를 클라이언트에게 제공한다.

- 여기서 만약에 클라이언트가 주문완료 HTML 페이지에서 새로고침을 했다고 가정해보자.

- 웹 브라우저는 마지막 요청인 POST 요청을 서버로 다시 요청한다.

- 서버는 마찬가지로 이 요청을 받아서 처리 하고 주문 데이터베이스에 정보를 저장할 것이다. 그리고 마찬가지로 성공 응답과 HTML 페이지를 제공한다. 그렇게 되면 중복주문이 발생하게 된다.

- 물론 서버에서 유효성 검증을 통해 중복 주문을 막아야 하지만 이는 가정하지 않겠다.

 

<PRG 적용 후>

- POST로 주문을 하고 새로 고침으로 인한 중복 주문을 방지할 수 있다.

- 우리는 POST로 주문을 하고 주문 결과 화면을 GET 메서드로 리다이렉트 시켜주는 것이다.

- 그렇게 되면 새로고침을 해도 결과 화면을 GET으로 조회하게 된다. 중복 주문 대신 결과 화면만 GET으로 다시 요청하는 것!

- 웹 브라우저에서 /order 경로에 POST + 주문 데이터와 함께 서버로 요청을 보낸다.

- 서버에서는 요청을 처리하고 DB에 정보를 저장한다.

- 이후 302 또는 303 응답 + Location 정보를 제공한다. Location은 /order-result/19로 주문번호 19로 지정되었다고 가정하겠다.

- 클라이언트는 해당 Location 정보로 리다이렉트를 하게 된다. 이 때 메서드를 GET으로 변경하고, 서버로 다시 요청하게 될 것이다.

- 서버에서는 이 요청을 받아서 19번 주문 정보를 DB에서 조회하고 성공 응답과 함께 주문완료 HTML 화면을 클라이언트에게 제공한다.

- 이렇게 되면 주문완료 페이지에서 새로고침을 하게 되어도 주문완료 페이지만 조회를 하게 된다. URL이 이미 POST에서 GET으로 Redirection이 되었기 때문에 새로고침을 해도 GET으로 결과 화면만 조회되는 것이다.

 

우리는 그러면 302, 303, 307에서 어떤 걸 써야할 까?

- 303, 307을 사용하는 것을 권장하지만 대부분의 애플리케이션에서는 302를 기본값으로 사용하고 있다고 한다. 실제로 자동 리다이렉션 때 GET으로 변해도 된다고 하면 302를 사용해도 무방할 것 같다.

 

기타 리다이렉션 (300, 304)

- 300 Multiple Choices: 안씀

- 304 Not Modified

   -- 캐시를 목적으로 사용한다.

   -- 클라이언트에게 리소스가 수정되지 않았음을 알려준다. 그러면 클라이언트에서는 Local PC에 저장되어 있는 캐시를 재사용한다.

   -- 304 응답은 응답에 메세지 바디를 포함시키면 안된다. 로컬 캐시를 사용해야 하기 때문이다.

   -- 조건부 GET, HEAD 요청 시 사용한다.

 

4XX - 클라이언트 오류

클라이언트의 요청에 잘못된 문법 등으로 서버가 요청을 수행할 수 없다는 것을 의미한다. 오류의 원인이 클라이언트에 있다는 것이다.

클라이언트가 이미 잘못된 요청을 하고 있기 때문에 수백번을 시도해도 똑같이 실패한다.

 

(1) 400 Bad Request

클라이언트가 잘못된 요청을 해서 서버가 요청을 처리할 수 없다는 것이다. 클라이언트에서는 요청 내용을 다시 검토하고 보내야 한다. 대표적으로는 요청 파라미터가 잘못되었거나 API 스펙이 맞지 않을 때 발생시키는 에러이다.

 

(2) 401 Unauthorized

클라이언트가 해당 리소스에 대한 인증이 필요하다는 것이다. 인증되지 않았다는 것이고, 401 오류 발생 시 응답에 www-authenticate 헤더와 함께 인증 방법을 설명한다. 참고로 인증과 인증의 차이에 대해서 간단하게 언급하자면, 인증은 Authentication으로 본인이 누구인지 확인하는 과정이다. 대표적으로는 로그인 과정이 있을 수 있고, 인가는 Authorization으로 권한 부여를 의미한다. 대표적으로 Admin 권한처럼 특정 리소스에 접근할 수 있는 권한을 의미한다.

 

(3) 403 Forbidden

서버가 요청을 이해했지만 승인을 거부한다는 것이다. 주로 인증 자격 증명은 있지만 접근 권한이 불충분한 경우 이 에러가 발생한다. 예를 들어 ADMIN 등급이 아닌 사용자가 로그인은 했지만 ADMIN 등급의 리소스에 접근하는 경우를 의미한다.

 

(4) 404 Not Found

요청 리소스를 찾을 수 없다는 것이다. 요청 리소스가 서버에 없거나 클라이언트가 권한이 부족한 리소스에 접근할 때 해당 리소스를 숨기고 싶을 때 사용한다.

 

5XX - 서버 오류

서버의 문제로 오류가 발생하는 것을 의미한다. 서버에 문제가 있기 때문에 요청을 재시도 하게 되면 성공할 수 있음. 복구가 될 수도 있기 때문이다.

 

(1) 500 Internal Server Error

서버 문제로 오류가 발생했다는 것을 의미한다. 애매하면 500으로 보내주면 된다.

 

(2) 503 Service Unavailable

서비스 이용 불가를 의미한다. 서버가 일시적인 과부하 또는 예정된 작업으로 잠시 요청을 처리할 수 없음을 의미한다. Retry-After 헤더 필드로 얼마 뒤에 복구가 되는지 보낼 수 있다.

 

참고로 500 오류는 최대한 지양하는 것이 좋다. 예를 들어 잔액 부족이나 20세 이상만 회원가입이 가능한데 15세가 가입을 시도했다 등의 상항인 경우에는 500대 에러 보다는 400대 에러를 발생시키는 것이 좋다. 500대 에러는 정말 서버에 내부적으로 문제가 있는 경우에만 발생시키는 것이 좋다. (DB 에러, NPE, 로직 오류 등)