본문 바로가기

[네트워크] HTTP 이해하기

들어가며

개발자가 되기 위해서는 정말 다양한 지식을 알아야 합니다. 그중에서도, 네트워크는 필수적으로 알아야 하는 분야라고 생각했지만, 막상 네트워크를 제대로 공부한 적은 드물었습니다. 이번 기회에 네트워크에 대해 조금씩이라도 공부하면서 개발자로서의 기본을 쌓기 위해 노력하고 싶습니다. 아래 내용은 인프런의 모든 개발자를 위한 HTTP 웹 기본 지식 강의를 공부하며 작성했습니다. 아래 내용을 보다 자세히 알고 싶으신 분들은 링크를 참고해주세요.

 

 

 

 

 


 

 

 

 

HTTP

Hyper Text Transfer Protocol의 약자로 인터넷에서 데이터를 주고받을 수 있는 프로토콜입니다. 프로토콜이라는 것은 하나의 규칙이기 때문에 프로그램은 이 규칙에 맞춰 서로 정보를 교환할 수 있게 됐습니다. 주로 사용하는 HTTP/1.1은 TCP를 기반으로 동작하고, HTTP/2 또한 TCP 기반으로 동작합니다. HTTP/3는 UDP를 기반으로 동작합니다. 

 

이런 HTTP는 크게 4가지 특징이 있습니다.

 

  • 클라이언트 / 서버의 구조
  • 무상태(Stateless) 
  • 비연결성
  • 단순하며, 확장이 용이하다

 

 

여기서 무상태와 비연결성에 대해 좀 더 자세히 알아보겠습니다.

 

 

Stateless

Stateless의 특성은, 응답 서버를 쉽게 바꿀 수 있기 때문에 무한한 서버 증설이 가능합니다. 하지만 stateful의 경우 항상 같은 서버가 유지되어야 합니다. 하지만 상태를 유지해야 하는 상황이 있는데, 이게 바로 로그인입니다. 로그인한 사용자의 경우 로그인했다는 상태를 서버에 유지해야 합니다. 일반적으로 브라우저 쿠키와 서버 세션 등을 사용해서 상태를 유지합니다. 

 

비연결성

HTTP는 기본이 연결을 유지하지 않는 모델입니다. 만약 연결을 모두 하고 있다면, 서버의 자원은 효율적으로 사용하지 못할 것입니다. 하지만 비연결성의 한계는 TCP/IP 연결을 새로 맺어야 한다는 것입니다. 즉 3 way handshake 시간이 추가됩니다. 지금은 HTTP 지속 연결(Persistent Connections)로 문제를 해결합니다. 그리고 HTTP/2, HTTP/3에서 더 많은 최적화가 됐습니다. 그럼 HTTP의 메서드, 상태 코드, 헤더 등의 내용을 알아보면서, HTTP를 조금 더 자세하게 알아보겠습니다.

 

 

 

 

 

 


 

 

HTTP 메서드 속성

HTTP API를 만들 때, URI와 HTTP 메서드를 활용해서 설계하곤 합니다. 이때 GET, POST, PUT, DELETE 등의 HTTP 메서드를 활용해서 API를 설계합니다. 이때 HTTP 메서드에도 각각의 속성이 있다는 것을 알아야 합니다. 그럼 HTTP 메서드에 어떤 속성들이 있는지 알아보겠습니다.

 

 

 

출처: https://ko.wikipedia.org/wiki/HTTP

 

안전

안전이라는 속성은, API를 호출해도 리소스를 변경하지 않는다는 의미입니다. GET 메서드를 활용한다면, 리소스를 조회하는 메서드이기에, 리소스를 변경하지는 않기에 안전하다는 속성이 있습니다. 

 

 

멱등

