seminar-2020 icon indicating copy to clipboard operation
seminar-2020 copied to clipboard

(옮김) HTTP는 크게 Header와 Body로 이루어지는데, Postman의 response를 보면 왜 Cookies가 따로 있나요?

Open davin111 opened this issue 3 years ago • 2 comments

이번 세미나부터는 세미나 이후 참여가 자유인 질의응답 시간을 따로 가집니다. 물론 개인 일정, 다음 세미나 등으로 참여가 어려운 분이 있을 수 있습니다. 이 내용은 오늘 Backend 세미나 이후 질의응답 시간 중 나온 질문인데 제가 좀 더 찾아보고 정리해 말씀드리겠다고 한 질문입니다. 원문은 아니고 해당 질문의 내용을 제가 기억하는대로 짧게 옮겼습니다. HTTP에 대한 내용이라 frontend, android, backend 등 여러 영역에 해당될 질문이라 general로 label을 달았습니다.

HTTP는 크게 Header와 Body로 이루어져있는 것으로 알고 있습니다. 그런데 여기서 Postman의 response(하단부)를 보면 Cookies와 Headers가 나눠져있습니다. 이는 어떻게 된 것인가요?

스크린샷 2020-09-05 16 20 13

davin111 avatar Sep 05 '20 07:09 davin111

정말 좋은 질문이었던 것 같습니다. 제가 알고 있는 바도 그렇기는 한데, Postman을 놓고 이를 고민해본 적이 없어서 '그냥 Cookies도 Headers에 포함되는 거 맞는데, 요청을 주고받는 것에 대한 GUI 툴이니까 Cookies만 따로 이렇게 꺼내둔 거겠죠?' 정도로 넘어가려다가 정확히 파악하고 다른 세미나 분들께도 잘 공유되도록 말씀드리는 게 좋겠다 싶어, 저도 이에 대해 다시 찾아보고 자세히 남깁니다. 우리가 개발하며 매번 쓰는 것(아니 사실 일상적으로 전 인류가 엄청나게 쓰고 있긴 하죠)이 HTTP인데, 이 기회에 해당 구조를 잘 알아두면 좋으니까요! 덕분에 기본을 다시 복습하고 좀 더 탄탄히 합니다, 감사합니다.

davin111 avatar Sep 05 '20 07:09 davin111

HTTP request, response의 구조

세미나 과정상 명확히 짚어진 곳이 없고 HTTP가 HyperText Transport Protocol이란 것이며, 우리가 일반적으로 개발을 해서 기기 간 네트워크 통신을 할 때 가장 일반적으로 쓸 프로토콜이다, 정도로 언급된 것 같은데요. HTTP의 Request와 Response의 구조는 둘 다 대략적으로는 아래와 같이 생겼습니다.

36

아주 간단하고, 질문해주셨듯 Header와 Body로 이루어져있는 것을 볼 수 있습니다.


curl

실제 확인을 위해 curl을 이용해 backend/seminar1/waffle_backend의 서버를 로컬에 실행한 후 요청을 보내볼게요! curl은 다양한 통신 프로토콜을 통해 요청을 주고받을 수 있는 CLI 툴이라고 할 수 있습니다. 이러한 요청 프로그램의 GUI로 Postman이 있다면, CLI로는 curl이 있는 것이죠. 참고로 저는 MacOS 환경이고, Linux에서는 거의 동일하게 쉽게 테스트해보실 수 있을 것입니다. Windows에도 curl이 있는 것으로 알지만 명령어 방식이 완전히 같을지는 모르겠네요. 어쨌든 직접 해보시기를 권장드립니다! 재밌어요 :)

이런 글에도 잘 설명이 되어있습니다만, 제가 사용한 curl 명령어의 옵션들을 하나하나 짚어볼게요. curl을 이용해 요청을 보내는 이 행동이, 이 Issue 본문 이미지의 Postman에서 하려는 /api/v1/user/login/ endpoint에다가 PUT method로 body에 username으로는 davin111, password로는 test를 넣어보내려는 것과 완전히 동일함을 확인하시면 되겠습니다.

-v는 오로지 디버깅을 위한 목적을 갖는 옵션입니다. 일반적으로는 내가 보내는 request에 대해 굳이 console에 출력시킬 필요가 없겠지만, 여기서는 HTTP request와 response의 구조 모두를 보기 위해 굳이 출력시켰습니다. request, response를 모두 verbose하게 나타내고, response의 경우 header와 body 모두를 출력합니다.

-X PUT은 뒤에 지정한 endpoint "http://localhost:8000/api/v1/user/login/"에다가 어떤 method로 요청할지를 나타냅니다. 명시하지 않으면 마치 웹브라우저가 그렇듯 GET이 기본입니다.

-d "username=davin111&password=test"는 request에 data(body)가 있는 경우 해당 데이터를 명시하기 위해 포함되어있습니다. Postman에서 form-data 탭을 택해 간편하게 넣었던 것을, 이제 &=를 활용해 적절히 넣어줘야함을 확인할 수 있습니다.

