HTTP의 구조 및 핵심 요소
16 Nov 2021
깔끔한 파이썬 탄탄한 백엔드 chapter 4장을 참고하였습니다.
프론트엔드 시스템과 백엔드 API 시스템은 일반적으로 HTTP 프로토콜을 기반으로 통신합니다. 4장에서는 HTTP의 구조 및 백엔드 API 시스템 개발에 필요한 핵심 요소들에 대해 배웠습니다.
다음 리스트가 그에 대한 내용입니다.
- HTTP 핵심요소
- HTTP 구조
- 자주 사용되는 HTTP method와 Status Code
HTTP와 HTTP 통신방식
HTTP란? HyperText Transfer Protocol의 약자로서, 웹상에서 서로 다른 서버간에 하이퍼텍스트 문서, 즉 HTML을 서로 주고 받을 수 있도록 만들어진 프로토콜 통신 규약입니다. 서버 간의 통신에서 서로 이해할 수 있는 공통의 통신 형식을 프로토콜이라고 하고, 가장 널리 사용되는 프로토콜이 HTTP입니다.
HTTP 통신 방식에는 2가지 특징이 있습니다. 첫번째는 HTTP의 요청(request)과 응답(response) 방식이고, 두번쨰는 statelesss입니다.
HTTP의 요청(request)과 응답(response)
HTTP 프로토콜은 기본적으로 요청과 응답의 구조로 구성되어 있습니다. 클라이언트가 먼저 HTTP 요청을 서버에 보내면 서버는 요청을 처리한 후 결과에 따른 HTTP 응답을 클라이언트에게 보냄으로써 하나의 HTTP 통신이 됩니다. 3장에서 구현했던 ping - pong api에서 요청과 응답을 살펴 봅시다.
-
다음 코드는 ping 엔드포인트를 구현한 코드입니다.
@app.route("/ping",methods=['GET']) def ping(): return "pong"
ping 엔드포인트 또한 HTTP 요청과 응답이 오고가는 구조 입니다. HTTP 요청은 “/ping” 주소에 GET 요청을 보내는 것이고, HTTP 응답으로 200 OK 상태코드와 함께 “pong”이라는 텍스트를 보냅니다. Flask가 자동으로 HTTP 부분을 자동으로 처리해 주기 때문에 상태코드 및 HTTP 요소가 보이지 않습니다. (그래서 Flask를 사용하면 일반 함수를 사용구현하듯이 엔드포인트를 구현할 수 있습니다.)
Stateless
HTTP 통신은 statelsess입니다. 클라이언트와 서버가 HTTP 통신을 여러번 주고 받을때, 각 통신은 연결되어 있지 않고 독립적입니다. 이전에 처리된 HTTP 통신에 대해서는 전혀 알지 못합니다. 그래서 HTTP 프로토콜은 stateless라고 합니다.
HTTP 프로토콜이 statless이기 때문에 서버 디자인이 간단해집니다. HTTP 통신들의 상태를을 저장할 필요가 없고, 여러 다른 HTTP 통신 간의 진행이나 연결 상태의 처리,저장을 구현 및 관리하지 않아도 됩니다. 오직 각각의 HTTP 요청에 대해 독립적으로 응답만 보내주면 됩니다.
단점은 statless이기 때문에 HTTP 요청을 보낼때 요청을 처리하기 위해 필요한 모든 데이터를 매번 포함시켜서 요청을 보내야 합니다.
예를들어 어떠한 HTTP 요청을 처리하기 위해서는 사용자가 로그인이 되어야 한다고 가정해 봅시다. 이전 통신을 통해 로그인을 했다고 해봅시다. HTTP는 statless이기 때문에 새로 보내는 HTTP 통신에서는 로그인 여부를 알지 못합니다. 클라이언트가 로그인 사실 여부를 포함시켜서 새로운 HTTP 요청을 보내기 위해서는 로그인 사실여부를 기억하고 있어야 합니다.
이러한 점을 해결하기 위해 쿠키와 세션등이 있고, 이를 사용하여 HTTP 요청의 처리에 필요한 진행 과정이나 데이터를 저장합니다. 위의 예시에서는 클라이언트의 쿠키에 로그인 여부에 대한 정보가 저장되어 있습니다.
- 쿠키(cookie) : 웹 브라우저가 웹사이트에서 보내온 정보를 저장할 수 있도록 하는 조그만한 파일을 말합니다. 클라이언트가 필요한 정보를 포함해서 http 요청을 보내기 위해 저장하는 파일을 의미합니다.
- 세션(session) : 쿠기와의 차이점은 쿠키는 웹브라우저, 즉 클라이언트 측의 데이터를 저장하고 세션은 웹서버에서 데이터를 저장합니다.
HTTP 요청 구조
HTTP통신은 요청과 응답으로 이루어져 있으므로, 요청과 응답의 구조에 대해 알아봅시다. 브라우저에서 inspect의 network에 들어가면 request, response header를 볼 수 있습니다.
request/response message를 보는 방법은 브라우저의 inspect에서 network를 통해 볼수도 있고, curl -v
command를 터미널에 입력하면 볼 수 있습니다. 아래 요청 메시지는 curl -v [changhyeonnam.github.io/about.html](http://changhyeonnam.github.io/about.html)
을 터미널에 쳤을때 나오는 메시지 입니다. curl
server로 직접 데이터를 받거나 보낼때 사용하는 명령어로, -v option은 verbose 모드를 의미합니다.
* Trying 185.199.111.153...
* TCP_NODELAY set
* Connected to changhyeonnam.github.io (185.199.111.153) port 80 (#0)
> GET /about.html HTTP/1.1
> Host: changhyeonnam.github.io
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 301 Moved Permanently
< Server: GitHub.com
< Content-Type: text/html
< permissions-policy: interest-cohort=()
< Location: https://changhyeonnam.github.io/about.html
< X-GitHub-Request-Id: 42F4:7EBF:1B9D5:2D5AC:619372F9
< Content-Length: 162
< Accept-Ranges: bytes
< Date: Tue, 16 Nov 2021 08:59:37 GMT
< Via: 1.1 varnish
< Age: 0
< Connection: keep-alive
< X-Served-By: cache-icn1450099-ICN
< X-Cache: MISS
< X-Cache-Hits: 0
< X-Timer: S1637053178.804846,VS0,VE181
< Vary: Accept-Encoding
< X-Fastly-Request-ID: 656aa70ad6c1969dee843416e10b2518dff0608a
<
<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx</center>
</body>
</html>
* Connection #0 to host changhyeonnam.github.io left intact
* Closing connection
HTTP 요청 구조
TCP 연결한것과 네이버 서버이므로 포트 80번에 연결 한것을 출력하고 나서 HTTP request message를 출력합니다.
> GET /about.html HTTP/1.1
> Host: changhyeonnam.github.io
> User-Agent: curl/7.64.1
> Accept: */*
HTTP 요청메시지는 위와 같고, 크게 세 부분 (1) Start line (2) Header (3) Body로 구성 됩니다.
-
Start line
GET / HTTP/1.1
http 메서드, request target, http version으로 구성됩니다.
-
Header
> Host: changhyeonnam.github.io > User-Agent: curl/7.64.1 > Accept: */*
- host : host url을 의미합니다.
- User-Agent : 클라이언트 정보를 의미 합니다. 웹 브라우저 정보 등이 포함됩니다.
- Accept : 서버에서 해당 타입에 대한 데이터를 보내달라고 요청하는 헤더 입니다.
- connection : 해당 요청이 끝난 후에 클라이언트와 서버가 계속해서 네트워크 연결을 유지할지(keep-alive), 끊을지(close)에 대한 헤더입니다.
- content type, content-length 등이 있습니다.
-
Body
- HTTP 요청이 전송하는 데이터를 담고 있는 부분으로, 위의 요청은 GET이므로 body가 비여있습니다.
HTTP 응답 구조
< HTTP/1.1 301 Moved Permanently
< Server: GitHub.com
< Content-Type: text/html
< permissions-policy: interest-cohort=()
< Location: https://changhyeonnam.github.io/about.html
< X-GitHub-Request-Id: 42F4:7EBF:1B9D5:2D5AC:619372F9
< Content-Length: 162
< Accept-Ranges: bytes
< Date: Tue, 16 Nov 2021 08:59:37 GMT
< Via: 1.1 varnish
< Age: 0
< Connection: keep-alive
< X-Served-By: cache-icn1450099-ICN
< X-Cache: MISS
< X-Cache-Hits: 0
< X-Timer: S1637053178.804846,VS0,VE181
< Vary: Accept-Encoding
< X-Fastly-Request-ID: 656aa70ad6c1969dee843416e10b2518dff0608a
<
<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx</center>
</body>
</html>
* Connection #0 to host changhyeonnam.github.io left intact
* Closing connection
HTTP 응답 메시지는 위와 같고 세가지 부분 (1) Status Line (2) Header (3) Body로 이루어져 있습니다.
-
Status Line
HTTP/1.1 301 Moved Permanently
(HTTP 301 : 영구적인 URL redirection을 위해 사용되는 응답 상태 코드입니다.)
Status Line은 (1) HTTP version (2) Status Code (3) Status Text로 이루어져 있습니다.
-
Header
< Server: GitHub.com < Content-Type: text/html < permissions-policy: interest-cohort=() < Location: https://changhyeonnam.github.io/about.html < X-GitHub-Request-Id: 42F4:7EBF:1B9D5:2D5AC:619372F9 < Content-Length: 162 < Accept-Ranges: bytes < Date: Tue, 16 Nov 2021 08:59:37 GMT < Via: 1.1 varnish < Age: 0 < Connection: keep-alive < X-Served-By: cache-icn1450099-ICN < X-Cache: MISS < X-Cache-Hits: 0 < X-Timer: S1637053178.804846,VS0,VE181 < Vary: Accept-Encoding < X-Fastly-Request-ID: 656aa70ad6c1969dee843416e10b2518dff0608a <
HTTP 응답의 헤더는 HTTP 요청의 헤더와 대부분 동일합니다. User - agent 대신 Server 헤더가 사용됩니다.
-
Body
<html> <head><title>301 Moved Permanently</title></head> <body> <center><h1>301 Moved Permanently</h1></center> <hr><center>nginx</center> </body> </html>
자주 사용되는 HTTP 메소드
HTTP 메서드는 HTTP 요청이 의도하는 action을 정의하는 부분이고, API를 개발하는데 있어서 HTTP 메서드를 잘 이해하고 적절한 HTTP 메서드를 사용하는것이 중요합니다.
-
GET
POST와 함께 가장 자주 사용되는 메서드로, 어떤 데이터를 서버로 요청(GET)할때 사용되는 메서드입니다. 데이터의 생성, 수정, 삭제 등의 변경 사항 없이 단순히 데이터를 받아오는 요청이 주로 GET 메서드로 요청됩니다. 데이터를 받아올때 사용되기 때문에 위의 request message에서 본것처럼 요청 BODY가 비어있는 경우가 많습니다.
-
POST
GET과 다르게 데이터를 생성, 수정, 삭제 요청을 할 때 주로 사용되는 HTTP 메서드 입니다.
-
OPTIONS
OPTIONS 메서드는 특정 엔드포인트에서 허용하는 메서드들이 무엇이 있는지 알고자 하는 요청에 사용되는 메서드 입니다.
http -v OPTIONS http://127.0.0.1:5000/ping HTTP/1.0 200 OK Allow: OPTIONS, HEAD, GET Content-Length: 0 Content-Type: text/html; charset=utf-8 Date: Tue, 16 Nov 2021 09:43:29 GMT Server: Werkzeug/2.0.2 Python/3.8.7
앞선 3장의 ping endpoint에 OPTIONS 요청을 보내면 Allow에 허용하는 메서드들을 출력합니다.
만약 엔드포인트에서 허용하지 않는 HTTP 메서드 요청이 들어오면 405 Method Not Allowed 응답을 보냅니다.
http -v POST http://127.0.0.1:5000/ping HTTP/1.0 405 METHOD NOT ALLOWED Allow: OPTIONS, HEAD, GET Content-Length: 178 Content-Type: text/html; charset=utf-8 Date: Tue, 16 Nov 2021 09:47:27 GMT Server: Werkzeug/2.0.2 Python/3.8.7
-
PUT
POST 메서드와 비슷한 의미를 갖는 메서드 입니다. 데이터를 새로 생성할때 사용되는 메서드로, POST와 중복되기 때문에 모든 데이터 생성 및 수정과 관련한 요청은 POST를 통일하는 시스템이 많습니다.
-
DELETE
삭제 요청을 보낼때 사용하는 메서드로 , PUT과 마찬가지로 POST에 밀려 잘 사용되지 않습니다.
자주 사용되는 HTTP STatus Code와 Text
-
200 OK
가장 자주보게 되는 status code. HTTP 요청이 문제없이 성공적으로 잘 처리 되었을때 보내는 상태 코드입니다.
-
301 Moved Permanently
HTTP 요청을 보낸 엔드포인트의 URL 주소가 바뀌었다는 것을 나타내는 status code. 301 status code의 HTTP 응답에는 해당 엔드포인트의 새로운 주소를 나타내는 Location 헤더가 포함되어 나옵니다. 301 요청을 받은 클라이언트는 Location 헤더의 엔드포인트의 새로운 주소에 해당 요청을 다시 보내게 되고, 이 과정을 redirection 라고 합니다.
< HTTP/1.1 301 Moved Permanently < Server: GitHub.com < Content-Type: text/html < permissions-policy: interest-cohort=() < Location: https://changhyeonnam.github.io/about.html
위에서 사용한 응답메시지에서도 볼수 있습니다.
-
400 Bad Request
HTTP 요청이 잘못된 요청일때 보내는 응답 코드입니다. 주로 요청으로 잘못된 input 값들이 보내졌을때 사용됩니다.
-
401 Unauthorized
HTTP 요청을 처리하기 위해서 해당 요청을 보내는 클라이언트의 신분(credential) 확인이 요구되나 확인할 수 없을때 보내는 응답 코드입니다. 로그인이 필요한 경우 주로 401 응답을 보냅니다.
-
403 Forbidden
HTTP 요청을 보내는 주체가 해당 요청에 대한 권한이 없음을 나타내는 응답 코드입니다.
-
404 Not Found
HTTP 요청을 보내고자 하는 URI가 존재하지 않을 때 보내는 응답 코드입니다. “해당 페이지를 찾을 수 없습니다” 의 메시지에 대응되는 응답코드 입니다.
-
500 Internal Server Error
내부 서버 오류가 발생했다는 것을 알려주는 응답 코드 입니다. HTTP 요청을 받은 서버에서 해당 요청을 처리하는 과정에서 서버오류가 난 경우 사용하는 응답코드입니다.
API 엔드포인트 아키텍처 패턴
크게 2가지 방식이 있는데, 하나는 REST 방식이고, 하나는 GraphQL 방식 입니다. REST 방식은 가장 널리 사용되는 API 엔드포인트 아키텍처 패턴입니다. GraphQL은 페이스북에서 개발한 기술이며, 최근에 나온 기술 입니다.
RESTful API
RESTful HTTP API는 API 시스템을 구현하기 위한 아키텍쳐의 한 형식 입니다. RESTful API는 전송하는 리소스를 URI로 표현하고, 해당 리소스에대한 action을 HTTP method로 정의하는 방식입니다. 다음은 블로그 포스트 에서 사용한 REST 코드입니다.
-
REST API server가 따로 없기 때문에 다음 링크 에서 URI를 활용하여 사용합니다.
import requests url = 'https://jsonplaceholder.typicode.com/todos/1' response = requests.request("GET",url,headers={},data={}) print(response.status_code) # 200 data = response.content print(data) print(type(data)) # b'{\n "userId": 1,\n "id": 1,\n "title": "delectus aut autem",\n "completed": false\n}' # <class 'bytes'>
-
data가 위와 같이 바이트 형태이고, json 형태로 받기 위해서 json 패키지를 사용하여 변환합니다. json는 간단하게 말하면 dict과 list로 이뤄져있는 데이터 포맷이라고 생각하시면 됩니다.
import json data = json.loads(response.content) print(data) print(type(data)) # {'userId': 1, 'id': 1, 'title': 'delectus aut autem', 'completed': False} # <class 'dict'> for k,v in data.items(): print(f"key:{k}, value:{v}") # key:userId, value:1 # key:id, value:1 # key:title, value:delectus aut autem # key:completed, value:False
RESTful API의 장점은 self-descriptiveness로, 엔드포인트의 구조만 보더라도 해당 엔드포인트가 제공하는 리소스의 기능을 파악할 수 있습니다. 구조가 직관적이고 훨씬 간단합니다.
GraphQL
REST 방식의 구조적인 문제가 발생하여 페이스북에서 만든 방식 입니다. API의 구조가 특정 클라이언트에 맞추어져서 다른 클라이언트에서 사용하기 적합하지 않게 되는 문제가 생깁니다.
GraphQL은 REST 방식의 API와 다르게 엔드포인트가 하나입니다. 엔드포인트에 클라이언트가 필요 한것을 정의해서 요청하는 방식 입니다.
다음 kakao tech 블로그 링크 에서 GrpahQL에 더 자세한 내용을 볼 수 있습니다.
다음에 시간이 나면 Day1, 2-2. 그런 REST API로 괜찮은가 conference 발표를 정리해서 포스트를 작성해보겟습니다.