멱등이라는 속성은 한 번 호출하든, 여러 번 호출하든 결과는 똑같다는 의미입니다. 예를 들어 GET 메서드를 활용한다면, 한 번 혹은 여러 번 조회하더라도 같은 결과를 조회할 수 있습니다. 또한 PUT 메서드는 결과를 대체합니다. 따라서 같은 요청을 여러 번 해도 최종 결과는 같습니다. DELETE 메서드는 결과를 삭제합니다. 그래서 같은 요청을 여러 번 해도 삭제된 결과는 같습니다. 하지만 POST 메서드의 경우 멱등이 아닙니다. 예를 들어 결제와 관련된 API를 두 번 호출한다면 같은 결제가 중복해서 발생할 수 있습니다. 

 

여기서 재요청 중간에 다른 곳에서 리소스를 변경한다고 해도, 멱등은 외부 요인으로 리소스가 변경되는 것까지 고려하지 않기에, 외부 요인으로 리소스가 변경되더라도 같은 결과를 내보낸다면 멱등하다고 할 수 있습니다.

 

 

캐시 가능

캐시 가능이라는 속성은 응답 결과 리소스를 캐시 해서 사용해도 되는가와 관련 있습니다. GET, POST, PATCH 등의 메서드를 활용한 API는 캐시가 가능합니다. 하지만 실제로는 GET, HEAD 정도만 캐시로 사용한다고 합니다. 

 

그럼 클라이언트에서 서버로 데이터를 전송할 때 어떻게 전송할 수 있는지에 대해 알아보겠습니다.

 

 

 

 

 

 


 

 

데이터 전송

먼저 다양한 방법 중에서 HTML Form을 활용해서 데이터를 전송하는 방법을 알아보겠습니다.

 

 

 

HTML Form 데이터 전송

위와 같은 폼이 있다고 했을 때, 폼에 데이터를 입력하고 log in 버튼을 누르면 다음과 같은 형태로 웹 브라우저가 생성한 요청 HTTP 메시지가 생성될 것입니다.

 

 

POST /save HTTP/1.1
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded

id=test&password=1234

 

 

 

만약 사진 파일 혹은 정적 파일을 서버로 넘겨서 저장시켜주려고 할 땐 multipart/form-data를 활용합니다. multipart/form-data를 활용했을 땐 아래와 같은 HTTP 메시지가 생성됩니다.

 

 

POST /save HTTP/1.1
Host: localhost:8080
Content-Type: multipart/form-data; boundary=-----XXX
Content-Length: 10457
------XXX
Content-Disposition: form-data; name="username"
test
------XXX
Content-Disposition: form-data; name="age"
1234
------XXX
Content-Disposition: form-data; name="file1"; filename="intro.png"
Content-Type: image/png
109238a9o0p3eqwokjasd09ou3oirjwoe9u34ouief...
------XXX--

 

 

HTML Form 데이터 전송 시, Content-Type이 하나는 application/x-www-form-urlencoded이었고, 사진과 함께 전송한다면  Content-Type이 multipart/form-data였습니다. HTML Form 전송은 GET, POST만 지원하는 특징이 있습니다. 

 

application/x-www-form-urlencoded로 오는 요청은 form의 내용을 메시지 바디를 통해 전송할 때, 사용되는 content-type입니다. 전송 데이터를 url encoding 처리합니다. 예를 들어 abc김이라는 데이터를 전달하면 abc%EA%B9%80와 같은 형태로 인코딩합니다. 

 

multipart/form-data는 파일 업로드 같은 바이너리 데이터 전송 시 사용됩니다. 다른 종류의 여러 파일과 폼의 내용을 함께 전송할 수 있습니다. 

 

HTML FORM을 사용하면, GET, POST만 지원하기에, PUT, PATCH 등의 HTTP 메서드를 활용하기엔 제약이 생길 수 있습니다. 이때 데이터를 수정해야 한다면, 컨트롤 URI를 활용해서, POST의 /new, /edit, /delete 등의 URI를 생성하기도 합니다. 

 

 

 

HTTP API 데이터 전송