*****ui-MacBookPro:~ $ curl -v -X PUT -d "username=davin111&password=test" "http://localhost:8000/api/v1/user/login/"     
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8000 (#0)
> PUT /api/v1/user/login/ HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.64.1
> Accept: */*
> Content-Length: 31
> Content-Type: application/x-www-form-urlencoded
> 
* upload completely sent off: 31 out of 31 bytes
< HTTP/1.1 200 OK
< Date: Sat, 05 Sep 2020 07:32:41 GMT
< Server: WSGIServer/0.2 CPython/3.8.3
< Content-Type: application/json
< Vary: Accept, Cookie
< Allow: PUT, OPTIONS
< X-Frame-Options: DENY
< Content-Length: 144
< X-Content-Type-Options: nosniff
< Referrer-Policy: same-origin
< Set-Cookie:  csrftoken=EIe0p13dVAPfWFUofBy1aS3ueBAE6aBvjjwSWdmav0nYMg1UqeYo7E3wvHMJcHam; expires=Sat, 04 Sep 2021 07:32:41 GMT; Max-Age=31449600; Path=/; SameSite=Lax
< Set-Cookie:  sessionid=6mv2rbiv47u52t1gfcgww3jfmy651fux; expires=Sat, 19 Sep 2020 07:32:41 GMT; HttpOnly; Max-Age=1209600; Path=/; SameSite=Lax
< 
* Connection #0 to host localhost left intact
{"id":2,"username":"davin111","email":"[email protected]","last_login":"2020-09-05T07:32:41.807337Z","date_joined":"2020-09-05T05:19:32.755492Z"}* Closing connection 0
*****ui-MacBookPro:~ $ 

>로 시작하는 부분이 제가 curl을 이용해 보낸(당연히 Postman으로 보내더라도 내부적으로 같습니다) HTTP request, <로 시작하는 부분이 로컬에 실행되고 있는 waffle_backend 서버로부터의 HTTP response라고 할 수 있습니다. 정말 별 게 아니고, 이전에 제가 backend 세미나에서 언급했듯 그냥 정말 text구나, 할 수 있습니다. 당연히 text이고, 그러니까 HyperText 어쩌고인 HTTP입니다.


요청 라인

request와 response가 다른 것은 요청 라인 정도뿐입니다. request는 PUT /api/v1/user/login/ HTTP/1.1이고, 여기에 method, endpoint, 그리고 HTTP 버전이 표시되어있습니다. response는 HTTP/1.1 200 OK이고, HTTP 버전과 status code(200)에 대한 정보가 있습니다.


header

그리고 그 아래부터 request의 경우 Content-Type에 대한 정보까지가 header, response의 경우 Set-Cookie까지가 header입니다. 단순한 형식으로, 뭔가 메타 정보스러운 것들을 갖고 있구나, 라고 보시면 될 거 같습니다.

특히 흥미로운 것은 Content-TypeContent-Length인데요, 이것은 HTTP 구조상 뒤이어질 body가 어떤 형식으로 얼마나의 분량을 갖고 있는지 알려줍니다. 때문에 네트워크 통신을 하는 주체들은, 앞 부분에 있는 header를 보고 뒤어어질 body를 어떤 형식으로 얼만큼 받아들여 해석해야할지를 알게 되는 것이지요. 이 경우 request의 Content-Length는 31인데, 일일이 username=davin111&password=test를 세보시면 정말 31글자인 것을 확인할 수 있습니다.(옛날 옛적에는 이런 길이도 줄여서 최적화를 하려고 했대요, 요즘 것들은 이런 거 귀한 것도 모르고 막 쓰지만~ 🤣) Postman에서 'Hide auto-generated headers' 옵션을 풀면 아래와 같이 뭔가 자동으로 매번 생성되어 가고 있던 것을 확인할 수 있는데, 이것이 이 부분입니다.

스크린샷 2020-09-05 16 57 19


body

response도 마찬가지입니다. backend 세미나 중 몇 번이나 언급된 JSON 형식의 body가 갈 것이라는 예고를 하는 Content-Type을 볼 수 있고, Content-Length로 해당 길이를 알려줍니다.

그리고 약간의 공백 후 request와 response 모두 body가 이어지는 구조인데요, request의 body의 경우는 지금 curl 옵션에서는 표시되지 않고 있습니다. 실제로는 username=davin111&password=test가 공백 후 이어져서 전달됩니다.

response의 body는 {"id":2,"username":"davin111","email":"[email protected]","last_login":"2020-09-05T07:48:31.012940Z","date_joined":"2020-09-05T05:19:32.755492Z"}으로, Postman의 body에서 'Raw'를 택하면 이것과 똑같이 볼 수 있습니다.

스크린샷 2020-09-05 17 01 40


결론

서론이 엄청나게 길었는데요, 결론은 간단합니다. 이미 눈치채셨겠지만, response의 header 중 Set-Cookie가 있고, 거기에 Postman 하단의 response의 Cookies들에서 보이던 값들이 그대로 있네요. 그냥 Postman은 역시 예상대로, header의 값들에서 cookie에 해당하는 내용은 중요하니까 따로 빼서 표시해놓는 것입니다. 이거 Cookie로 set해둬! 라고 온, 비교적 좀 더 적극적인 의미를 갖는 header니까요. https://www.toolsqa.com/postman/cookies-in-postman/ 여기서도 대략 그런 내용을 찾을 수 있네요.

다들 한 번씩 로컬에서 curl을 사용하여, API를 보내보는 시도를 해보면 좋겠네요!

davin111 avatar Sep 05 '20 08:09 davin111