TIL - http와 Request/Response message

solee·2022년 2월 24일
0

TIL

목록 보기
4/20
post-thumbnail

httpie로 여러 요청을 보내 코드를 테스트하다 보니, 이게 뭔지 궁금해진다.

이거:

이제껏 이 메시지들을 수백번(그래... 이건 좀 과장이다) 봐오면서 신경쓴 건 맨 위의 POST와 잘 들어간 데이터, 그리고 200 OK와 잘 돌아온 응답뿐이었다. 지겹도록 봐 오던 http를 막상 잘 모른다는 생각이 들어서 좀 자세히 살펴보려고 한다.




HTTP

먼저 http가 뭐 하는 놈인지부터 다시 살펴보자. 인터넷을 써 온 수많은 세월 덕에 지겹도록 익숙한 http는 Hyper Text Transfer Protocol의 약어다. 인터넷에서 클라이언트와 서버가 통신할 때 사용하는 프로토콜로, 말하자면 데이터를 주고받기 위한 통신 규약이다.

내가 설명하고자 하는 http의 특징을 꼽아 보자면 다음과 같다.

  • stateless
  • Request/Response



  • stateless

stateless는 그냥 의미만 따지면 "상태가 없는, 나라가 없는, 위엄을 잃은..." 이라는 뜻이다. http에 대해서는 다른 의미로 쓰인다. stateless하다는 것은 서버에 클라이언트의 상태state를 보존하지 않는다는 뜻이다. 풀어서 설명하면 다음과 같다.

클라이언트: (로그인 정보) 로그인했어요!
서버: 확인했어요. 인증된 사용자군요. 

클라이언트: (로그인된 상태)(수정할 내용) 회원정보를 수정할래요!
서버: 인가받지 않은 사용자군요. 불가능해요.

클라이언트: ???네?

클라이언트가 아무리 직전에 로그인했어도 서버가 이 클라이언트가 로그인했다는 상태를 보존하지 않기 때문에 서버에게 해당 클라이언트는 언제나 새롭고 짜릿하다.

그러면 어떻게 하라고?

그래서 token(auth)을 이용한다. 이전 글에서 로그인 로직을 짤 때도 token을 받아 자신의 서버에서 인가받은 사용자인지 검증하고, user_id를 확인한 후 해당 id를 이용해 어떤 유저인지 파악했던 것을 기억할 것이다. 이 token을 매 요청마다 서버에게 보냄으로써 클라이언트는 자신이 인가받은 사용자임을 매번 새롭게 인증한다. 풀어서 설명하면 다음과 같다.

클라이언트: (로그인 정보) 로그인했어요!
서버: 확인했어요. 인증된 사용자군요. (토큰 응답) 

클라이언트: (로그인된 상태)(수정할 내용)(토큰 전송) 회원정보를 수정할래요!
서버: (토큰 검증) 확인했어요. 인가받은 사용자군요. 회원정보를 수정했어요. (성공 응답 전송)

클라이언트: (로그인된 상태)(1:1문의 게시판 데이터 요청)(토큰 전송) 1:1문의 게시판 답장을 보내주세요!
서버: (토큰 검증) 확인했어요. 인가받은 사용자군요. (해당하는 데이터 전송)

매 요청마다 클라이언트는 토큰을 전송해 인가받았음을 검증하고, 서버는 매 요청마다 검사한다. 그러므로 프론트단에서 이런 로그인 정보 등을 계속해서 가지고 있으며 전송하고 로그아웃할 때 삭제하거나 한다.

이렇게 서버가 매번 검증한다는 것은, 서버가 달라져도 괜찮다는 뜻이다. 상태를 보존하는 경우(stateful)라면 해당 정보를 가진 서버가 아닐 경우, 즉 같은 서버가 아닐 경우 문제가 생기기 때문이다. 반대로 상태를 보존하기 위해(예를 들면 로그인 정보를 유지하기 위해) 쿠키나 세션 등에 데이터를 계속해서 가지고 있어야 한다.



  • Request/Response