HTML Form을 활용해서 데이터를 전송하는 방법도 있고, HTTP API를 활용해서 데이터를 전송하는 방법도 있습니다. HTTP API 데이터 전송 방법에는 여러 가지 방법이 있습니다. 서버에서 서버로 데이터를 전송하기도 하고, 앱, 웹 클라이언트가 데이터를 달라고 요청하는 경우도 있습니다. 웹 클라이언트에서는 HTML Form이 아닌, AJAX와 같은 자바스크립트를 통한 통신을 할 때 HTTP API 데이터를 요청하기도 합니다. 이때 사용하는 Content-Type은 application/json을 주로 사용합니다. 

 

 

HTTP/1.1 201 Created
Location: /members/100

 

HTTP API에서 POST방식의 경우 클라이언트는 등록될 리소스의 URI를 모르기에, 서버가 새로 등록된 리소스 URI를 생성해줍니다. 

 

 

 

 

 

 


 

 

 

HTTP 헤더 표현

헤더는 아래와 같이 요청, 응답이 있을 때 요청에는 Host: www.google.com, 응답은 Content-Type과 Content-Length 부분을 헤더라고 합니다. 

 

 

GET /search?q=hello&hl=ko HTTP/1.1
Host: www.google.com
HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8
Content-Length: 3423
<html>
 <body>...</body>
</html>

 

 

헤더는 HTTP 전송에 필요한 모든 부가정보가 담깁니다. 메시지 바디 내용, 메시지 바디 크기, 압축, 인증, 요청 클라이언트, 서버 정보, 캐시 관리 정보 등과 같은 정보들이 담깁니다. 헤더는 메시지 본문에 있는 데이터를 해석할 수 있는 정보를 제공합니다. Content-Type, Content-Encoding, Content-Language, Content-Length 등의 표현 헤더 방식이 있습니다. 

 

Content-Type

Content-Type은 표현 데이터의 형식입니다. 컨텐트 바디에 들어가는 내용이 무엇인지 알려주는 것입니다. 

 

HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8
Content-Length: 3423
<html>
 <body>...</body>
</html>

 

예를 들어 위의 내용은 html이 들어간다는 것입니다. 인코딩과 관련된 정보도 넣어줍니다. 

 

 

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 16
{"data":"hello"}

 

만약 json 형태로 응답이 간다면, application/json이라고 표현합니다.

 

 

Content-Encoding

Content-Encoding은 표현 데이터의 압축 방식입니다.

 

HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8
Content-Encoding: gzip
Content-Length: 521
lkj123kljoiasudlkjaweioluywlnfdo912u34lj
ko98udjkl

 

 

표현 데이터를 압축하기 위해 사용합니다. 데이터를 전달하는 곳에서 메시지 바디 부분을 압축 후에, 압축했으면 서버에서 클라이언트로 보낼 때, 클라이언트가 무엇으로 압축되어있는지 알아야 하기에, 부가정보를 보내주는 것입니다. gzip, deflate, identity 등의 정보가 있습니다.  

 

 

 

Content-Language

Content-Language는 표현 데이터의 자연 언어입니다.

 

 

HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8
Content-Language: ko
Content-Length: 521
<html>
안녕하세요.
</html>

 

예를 들어 Content-Language가 ko면 본문에는 한국어가 들어있구나 알 수 있습니다.

 

 

Content-Length

Content-Length는 표현 데이터의 길이입니다. 

 

HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8
Content-Length: 5
hello

 

 

바이트 단위이며, Transfer-Encoding을 사용하면 Content-Length를 사용하면 안 됩니다. 

 

 

 

 

 

 

 

 


 

 

 

HTTP 헤더 협상

협상은 콘텐츠 네고시에이션이라 하는데, 클라이언트가 서버에게 원하는 표현으로 달라고 요청하는 것입니다. 요청을 할 때 우선순위에 맞는 정보를 달라고 하는 것인데, 이는 아래의 내용을 보면서 살펴보겠습니다.

 

 

