인프런 - 모든 개발자를 위한 HTTP 웹 기본 지식에서 김영한 님이 강의해주는 내용을 바탕으로 정리를 진행했습니다. 강의를 직접 듣고 싶으신 분들은 하단 북마크를 눌러주세요.
*제 포스팅에 나오는 모든 이미지는 김영한 님께서 만든 강의 자료에서 가져왔습니다.
목차 보기
들어가며
클라이언트가 서버로 요청을 보내면 그 요청이 서버에서 잘 처리가 되었는지, 문제가 있는지 확인할 필요가 있습니다. HTTP 응답 메시지에서 HTTP 상태 코드를 사용해서 요청이 성공했는지, 실패했는지 알려주게 됩니다.
클라이언트가 보낸 요청이 성공했는지, 실패했는지는 총 5가지의 상태로 나타내어집니다. 이번 챕터에서는 각 상태가 뜻하는 바가 무엇이며, 어떤 상태 코드가 해당 상태를 의미하는지 알아보도록 하겠습니다.
HTTP 상태코드
HTTP 상태 코드에는 총 5가지 상태가 존재합니다.
•
1xx (Informational) : 요청이 수신되어 처리중
•
2xx (Successful) : 요청 정상 처리
•
3xx (Redirection) : 요청을 완료하려면 추가 행동이 필요
•
4xx (Client Error) : 클라이언트 오류, 잘못된 문법 등으로 서버가 요청을 수행할 수 없음
•
5xx (Server Error) : 서버 오류, 서버가 정상 요청을 처리하지 못함
2xx에 포함된 상태 코드는 클라이언트가 보낸 요청이 성공했음을 알립니다. 4xx, 5xx에 포함된 상태 코드는 클라이언트가 보낸 요청이 실패했음을 알립니다. 크게 보면 성공, 실패로 나뉘지만 각 숫자 범위에 주요하게 사용되고 의미를 가지고 있는 상태 코드들이 존재합니다. 해당 코드들은 상태 코드들을 더 자세하게 보면서 설명드리겠습니다.
2xx 안에 포함된 상태 코드라고 한다면, 200부터 299까지 모두 2xx에 포함된 상태 코드입니다. 200부터 299까지 모두 성공을 나타내는 상태 코드입니다. 그 중에서 특별한 의미를 가지는 코드도 있고, 아무런 의미를 가지지 않는 상태 코드도 있습니다.(물론, 요청이 성공했다는 의미는 모든 2xx 코드들이 가지고 있습니다.)
만약, 미래에 새로운 코드가 정의되어서 299가 클라이언트로 내려왔다고 합시다. 아마 클라이언트는 299 상태 코드를 인식할 수 없기 때문에 당황스러울겁니다. 200이라면 요청 정상 처리로, 201이라면 내가 보낸 요청 메시지 바디에 담긴 값이 잘 생성되었다로 해석할 수 있겠지만, 299는 클라이언트가 처음 보는 상태 코드거든요.
그럴 때는 당황하지말고, 상위 상태 코드로 해석해서 처리하면 됩니다. 즉, “2xx 안에 포함되네?” 하고 정상 처리로 받아들이면 된다는 겁니다. 큰 범위로 이해하고 처리하면 됩니다.
이렇게 되면 미래에 새로운 상태 코드가 추가되어도 클라이언트는 변경을 하지 않아도 됩니다. 새로운 상태 코드의 상위 레벨로 이해하고 해석하면 되니깐요.
2xx - 성공
1xx가 먼저지만 1xx는 거의 사용하지 않습니다. 따라서, 위의 설명 외에 추가적인 설명을 생략하겠습니다. 한 번도 실무에서 본 적이 없는 상태 코드라고 합니다. 그냥 요청이 수신되어 서버에서 처리중이라는 뜻을 가졌다는 것만 아시면 될 듯 합니다.
그렇다면 2xx부터 알아볼까요?
위에서도 얘기했지만 2xx 상태 코드들은 클라이언트가 보낸 요청이 잘 처리가 되었다는 의미를 가집니다. 즉, 요청 성공을 뜻합니다. 우리는 2xx 상태 코드들 중에서도 많이 사용되는 200, 201, 202, 204 상태 코드에 대해서 자세하게 알아보려고 합니다.
200 OK
200은 클라이언트가 보낸 요청이 성공했다는 뜻을 가지고 있습니다.
상황) /members에 있는 100번째 멤버 조회
1.
클라이언트가 서버에게 members에 있는 100번 리소스 정보를 달라고 요청했습니다.
2.
서버는 요청 메시지를 받아서 잘 처리해 클라이언트에게 응답 메시지를 전달해줬습니다. 이때 서버가 클라이언트의 요청을 잘 처리했기 때문에 200를 상태 코드로 보내줍니다.
3.
클라이언트는 200 상태 코드를 보고 서버에서 요청을 잘 처리했다는걸 알 수 있습니다.
201 Created
201은 클라이언트의 요청을 가지고 서버 쪽에서 리소스를 생성했다는 뜻을 가집니다. 주로 POST 메서드를 사용해서 HTTP 요청 메시지 바디에 데이터를 넣어서 보낸 경우에 받을 수 있는 상태 코드겠지요?
상황) /members에 회원 정보 등록
1.
클라이언트가 서버에게 members에 신규 리소스(회원 정보)를 등록해달라고 요청했습니다. POST 메서드의 경우에는 서버에서 리소스를 생성합니다.
2.
서버는 신규 리소스를 생성하고 URI를 만듭니다. 신규 리소스를 생성한 서버는 응답 메시지에 201 상태 코드를 보내줍니다. 그리고 헤더에는 Location을 넣어줍니다. Location에는 새로 생성된 리소스의 URI가 들어있습니다.
3.
클라이언트는 201 상태 코드를 보고 리소스가 잘 생성됐구나를 알 수 있습니다. 그리고 Location 헤더가 있을 수도 있겠구나 라고 판단합니다.
202 Accepted
202는 클라이언트의 요청이 접수되었으나 아직 처리가 완료되지 않았음을 의미합니다. 처리가 완료되지 않았다는 게 무슨 뜻이냐면 클라이언트 입장에서는 요청이 성공적으로 완료되었지만, 서버 입장에서는 받았다가 1시간 뒤에 배치 프로세스가 요청을 처리해야지만 정상적으로 처리가 되었다고 생각되는 경우 같은 걸 뜻합니다.
즉, 클라이언트의 요청은 성공적으로 처리되었다 라고 보면 됩니다.
하지만, 해당 상태 코드는 잘 사용하진 않습니다.
204 No Content
클라이언트가 요청 메시지를 보내면 서버는 보통 응답 메시지를 내보냅니다. 이때에 HTTP 응답 메시지 바디에 데이터를 포함해서 보냅니다.
204는 서버가 성공적으로 요청을 처리했지만, 응답 메시지 페이로드에 보낼 데이터가 없는 경우를 뜻합니다.
데이터가 없는 경우가 있을까요?
웹 브라우저에서 웹 문서 편집기를 사용하는 경우, 204 상태 코드를 사용할 수 있습니다. 웹 문서 편집기를 사용해서 문서를 편집하면서 간간히 저장 버튼을 눌러서 내용을 저장한다고 합시다.
1.
저장 버튼을 누르면 서버로 데이터가 넘어갑니다. 아마 저희가 작성하고 있던 문서 내용일겁니다.
2.
서버는 데이터를 저장하고 나서 응답 메시지를 보내주어야 하는데, 응답으로 보내줄 정보가 없습니다. 그저 문서 저장을 성공했다는 OK 메시지만 보내주면 되니깐요. 서버에서 페이로드를 보내줘도 클라이언트가 해당 정보로 할 수 있는게 없습니다.
특히, 편집 → 저장 → 편집 → 저장 을 반복하는 상황이라고 하면 아무 내용을 보내주지 않아도 되는거죠. 저장 버튼을 눌러도 늘 같은 화면을 유지하면 됩니다.
3.
서버는 클라이언트로 204 상태 코드를 내려줍니다. 응답 메시지 페이로드에 내용이 없어도 204 메시지만으로도 클라이언트는 성공으로 인식할겁니다. 물론, 처리를 실패했을 경우에 실패 코드를 보내서 저장에 실패했음을 알려야 합니다.
200, 201, 202, 204 외에 많은 2xx HTTP 상태 코드가 존재합니다. 하지만, 주로 사용하는 상태 코드는 4가지 입니다.
그렇다면 항상 이 4가지 상태 코드를 사용해야 하는가?
그렇진 않습니다. 거의 200 만 사용합니다. 200, 201 정도만 사용하자는 경우도 많습니다. 각 팀끼리 범위를 잡고 상태 코드를 사용하는 것이 좋습니다. 안그러면 상태 코드가 너무 많기 때문에 그렇습니다.
3xx - 리다이렉션
3xx 상태 코드들은 리다이렉션의 의미를 가집니다. 클라이언트가 서버로 요청을 보냈을 때, 서버가 클라이언트의 요청을 완료하려면 추가적인 작업이 필요하다고 판단하여 클라이언트로 다시 보내는겁니다.
즉, 클라이언트의 요청을 완료하기 위해서 유저 에이전트(주로, 웹 브라우저)의 추가 조치가 필요하다는 겁니다.
3xx 상태 코드들을 알아보기 전에 우리는 리다이렉션에 대해서 먼저 알아보려고 합니다.
리다이렉션(Redirection)?
리다이렉션은 서버가 3xx 응답 결과에 Location 헤더를 함께 보내주면, 웹 브라우저가 해당 응답 결과를 받고 Location 위치로 자동 이동하는걸 말합니다.
어떻게 클라이언트가 서버의 응답을 받고서 해당 위치로 자동 이동하는걸까요?
이해를 돕기 위해서 자동 리다이렉트 흐름을 설명드리겠습니다.
상황) /event 를 조회하려고 했지만 페이지가 바뀐 경우
이전에 사용하던 /event 페이지가 더이상 사용하지 않는 페이지가 되었고, 해당 페이지는 /new-event로 변경되었습니다. 하지만, 기존 사용자들이 해당 페이지(/event)를 북마크하고 있었기 때문에 해당 페이지로 들어갈 수 있게 되었습니다. 이 때 어떤 식으로 처리될까요?
1.
클라이언트가 서버로 /event 페이지를 GET 하겠다는 요청 메시지를 보냅니다.
2.
서버는 해당 요청 메시지를 받고 /event 페이지가 더이상 사용되지 않는다는 걸 확인합니다.
3.
서버는 클라이언트에게 올바른 경로를 알려줍니다. 응답 메시지에 301 상태 코드와 Location 헤더를 함께 보냅니다. Location 헤더에는 바뀐 경로가 들어있습니다.
4.
클라이언트는 3xx 상태 코드고 Location 헤더가 있다는 걸 확인합니다. 그리고 Location 헤더에 들어있는 경로로 웹 브라우저가 스스로 URI 경로를 바꾸고 이동합니다.
5.
클라이언트에서 서버로 /new-event 페이지를 GET 하겠다는 요청 메시지를 보냅니다.
6.
서버는 요청을 확인하고 HTML 데이터를 200 상태 코드와 함께 내려줍니다.
7.
사용자는 새로운 이벤트 페이지 /new-event 화면을 봅니다.
위의 상황에서는 옛날 페이지(/event)를 요청했지만 새로운 페이지(/new-event)가 나타났습니다. 페이지가 바뀌는걸 사용자는 인식했을까요? 아니요. 리다이렉션이 빠르게 진행되기 때문에 사용자는 거의 인식하지 못합니다.
그렇다면 리다이렉션엔 어떤 것이 있을까요?
⓵ 영구 리다이렉션
영구 리다이렉션은 특정 리소스의 URI가 영구적으로 이동한 걸 말합니다. 위의 상황이 영구 리다이렉션의 예시입니다.
이전에는 /event 였지만 영구적으로 URI가 /new-event로 바뀐거니깐요. 이 경우는 /event 경로를 사용해서는 안됩니다.
⓶ 일시 리다이렉션
일시 리다이렉션은 일시적인 변경으로 인한 리다이렉션입니다. 일시적으로 잠깐 페이지를 이동할 때 사용합니다.
예를 들어서 주문 완료 후에 주문 내역 화면으로 일시적으로 이동합니다. 그렇다고 주문 화면이 영구적으로 사라진 건 아닙니다. 그저 주문 내역 화면으로 잠깐 페이지를 이동한겁니다.
⓷ 특수 리다이렉션
특수 리다이렉션은 이런 경우에 사용합니다.
예를 들어서 클라이언트에 캐시되어있는 데이터가 있었는데 캐시 기간이 만료된 거 같아서 클라이언트가 서버로 만료 기간을 확인하는 요청을 보냈습니다. 아마도 생성 일자같은 정보를 서버로 보냈을겁니다.
서버가 해당 요청을 보고 캐시가 아직 만료가 되지 않았으니 그대로 사용해도 된다는 응답을 보냈습니다. 해당 응답에서 특수 리다이렉션이 사용됩니다. 결과 대신 캐시를 사용합니다.
이제 리다이렉션이 뭔지 알았으니 3xx 상태 코드들을 알아볼까요? 3xx 상태 코드들은 위에서 배운 리다이렉션 종류와 함께 연결해서 알아봅시다.
영구 리다이렉션(301, 308)
영구 리다이렉션은 리소스의 URI가 영구적으로 이동했을 때 발생합니다. 그렇기 때문에 원래 사용하던 URL은 사용하면 안됩니다.
이 부분을 검색 엔진 등에서도 인지하고 변경해야 합니다. 어떻게 변경하냐면 검색 엔진이 /event로 들어갔을 때 영구적으로 경로가 /new-event 로 바뀐 걸 인지합니다. 그리고 검색 엔진은 /event 경로를 버리고 새로운 경로를 현재 URL로 변경합니다.
영구 리다이렉션을 하는 상태 코드는 301, 308 입니다.
사실 301, 308이 하는 기능은 똑같습니다. 둘 다 경로가 완전히 바뀌었다는 걸 알려줍니다. 하지만 차이는 존재합니다. 어떤 차이가 존재하는지 301, 308에 대해서 설명하면서 알려드리겠습니다.
301 Moved Permanently
301은 리다이렉트 시 요청 메서드가 GET 으로 변하고, 요청 메시지 본문이 제거될 수 있습니다. 무조건 GET 으로 변하고 본문이 항상 제거되는건 아니지만 거의 이렇게 동작합니다.
거의 이렇게 동작한다구요?
제가 거의라고 표현한 이유는 무조건 GET 메서드로 변하고, 본문이 항상 제거되는 것은 아니기 때문입니다. 과거에 스펙이 처음 제정할 당시에 301이 나오면 처음에 요청한 메서드를 사용해서 리다이렉트를 할 것이라고 생각했습니다.
하지만 막상 만들어놓고보니 대부분의 브라우저들이 301 상태 코드가 나오면 GET 으로 요청을 보내는 겁니다. 따라서, 스펙 자체를 수정하게 되었습니다. 그렇기 때문에 GET 메서드로 리다이렉트하지 않는 브라우저도 있을 수 있어서 거의 라는 단어로 표현하게 되었습니다.
상황) 이벤트에 참여하려고 했지만 페이지가 바뀐 경우
이전 상황과 비슷하지만 이번엔 POST 메서드를 사용해서 이벤트를 참여한다고 해봅시다. 아마 사용자는 이벤트 참여를 위해서 정보를 추가로 보냈을 겁니다.
1.
POST 메서드를 사용하고 HTML FORM 형식인 메시지 바디가 서버로 전송됩니다.
2.
서버는 요청을 확인하고 해당 경로가 더이상 사용되지 않는 경로라는걸 확인합니다.
3.
서버는 301 상태 코드와 함께 Location 헤더에 변경된 경로를 넣어줍니다.
4.
클라이언트는 301 상태 코드고 Location 헤더가 있다는 걸 확인합니다. 그리고 Location 헤더에 들어있는 경로로 웹 브라우저가 스스로 URI 경로를 바꾸고 이동합니다.
5.
클라이언트 301 상태 코드를 받았기 때문에 POST 메서드를 GET 메서드로 변경해서 요청합니다. 그리고 작성한 메시지를 제거합니다.
6.
서버에서는 클라이언트에서 요청한 새로운 경로 정보를 응답 메시지로 보내줍니다.
7.
클라이언트에 새로운 경로 페이지가 보입니다. 사용자는 처음부터 다 다시 정보를 작성합니다.
301 상태 코드는 GET 메서드로 바꿔서 요청을 보냅니다. 사용자는 이벤트 등록을 하려고 했는데 결과적으로 새로운 페이지만 나타나게 됩니다. GET 메서드로 바뀌면서 가지고 있던 메시지 바디도 날라가기 때문에 새로운 이벤트 페이지 화면에서 처음부터 다시 작성해야 합니다.
308 상태 코드는 이 문제를 해결해줄 수 있습니다.
308 Permanent Redirect
308은 기능은 301과 동일하게 합니다. 하지만 리다이렉트 시에 요청 메서드와 본문을 유지하고 있습니다. 즉, 위의 상황에서 POST를 처음에 사용하면 리다이렉트 시에도 POST 메서드를 사용합니다.
상황) 이벤트에 참여하려고 했지만 페이지가 바뀐 경우
301과 동일한 상황으로 308은 어떻게 진행하는지 보겠습니다. 이번에도 이벤트 참여를 위해서 사용자가 메시지 바디에 본문을 담아서 보냈습니다.
1.
POST 메서드를 사용하고 HTML FORM 형식인 메시지 바디가 서버로 전송됩니다.
2.
서버는 요청을 확인하고 해당 경로가 더이상 사용되지 않는 경로라는걸 확인합니다.
3.
서버는 308 상태 코드와 함께 Location 헤더에 변경된 경로를 넣어줍니다.
4.
클라이언트는 308 상태 코드고 Location 헤더가 있다는 걸 확인합니다. 그리고 Location 헤더에 들어있는 경로로 웹 브라우저가 스스로 URI 경로를 바꾸고 이동합니다.
5.
클라이언트 308 상태 코드를 받았기 때문에 POST 메서드로 동일하게 요청합니다. 그리고 이전에 작성한 메시지를 함께 보냅니다.
6.
서버에서는 클라이언트에서 요청한 새로운 리소스를 등록하고 응답 메시지를 보내줍니다.
7.
클라이언트에는 참여가 완료되었다는 화면이 뜨겠죠?
하지만 실무에서는 위의 상황처럼 거의 사용하지 않습니다.
/event에서 /new-event로 바뀌면 내부적으로 전달해야하는 데이터 자체가 다 바뀌기 때문입니다. 이전에는 이름과 나이를 보내면 됐지만, 바뀐 페이지에서는 다른 값을 전달해야할 수도 있기 때문입니다.
따라서, 이런 경우에는 POST 메서드로 처음에 들어와도 웬만하면 GET 메서드로 돌려서 다시 요청하는게 좋습니다.
일시적인 리다이렉션(302, 307, 303)
일시적인 리다이렉션은 리소스의 URI가 일시적으로 변경됐을 때 사용합니다. 따라서 나중에 URI가 안 바뀔 수도 있고, A로 바꿨다가 B로 바뀔 수도 있습니다. 그렇기 때문에 검색 엔진 등에서 URL을 변경하면 안됩니다. 다음에 또 어떻게 될 지 모르기 때문에 계속 들어오던 URL를 가지고 있으면 됩니다.
일시적인 리다이렉션을 하는 상태 코드는 302, 307, 303 입니다.
302, 307, 303이 하는 기능은 똑같습니다. 하지만, 각자 차이가 조금씩 존재합니다. 이 부분을 각 상태 코드를 설명하면서 알려드리겠습니다.
302 Found
302는 301과 동일하게 리다이렉트 시에 요청 메서드가 GET으로 변하고, 요청 메시지 본문이 제거될 수 있습니다. 무조건 GET 으로 변하고 본문이 항상 제거되는건 아니지만 거의 이렇게 동작합니다.
301 상태 코드와 동일하게 무조건 GET 으로 변하고, 요청 메시지 본문이 제거되는 건 아니라서 거의 라고 표현하겠습니다.
307 Temporary Redirect
307은 302와 기능이 동일합니다. 하지만 308과 동일하게 리다이렉트 시에 요청 메서드와 본문을 유지하고 있습니다. 즉, POST를 처음에 사용하면 리다이렉트 시에도 POST 메서드를 사용합니다.
요청 메서드 변경이 MUST NOT 이기 때문에, 메서드는 처음에 온대로 사용해야 합니다.
303 See Other
303은 302와 기능도 같고, 리다이렉트 시에 요청 메서드가 GET 으로 변하고, 요청 메시지 본문이 제거됩니다. 그렇다면, 둘의 차이점은 뭘까요?
303은 무조건 GET 메서드를 사용해야하며, 무조건 요청 메시지 본문이 제거됩니다. 303은 명확하게 그래야만 합니다. 만약, 내가 POST 메서드로 오던, 뭘로 오던 무조건 GET 메서드로 변경해야한다면, 303을 사용하면 됩니다.
아마 307, 303이 302보다 많이 사용될 것이라고 생각하실 수도 있습니다. 하지만 실무에서는 302를 많이 사용합니다. 왜냐하면 302를 사용해도 큰 문제가 없기 때문입니다.
대부분의 프레임워크에서는 라이브러리들이 302를 디폴트로 사용하는 경우가 많다고 합니다. 따라서, GET으로 변경해야 하는 상황이라면 그냥 302를 써도 무관하다는거죠.
그래서 일시적인 리다이렉션은 언제 사용하느냐?
일시적인 리다이렉션이 꼭 필요한 예시가 바로 PRG(POST/Redirect/GET) 입니다.
상황) 상품 주문 페이지에서 주문을 하는 상황(PRG 사용 전)
사용자가 상품 주문 페이지에서 주문하고 싶은 제품을 고른 후에 주문하기 버튼을 눌러서 주문을 했다고 칩시다. POST 메서드를 사용해서 내 주문 정보가 보내질겁니다. 그때 사용자가 실수로 웹 브라우저를 새로고침했다면 어떻게 될까요?
물론 웹 브라우저에서는 진짜로 새로고침을 진행할건지, 한 번 더 물어볼겁니다. 그래도 “예”를 눌러서 새로고침을 진행했다면 요청 메시지가 다시 서버로 보내져서 중복 주문이 들어갈겁니다. 왜냐하면, 마지막 요청이 물건을 주문하는 요청이었기 때문에 새로고침 시에 해당 요청이 다시 보내진겁니다.
위의 상황 흐름을 그림과 함께 다시 볼까요?
1.
사용자가 마우스 1개를 주문합니다. 주문하기 버튼을 눌렀을 때, POST 메서드를 사용해서 물건과 물건의 갯수가 요청 메시지 바디로 보내집니다.
2.
요청 메시지를 받은 서버는 주문 데이터를 DB에 저장합니다.
3.
서버는 데이터 저장이 성공적으로 완료했다는걸 응답 메시지를 통해서 클라이언트에게 보내줍니다.
4.
사용자가 결과 화면을 새로고침하기 위해서 새로고침 버튼을 눌렀습니다.
5.
마지막 요청이 ⓵에서 주문하기 버튼을 눌렀을 때 보내졌던 POST 요청이기 때문에 해당 요청이 다시 서버로 보내집니다.
6.
요청 메시지를 받은 서버는 주문 데이터를 DB에 저장합니다.
7.
서버는 데이터 저장이 성공적으로 완료했다는걸 응답 메시지를 통해서 클라이언트에게 보내줍니다.
아마 사용자가 결과 화면을 계속해서 새로고침하게 된다면 계속 물건을 구매한 것으로 처리가 될 겁니다. 나중에 마우스를 여러 개 받게 되는 문제가 발생하게 되겠죠. 이런 문제가 발생하지 않도록 서버에서 잘 막아야 합니다.
서버에서는 중복 주문이 오면 서버에서 이미 사용된 주문번호라고 해서 해당 요청을 버리는 일을 해야합니다.
물론, 클라이언트에서 애초에 해당 요청이 오지 않게끔 막아주는 것이 훨씬 좋습니다.
클라이언트 차원에서 1차적으로 방지해주는 것이 사용자의 사용성에도 좋기 때문입니다. 그렇다면, 어떻게 방지해줄 수 있을까요?
바로, 리다이렉션을 사용하는 겁니다.
상황) 상품 주문 페이지에서 주문을 하는 상황(PRG 사용 후)
위의 상황과 동일합니다. 하지만 이번엔 리다이렉션을 사용해볼겁니다. 이전과 다르게 리다이렉션 상태 코드를 받아서 GET 메서드로 리다이렉션을 해보려고 합니다. 그렇게 되면 새로고침 문제가 어떻게 해결되는지 볼까요?
1.
사용자가 마우스 1개를 주문합니다. 주문하기 버튼을 눌렀을 때, POST 메서드를 사용해서 물건과 물건의 갯수가 요청 메시지 바디로 보내집니다.
2.
요청 메시지를 받은 서버는 주문 데이터를 DB에 저장합니다.
3.
서버는 응답 메시지에 302 상태 코드와 함께 Location 헤더를 보냅니다.
4.
클라이언트는 302 상태 코드를 확인하고 Location 헤더 안에 있는 경로로 자동 리다이렉트시킵니다.
5.
302 상태 코드를 응답으로 받았기 때문에 POST 메서드는 GET 메서드로 바뀝니다. 그리고 가지고 있던 메시지 바디는 제거됩니다. 그 상태로 새로운 경로로 요청 메시지가 보내집니다.
6.
요청 메시지를 받은 서버는 해당 경로 데이터를 조회합니다.
7.
서버는 조회한 주문 정보를 HTML 화면으로 내려줍니다.
8.
클라이언트는 주문 완료 화면을 성공적으로 받게 됩니다.
9.
이 상태에서 새로고침을 해도 마지막 요청이 GET 메서드를 사용해서 19번 데이터를 조회하는 요청이었기 때문에 계속 해당 요청이 보내지게 됩니다. 즉, 중복 주문 문제가 해결됩니다.
PRG 이후에는 요청이 이미 POST에서 GET으로 변경되었기 때문에 새로고침을 진행해도 GET 요청으로 인한 결과 화면만 받게 됩니다.
이렇게 되면 사용자는 실수로 새로고침을 해도 깔끔하게 결과 화면만 보이기 때문에 좋습니다. 만약, POST를 이전처럼 하게 된다면 진짜로 새로고침을 할 건지, 매번 경고창이 뜰겁니다. 또한 서버 입장에서도 오류가 줄어들기 때문에 GET를 사용한 방식이 좋습니다.
그래서 우리는 302, 307, 303 중 무엇을 사용해야 할까요?
현실적으로 307, 303을 사용하는 걸 권장하지만, 이미 많은 애플리케이션 라이브러리들이 302를 기본값으로 사용하고 있습니다. 자동 리다이렉션 시에 GET 메서드로 변해도 된다면 그냥 302를 사용해도 큰 문제는 없습니다.
기타 리다이렉션
300은 요청에 대해서 하나 이상의 응답이 가능한 상태코드입니다. 사용자가 응답을 선택해야합니다. 다만 표준화된 선택법이 없어 잘 사용되지 않습니다.
304 Not Modified
304 상태 코드는 진짜 많이 사용됩니다. 304는 캐시를 목적으로 사용하는 상태 코드 입니다.
클라이언트가 서버로 캐시가 만료된 거 같으니 서버에서 해당 데이터를 다시 내려달라고 요청합니다. 하지만, 서버에서는 아직 캐시가 만료되지 않았으니 그대로 사용하면 된다고 응답을 보냅니다.
클라이언트는 로컬 PC에 있는 캐시를 재사용하게 되고 캐시로 리다이렉트하게 됩니다. 그렇게 되면 데이터를 다시 다운받지 않아도 되기 때문에 네트워크 다운로드 용량이 많이 줄어들게 됩니다.
304 응답에서는 서버가 응답 메시지에 메시지 바디를 포함하면 안됩니다. 왜냐하면, 서버에서 클라이언트로 로컬 PC에 있는 캐시를 그대로 사용하라는 것이기 때문에 바디를 포함해서는 안됩니다.
304 상태 코드는 조건부 GET, HEAD 요청 시 사용되며, 이후 HTTP 헤더 챕터에서 조건부 요청에 대해서 배우게 될 때 자세하게 알려드리겠습니다.
4xx - 클라이언트 오류(Client Error)
4xx 상태 코드는 설명 그대로 클라이언트가 무언가를 잘못 했기 때문에 발생하는 에러입니다. 클라이언트가 보낸 요청에 잘못된 문법 등이 있어서 서버에서 요청을 수행할 수 없는거죠.
즉, 오류의 원인이 클라이언트 입니다.
4xx 상태 코드가 가지고 있는 큰 특징이 있는데, 이 특징이 4xx 상태 코드와 5xx 상태 코드를 나눕니다.
특징을 설명드리기 위해서 클라이언트가 어떤 요청을 보내는 예를 들어보겠습니다.
•
4xx
(클) : 클라이언트가 서버로 요청을 보냄
(서) : 서버에서 요청을 받았지만 잘못된 문법이 있는 요청이라 4xx 응답 메시지 보냄
(클) : 클라이언트가 서버로 다시 요청을 보냄
(서) : 서버에서 요청을 받았지만 잘못된 문법이 있는 요청이라 4xx 응답 메시지 보냄
(클) : 클라이언트가 서버로 다시 요청을 보냄
(서) : 서버에서 요청을 받았지만 잘못된 문법이 있는 요청이라 4xx 응답 메시지 보냄
… (반복)
4xx는 클라이언트가 무언가 잘못된 요청을 했기 때문에 오는 상태 코드입니다. 따라서, 클라이언트가 이를 인지하고 코드를 수정하지 않는 이상 여러번 재시도를 해도 실패합니다.
•
5xx
(클) : 클라이언트가 서버로 요청을 보냄
(서) : 서버에서 장애가 발생해서 500 응답 메시지 보냄
(클) : 클라이언트가 서버로 다시 요청을 보냄
(서) : 서버가 복구되어서 클라이언트로 200 응답 메시지 보냄
(클) : 응답 메시지 확인 완료 후 응답 데이터 처리
5xx는 서버에서 장애가 나거나 해서 서버에서 문제가 있을 때 내려주는 상태 코드입니다. 따라서, 나중에 문제가 해결되어서 서버가 복구되면 클라이언트에서 보낸 요청 메시지를 확인해서 200 상태 코드를 내려줄 수도 있습니다. 물론, 해당 요청 메시지에 문제가 있다면 다른 상태 코드가 가겠지만, 4xx 과는 다르게 요청 처리가 성공할 가능성이 존재합니다.
4xx는 클라이언트가 수정해서 다시 보내지 않는 이상, 복구가 불가능하지만 5xx는 성공 가능성이 존재합니다.
400 Bad Request
400은 클라이언트가 잘못된 요청을 해서 서버가 요청을 처리할 수 없는 경우에 보내는 상태 코드입니다.
클라이언트가 보낸 요청에서 요청 구문이나 메시지 등등에 문제가 있을 때 발생합니다.
그렇기 때문에, 클라이언트가 요청 내용을 다시 검토해서 제대로 된 요청을 보내주어야 합니다.
예를 들어서, 문자를 보내주어야 하는데 숫자 타입으로 요청을 보냈다던지 하는 경우가 있을 수 있겠죠. 이럴 때 문제가 생기지 않도록 백엔드 개발자들은 이런 부분들을 철저하게 검증해서 400 에러가 보내질 수 있도록 해야합니다. 잘못해서 5xx 에러를 보낸다면 클라이언트는 서버 쪽에 문제가 있다고 생각하기 때문입니다.
401 Unauthorized
401은 클라이언트가 해당 리소스에 대한 인증이 필요하다는 걸 나타냅니다.
인증이 필요하다는 건 로그인이 필요하다는 뜻입니다.
401 오류 발생시 응답에 WWW-Authenticate 헤더와 함께 인증 방법을 설명해주기 때문에 401 오류가 발생하면 클라이언트에서는 해당 헤더를 확인해보면 됩니다.
인증과 비슷한 인가라는 개념이 있습니다. 다음 상태 코드를 설명하기 전에 두 개념의 차이를 짚고 넘어가려고 합니다.
•
인증(Authentication)
인증은 본인이 누구인지 확인하는 과정입니다. 로그인을 떠올려보면 비회원이 고유한 id를 가진 회원으로 바뀌는 과정입니다. 고유한 id를 가졌기 때문에 서버에서는 해당 id로 누군지 판별할 수 있습니다.
•
인가(Authorization)
인가는 권한을 부여하는 겁니다. 로그인한 사용자에게 권한 레벨을 부여하는 것입니다. 어드민 권한처럼 특정 리소스에 접근할 수 있는 권한을 인가가 부여합니다.
인증이 로그인을 할 수 있는지, 없는지에 대한 것이라면 인가는 이 리소스에 접근할 수 있는지, 없는지 등급을 나누는겁니다. 이 부분을 알고 나니 401의 오류 메시지가 Unauthentication이 아니고 Unauthorized로 되어 있다는 게 아쉽네요.
403 Forbidden
403은 서버가 요청을 이해했지만 승인을 거부했다는 걸 의미합니다.
즉, 인증 자격은 증명되었지만(로그인 완료), 접근 권한은 없다는 걸 의미합니다.
예를 들어서, 어드민 등급이 아닌 사용자가 어드민 리소스에 접근하는 경우, 로그인은 완료된 사용자지만 해당 리소스에 대한 권한이 없기 때문에 승인이 거부됩니다.
404 Not Found
404는 많이 들어본 상태 코드일겁니다. 요청 리소스를 찾을 수 없는 경우에 나타납니다.
이 리소스 자체가 없는데 클라이언트가 서버로 요청을 보낸겁니다. 서버는 해당 리소스가 없기 때문에 클라이언트에게 해당 리소스에 대한 정보를 제공해줄 수 없어서 404 오류를 보냅니다.
클라이언트가 권한이 부족한 리소스에 접근했을 시에도 404 오류를 보낼 수 있습니다. 권한이 부족한 리소스에 접근했을 때 403를 보낼 수도 있지만 그냥 완전히 해당 리소스를 숨기고 싶다면 404 오류를 보냅니다.
5xx - 서버 오류(Server Error)
5xx는 서버 문제로 오류가 발생했을 때 옵니다. 4xx 에서 설명했지만 서버에 문제가 있는것이라서 재시도하면 요청이 성공할 수 있다는 가능성이 존재합니다.
5xx는 null point exception 문제로 인해서 나는 경우도 있고, DB에 접근이 불가하게 되어서 나는 경우도 있고 다양합니다.
500 Internal Server Error
500은 서버 문제로 오류가 발생했을 때 나오는 상태 코드입니다. 백엔드에서 나는 애매한 에러들은 다 500이 나온다고 보시면 됩니다.
503 Service Unavailable
503은 서비스 이용 불가 시에 나옵니다.
예를 들어서, 서버를 한 시간동안 서버 다운한다고 하면 503 오류를 보내서 서버가 일시적으로 요청을 처리할 수 없다는 걸 보여줄 수 있습니다. 이 때, Retry-After 헤더를 사용해서 얼마 뒤에 복구되는지 예상 시간을 보낼 수 있습니다.
웬만해서는 서버에서 5xx 에러를 만들면 안됩니다. 진짜 서버에 문제가 터졌을 때 5xx 에러를 보내주어야 합니다.
예를 들어서 데이터도 잘 들어오고 해당 데이터가 서버까지 잘 왔는데, “고객의 잔고가 부족하다” 라고 해서 500 에러를 보내줘서는 안됩니다. 정상 프로세스지만 비즈니스 로직 상에 예외 케이스가 생긴 것이지 서버에서 문제가 생긴 것이 아니기 때문입니다.
500은 진짜 서버 문제가 있을 때 보내주고, 나머지는 400, 200대 상태 코드로 해결해주어야 합니다.