요청과 응답이다. 그런데 우리는 이것을 이미 알고 있다! 당장 위에 쓴 예시만 보아도 그렇다. 클라이언트가 꾸준히 서버에게 요청하면, 서버는 응답한다. 이게 바로 Request/Response다. 아까 맨 처음의 이미지만 보아도 그렇다.

이거:

그럼 이제 딱 보면 감이 온다. 윗부분이 request고 아랫분이 response다. 로그인 정보(이메일과 비밀번호)를 가지고 요청을 보내면 서버가 확인 후 응답한다.

이제 이 친구들을 조각조각 뜯어내 보자!




해체 쇼

POST /users/login HTTP/1.1
Accept: application/json, */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 52
Content-Type: application/json
Host: 127.0.0.1:8000
User-Agent: HTTPie/1.0.3

{
	(요청한 데이터)
}

HTTP/1.1 200 OK
Content-Length: 135
Content-Type: application/json
Cross-Origin-Opener-Policy: same-origin
Date: Thu, 24 Feb 2022 06:46:57 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.9.7
Vary: Origin
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

{
	(응답받은 데이터)
}

이 알 수 없는 데이터들은 꼭 곤충처럼 머리 가슴 배로 나눌 수 있다.



클라이언트의 Request

POST /users/login HTTP/1.1
Accept: application/json, */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 52
Content-Type: application/json
Host: 127.0.0.1:8000
User-Agent: HTTPie/1.0.3

{
	(요청한 데이터)
}

머리 가슴 배.

  1. start line
  2. headers
  3. body

3번이지만 익숙한 body를 보자! django에서 data = json.loads(request.body) 해줬던 것을 기억하는가?
클라이언트가 서버에 데이터를 전송하고 요청할 때 request의 body에서 데이터를 꺼내와 dictionary로 변환해 사용했다. 이 body가 바로 이 body다. 그러니까 {} 안에 든 (요청한 데이터)부분이 바로 body인 것이다.

남은 start line과 headers를 보자.



  • Start Line

Request를 머리 가슴 배로 나눈 것처럼, start line도 그렇다.

POST /users/login HTTP/1.1

세 부분으로 나누어 보자.

POST - HTTP Method - 말 그대로 요청이 어떤 method를 사용하는지 정의한다. GET, POST, DELETE가 많이 쓰이고, 그 외에도 여러 method가 있다.

/users/login - Request target - 요청의 목표. url을 정의함으로써 어떤 페이지에 요청하는지를 선언한다.

HTTP/1.1 - HTTP Version - http의 버전인데, 1999년에 발표된 1.1이 널리 쓰인다.

그러므로 이 start line은 이렇게 해석할 수 있다:

/users/login이라는 url에 GET메서드 요청을 http 1.1버전으로 전송한다.



  • headers

header에는 메타 데이터(데이터를 위한 데이터)를 담는다. 찾아보니 다양한 종류의 키가 있는데, 내 요청에 나온 친구들만 분석해 보겠다.

Accept: application/json, */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 52
Content-Type: application/json
Host: 127.0.0.1:8000
User-Agent: HTTPie/1.0.3

key와 value인 json=dict 형식을 가지는데, 위에서부터 차례로 읽어 보자.

Accept - 서버에게 요청하는 데이터의 형식을 명시한다. 아래의 Accept-Encoding같이 비슷한 역할을 하는 키가 많다. Accept는 json형식을 받겠다는 명시고, Accept-Encoding은 gzip과 deflate로 인코딩(이 알고리즘 방식으로 데이터를 압축)해서 응답해 달라는 명시다.

Connection - 이것은 http2에서는 사라진 키라고 한다. 기본적으로 keep-alive라는 값을 가진다.

Content-Length - 데이터 본문의 크기를 명시한다. 메시지의 크기에 따라 설정된다.

Content-Type - 현재 전송하는 메시지의 내용이 json형식을 가진다고 명시한다.

Host - 포트를 포함한 서버의 도메인을 명시한다.