클라이언트가 전달하는 협상 헤더는 아래와 같을 수 있습니다.

 

  • Accept: 클라이언트가 선호하는 미디어 타입 전달
  • Accept-Charset: 클라이언트가 선호하는 문자 인코딩
  • Accept-Encoding: 클라이언트가 선호하는 압축 인코딩
  • Accept-Language: 클라이언트가 선호하는 자연 언어

 

 

먼저 Accept-Language에 대해 알아보겠습니다. 예를 들어 클라이언트가 아래와 같이 요청과 응답을 주고받는다고 보겠습니다. 

 

 

// 요청
GET /event

// 응답(다중 언어를 지원하는 서버)
Content-Language: en

hello

 

 

요청을 할 때, 영어로 응답을 하고 있었습니다. 만약 한국어로 응답을 받고 싶다면 다음과 같이 설정할 수 있습니다.

 

 

 

// 요청
GET /event
Accept-Language: ko

// 응답(다중 언어를 지원하는 서버)
Content-Language: ko

안녕하세요

 

 

Accept-Language를 ko로 설정해서 요청한 결과 응답을 한국어로 받을 수 있었습니다. 하지만 아래와 같은 경우도 생길 수 있습니다.

 

 

// 요청
GET /event
Accept-Language: ko

// 응답(독일어, 영어 순으로 우선순위가 있는 다중 언어를 지원하는 서버)
Content-Language: de

Hallo(독일어)

 

 

위에서 한국어로 달라고 요청했지만, 독일어로 응답을 주고 있었습니다. 이는 요청을 받는 서버에서 어떤 언어를 우선순위로 넘겨줄 것이냐에 따라 응답의 방향성이 달라질 수 있다는 것을 뜻합니다. 그럼 요청을 할 때, 우선순위를 지정해서 요청을 해줘야 합니다. 0에서 1까지의 숫자 중, 클수록 높은 우선순위에 해당합니다. 생략하면 1을 뜻합니다. 그럼 아래와 같이 요청을 했을 때 어떤 결과를 얻을 수 있는지 알아보겠습니다.

 

 

// 요청
GET /event
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7

// 응답(독일어, 영어 순으로 우선순위가 있는 다중 언어를 지원하는 서버)
Content-Language: en

Hello(영어)

 

 

위와 같이 설정되어 있다면, 한국어 다음으로 영어가 우선순위이기 때문에 서버에서 응답을 줄 때 독일어가 아닌, 영어를 우선적으로 제공해주는 것을 알 수 있습니다. 이 뿐 아니라 협상에서 구체적으로 작성할수록 우선순위가 높다는 특징도 있습니다.

 

 

 

GET /event
Accept: text/*, text/plain, text/plain;format=flowed, */*

 

 

위와 같은 요청이 있다면, 아래와 같이 우선순위가 지정됩니다.

 

 

1. text/plain;format=flowed

2. text/plain

3. text/*

4. */

 

 

 

 

 


 

 

HTTP 전송 방식

전송방식은 4개(단순 전송, 압축 전송, 분할 전송, 범위 전송)로 분리할 수 있습니다. 

 

단순 전송은 요청을 하면 응답을 주는데, 메시지 바디에 대한 콘텐츠 길이를 알고 길이 값을 다 주는 것입니다.  

 

 

HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8
Content-Length: 3423
<html>
 <body>...</body>
</html>

 

단순하게 요청하고 받는 것이라 단순 요청입니다.

 

 

압축 전송은 서버에서 방금 준 데이터를 gzip으로 압축합니다. 

 

 

HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8
Content-Encoding: gzip
Content-Length: 521
lkj123kljoiasudlkjaweioluywlnfdo912u34ljko98udjkl

 

 

그래서 줄어들었을 때, Content-Encoding이라는 것을 넣어줘야만 합니다.

 

 

분할 전송은 Transfer-Encoding을 사용합니다. chunked, 덩어리라는 것인데 덩어리로 쪼개서 보낸다는 것입니다. 

 

 

HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked
5
Hello
5
World
0
\r\n

 

 

위처럼 5바이트, Hello, 5바이트, World. 5바이트 이렇게 나눠서 보내는데, 그럼 클라이언트는 서버가 먼저 보내는 데이터부터 받아볼 수 있다는 장점이 있습니다. 용량이 클 때, 한 번에 보내면 기다려야 하는데, 분할해서 전송하면 오는 대로 화면에 표시할 수 있습니다.

 

 

분할 전송 때는 Content-Length를 넣으면 안 됩니다. 왜냐하면 처음부터 얼마나 많은 데이터를 나눠서 보낼 수 있을지 예상이 안되기 때문입니다. 그다음 범위 전송이라는 것이 있습니다. 이미지를 받는다고 가정했을 때, 만약 받는 도중에 끊겼다면 어떻게 해야 할까요? 이때 처음부터 다시 요청하면 용량이 아까우니, 범위를 지정해서 요청할 수 있습니다. 서버에서 컨텐트 레인지라고 해서 길이만큼 보낼 수 있습니다. 

 

 

 


 

 

 

 

HTTP 헤더 정보

헤더에는 많은 정보들이 있지만 그중 특별한 정도들도 있습니다. Host, Location, Allow, Retry-After가 그의 예시입니다.

 

먼저 Host의 경우 요청한 호스트의 정보를 담는 곳입니다. 예를 들어 하나의 서버가 여러 도메인을 처리해야 하는 경우가 있습니다. 즉 하나의 IP 주소에 여러 도메인이 적용되어 있을 때 단순하게 아래와 같은 요청을 한다고 생각하겠습니다.

 

 

GET /hello HTTP/1.1

 

 

이렇게 되면, 서버에는 다양한 도메인이 있을 때, 어떤 도메인에 요청이 가야 할지 알 수 없습니다. 이를 위해 Host를 지정해줍니다.

 

 

GET /hello HTTP/1.1
Host: aaa.com

 

 

이렇게 되면 정확한 도메인에 요청을 할 수 있습니다. 또한 Location 헤더 값이 있습니다. 웹 브라우저는 3xx 응답의 결과에 Location 헤더가 있으면, Location 위치로 자동 이동하며, 201의 경우 Location 값은 요청에 의해 생성된 리소스 URI가 됩니다. 

 

 

또한 Retry-After의 경우 503 상태 코드 일 땐 서비스가 언제까지 불능인지 알려줄 수 있습니다. 날짜를 표기할 수 있고 초단위로도 표기할 수 있습니다. 

 

 

 

 

 


 

 

 

 

 

 

 

 

마치며

네트워크에 대해 조금씩이라도 지식을 쌓아가고 싶습니다. 기본을 꾸준히 쌓을 수 있는 개발자가 되겠습니다. 

 

 

 

 

 


 

 

 

 

 

출처

 

HTTP | MDN

하이퍼텍스트 전송 프로토콜(HTTP)은 HTML과 같은 하이퍼미디어 문서를 전송하기위한 애플리케이션 레이어 프로토콜입니다. 웹 브라우저와 웹 서버간의 커뮤니케이션을위해 디자인되었지만, 다

developer.mozilla.org

 

HTTP/2 소개  |  Web Fundamentals  |  Google Developers

HTTP/2(또는 h2)는 푸시, 다중화 스트림 및 프레임 제어를 웹에 구현하는 바이너리 프로토콜입니다.

developers.google.com

 

모든 개발자를 위한 HTTP 웹 기본 지식 - 인프런 | 강의

실무에 꼭 필요한 HTTP 핵심 기능과 올바른 HTTP API 설계 방법을 학습합니다., - 강의 소개 | 인프런...

www.inflearn.com