User-Agent - 클라이언트가 어떤 방식으로 요청했는지 명시한다.




서버의 Response

HTTP/1.1 200 OK
Content-Length: 135
Content-Type: application/json
Cross-Origin-Opener-Policy: same-origin
Date: Thu, 24 Feb 2022 06:46:57 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.9.7
Vary: Origin
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

{
	(응답받은 데이터)
}

Response도 마찬가지로 세 부분으로 나누어진다.

  1. start line
  2. headers
  3. body

마찬가지로 {} 안의 (응답받은 데이터) 부분이 body다.



  • start line
HTTP/1.1 200 OK

또다시 세 부분으로 나누어 보자.

HTTP/1.1 - HTTP Version - http의 버전.

200 - Status Code - 상태 코드. 이 코드는 400, 404, 500 등이 될 수 있으며 응답의 상태를 선언한다.

OK - Status Text - 상태를 설명해주는 텍스트. 이외에도 SUCCESS나 NOT FOUND 등이 도리 수 있다.

그러므로 이 start line은 이렇게 해석할 수 있다:

HTTP/1.1 버전 서버는 요청을 성공적으로 처리했으며 상태 200으로 응답한다.



  • headers
Content-Length: 135
Content-Type: application/json
Cross-Origin-Opener-Policy: same-origin
Date: Thu, 24 Feb 2022 06:46:57 GMT
Referrer-Policy: same-origin
Server: WSGIServer/0.2 CPython/3.9.7
Vary: Origin
X-Content-Type-Options: nosniff
X-Frame-Options: DENY

전반적으로는 동일하게 메타 데이터를 담고 있지만, response에만 사용되는 정보들도 있다. 위에서 설명한 것들은 넘어간다.

Cross-Origin-Opener-Policy - 줄여서 COOP라고도 불린다. 이 헤더를 사용하면 최상위 문서가 다른 문서와 브라우징 컨텍스트 그룹을 공유하지 않는다고 한다. 프로세스를 격리하므로 팝업으로 열렸을 때 XS-Leaks라는 cross-origin 공격을 방지할 수 있다. 새 창에서 열릴 때에는 문서에 대한 참조가 없기 때문에 참조를 제어할 수 있다. same-origin에 대해서는 아래에 설명을 따로 남기겠다.

Date - 응답하는 날짜와 시간을 표기한다.

Referrer-Policy - Referrer란 사이트에 남은 방문 흔적을 말한다. 내가 구글에서 A라는 사이트에 들어갔다면, A사이트에서는 구글에서 온 방문 흔적을 알 수 있다. 이 키를 통해 Referrer가 얼마나 전달되게 할지 결정할 수 있다.

Server - 요청을 처리하기 위한 원래 서버의 소프트웨어 정보를 가진다.

Vary - 이후 이루어지는 요청들에 대해 새로운 헤더를 요청하는 대신 캐시된 응답을 사용할 수 있는지를 결정한다. 서버의 리소스 선택에 대한 것이다.


X가 붙은 경우는 사용자가 직접 만든 것이며 2012년 이후로는 사용되지 않는다고 들었는데, 이것들과는 다른 모양이다.

X-Content-Type-Options - XSS, 즉 크로스 사이트 스크립트나 스니핑을 방지하기 위한 것으로 Content-Type 유형이 브라우저에 의해 변경되지 않도록 지정한다.

X-Frame-Options - 요청을 보낸 페이지를 <frame>, <iframe>, <object>로 렌더링할 수 있는지 여부를 나타내고, 다른 사이트에 해당 사이트의 콘텐츠들이 포함되지 않도록 방지한다. 클라이언트가 접근한 브라우저가 해당 헤더를 지원할 때에만 적용된다.


위에서 언급한 Cross-Origin-Opener-PolicyReferrer-Policy의 값으로 나오는 same-origin이란, 원본과 동일하다는 것으로 원본을 따르거나 원본이 같을 때에만 흔적을 남기는 등으로 쓰인다.




profile
DA DA DA

0개의 댓글