Java HTTP 서버발행일 2024. 5. 21.원본 https://blog.naver.com/jword_/223453721107 ↗

HTTP프로토콜에 대한 핵심개념 3가지

HTTP프로토콜에 대한 핵심개념 3가지 — #HTTP프로토콜 #HTTP란 #개발자의도구들 AI스쿨 msa기반 java 백엔드 코스 중에 공부한 내용을 ...

#[java]HTTPserver#Naver Blog

#HTTP프로토콜 #HTTP란 #개발자의도구들

​

AI스쿨 msa기반 java 백엔드 코스 중에 공부한 내용을 작성하였습니다

\* 현재 웹 서버 프로젝트를 진행중입니다. 전체적인 목차를 보시려면 여기에서 참고하세요

이미지

들어가기전에

이전글을 통해 java를 사용하여 TCP소켓통신이 어떻게 이뤄지는지 공부를 해봤습니다. 프로젝트가 끝난 지금 시점에서 누군가 "가장 중요했던 개념이 뭐였냐?"라고 물어본다면 저는 헤더 패킷을 읽는 과정이라고 생각합니다.

​

클라이언트에서 보낸 body에 헤더 정보를 붙여 서버에서 이 데이터가 어떤 데이터인지 파악할 수 있기 때문에, 프로토콜은 정보에 헤더를 붙이는 과정이라고도 봐도 무방할 것 같습니다. 이 헤더 정보를 통해 이진수로 되어있는 서로의 데이터가 어던 데이터인지 파악이 가능합니다.

​

이번에 시작할 프로젝트는 HTTP 서버 만들기 입니다. TCP소켓 통신과 결이 크게 다르지 않기 때문에 비교적 빠르게 끝나리라 예상됩니다. 핵심적인 개념들과 주의해야할 것 등을 개발하면서 블로그에 남겨볼까 합니다.

핵심개념

1. HTTP 프로토콜이란 무엇인가?

HTTP는 Hyper Text Transfer Protocol의 약자로, 직역해보면 HyperText를 전송하기 위한 프로토콜이 되겠습니다. 여기서 HyperText는 "text which contains links to other texts"라고 정의되는데 쉽게 말해 링크라고 보시면 될 것 같습니다.. 이 정의를 통해 HTTP가 만들어지는 초창기에 이 프로토콜이 어떤 목적으로 사용되기를 기대했는지 볼 수 있습니다.

​

웹의 구조는 HyperText의 덩어리입니다. 우리가 어떤 사이트를 방문하더라도 HyperText를 볼 수 있습니다. 이런 구조를 정의하기 위해서 HTML이라는 HyperTextMarkupLanguage 표준 언어가 등장했습니다. 이 역시 HyperText에 문서구조를 정의하는 Markup을 언어로 표현한 것입니다. 오늘날의 웹구조는 대부분 HTML로 만들어지게 되었습니다.

​

하지만, 오늘날 웹상에서는 HyperText가 아닌 image나 video, 심지어 app도 HTTP로 전송이 가능합니다. 이는 시대가 바뀜에 따라 웹 환경에서 요구되는 기능들도 바뀌게 되었습니다. HTTP는 시대에 걸쳐 여러 버전으로 등장하였고, 결과적으로 오늘날에는 HTML, image, video, app, and map3등 다양한 형태의 데이터를 전송할 수 있습니다. 따라서 재정의 해보면, 웹상에서 사용되는 표준 통신규약으로 보시면 될 것 같습니다.

핵심개념

2. 데이터를 주고 받는 방법

TCP 소켓 통신 프로젝트에서 스트임을 통해 데이터를 주고 받았습니다. 이 스트림은 끊어지지 않는한, 영원히 지속되는 하나의 통로로 보시면 됩니다.

​

이전 TCP 통신에서는 헤더 구조를 제가 직접 커스텀하여 사용하였습니다. 먼저 바디의 전체 길이를 4바이트로 잡고, 메세지 패킷 유형을 정의하여 Int형로 또 4바이트로 적어서 보냈습니다. 추가로 헤더에 필요한 정보가 있을 때마다, 해당 정보에 대한 길이를 명시해주어 어떤 데이터인지 읽을 수 있도록 하였습니다.

​

이렇게 헤더 패킷에 항상 길이를 명시해주는 이유는, 앞서 말한대로 스트림은 영원히 이어지는 일련의 데이터들이기 때문에, 이진 데이터로 정보를 전달하는 과정에서, 어디까지가 유효한 정보인지 알 방법이 없기 때문입니다.

​

그럼 HTTP는 어떨까요?

Header Format

HTTP는 기본적으로 Client-server 구조입니다. 이는 Client에서 연결을 요청하고(반드시 사전에 TCP연결), Server에서 연결을 수락하여 통신이 시작됩니다.

​

보통 Client는 웹 브라우저이고, Server는 웹서버가 되겠습니다. 이때 Client는 항상 request를 요청하고, 서버는 항상 Response로 응답합니다. (물론 서버가 다른 서버에 request 할 수 도 있겠습니다. 이때는 요청하는 Server가 Client가 되겠지요.)

​

이렇게 Clinet와 Server의 동작이 다르다 보니 header message format도 조금씩 차이가 있습니다.

이미지

위를 통해 Requests 헤더 패킷과, Response 헤더 패킷의 차이를 볼 수 있습니다. 사실 위 사진에서 나온 헤더 필드 말고도 많고 다양한 필드가 존재하는데, 저는 최소한의 동작만 가능하도록 구현을 해보고자 합니다. 그러니 필수적으로 들어가야할 필드를 알아봅시다.

​

Client

Post / HTTP/1.1 - (request Line)Host: Localhost User-Agent: ---Connection: keep-aliveContent-Type(exsit body): application/JsonContent-Lenght(exist body): 345(empty Line)body

Clinet는 기본적으로 requestLine이 꼭 포함되어있어야 합니다. 여기에는 HTTP method, URI, HTTP protocl version등이 포함되어 있습니다. 이 외에도 Host, User-Agent, Connection 등의 정보를 넣어 주고 Body가 들어있는 경우 반드시 Body가 어던 타입으로 되어있는지 Content-type을 명시하고, 바디길이가 어느정도인지 명시해야합니다.

​

Server

HTTP/1.1 statusCode(200) statusPhrase(ok) - (status Line)~~Server: Apache~~ (보안 이슈로 명시 X~~)~~Content-type: apllication/JsonDate: Content-Length: 200(empty Line)body

Server의 경우도 가장 첫줄에는 필수적으로 Status Line을 붙여서 보내야 합니다. 이 Status Line은 클라이언트가 요청한 Request를 서버에서 어떻게 처리했는지를 나타내고 있습니다. 기본적으로 웹서버는 HTTP 프로토콜이 아닌 모든 요청에 대해서 무시하거나, 잘못된 요청이라는 응답코드를 보내야합니다.

​

응답 코드에는 종류가 다양한데, 이는 아래에 따로 정리해 두겠습니다.

​

Header Parsing

앞서 TCP소켓 통신에서 가장 중요한 부분이 헤더 를 어떻게 읽어 들이는지에 대한 것이라고 언급하였는데요. HTTP도 마찬가지로 헤더를 어떻게 읽을 것인가가 중요합니다.

​

HTTP는 제가 만든 프로토콜이 아니라, 인터넷의 웹 환경을 사용하는 모든 Host간의 통신 규약입니다. 따라서 반드시 HTTP의 Header format을 따라야 할 필요가 있어요.

​

그럼 클라이언트도, 서버도 header 부분을 parsing하는 과정이 필요하겠지요? 이때 어떻게 parsing을 하면 좋을까요?

이미지

눈치 채신 분들도 있겠지만, request, response header의 마지막에 emptyLine 을 볼 수 있습니다. 이는 HTTP에서 header의 끝을 의미하는 공백 문자입니다. 이 emptyLine을 읽고나면 바로 다음 바이트부터는 body가 시작됩니다(body가 존재하는 경우). 그럼 header에서 parsing한 Content-Length만큼 body를 읽어주면됩니다.

​

HTTP는 오늘날의 프로토콜에서 사용하는 length길이와 예전에 사용하는 줄바꿈("\\r\\n")을 혼합하여 정보를 해독하는 프로토콜이 되겠습니다. 헤더와 바디부분을 따로 parsing해줘야 해요.

status Code

// 1xx: Informational CONTINUE(100, "Continue"), SWITCHING\_PROTOCOLS(101, "Switching Protocols"), PROCESSING(102, "Processing"), ​// 2xx: Success OK(200, "OK"), CREATED(201, "Created"), ACCEPTED(202, "Accepted"), NON\_AUTHORITATIVE\_INFORMATION(203, "Non-Authoritative Information"), NO\_CONTENT(204, "No Content"), RESET\_CONTENT(205, "Reset Content"), PARTIAL\_CONTENT(206, "Partial Content"), MULTI\_STATUS(207, "Multi-Status"), ALREADY\_REPORTED(208, "Already Reported"), IM\_USED(226, "IM Used"), ​// 3xx: Redirection MULTIPLE\_CHOICES(300, "Multiple Choices"), MOVED\_PERMANENTLY(301, "Moved Permanently"), FOUND(302, "Found"), SEE\_OTHER(303, "See Other"), NOT\_MODIFIED(304, "Not Modified"), USE\_PROXY(305, "Use Proxy"), TEMPORARY\_REDIRECT(307, "Temporary Redirect"), PERMANENT\_REDIRECT(308, "Permanent Redirect"), ​// 4xx: Client Error BAD\_REQUEST(400, "Bad Request"), UNAUTHORIZED(401, "Unauthorized"), PAYMENT\_REQUIRED(402, "Payment Required"),FORBIDDEN(403, "Forbidden"), NOT\_FOUND(404, "Not Found"),METHOD\_NOT\_ALLOWED(405, "Method Not Allowed"),NOT\_ACCEPTABLE(406, "Not Acceptable"), PROXY\_AUTHENTICATION\_REQUIRED(407, "Proxy Authentication Required"), REQUEST\_TIMEOUT(408, "Request Timeout"), CONFLICT(409, "Conflict"), GONE(410, "Gone"), LENGTH\_REQUIRED(411, "Length Required"), PRECONDITION\_FAILED(412, "Precondition Failed"), PAYLOAD\_TOO\_LARGE(413, "Payload Too Large"), URI\_TOO\_LONG(414, "URI Too Long"), UNSUPPORTED\_MEDIA\_TYPE(415, "Unsupported Media Type"), RANGE\_NOT\_SATISFIABLE(416, "Range Not Satisfiable"), EXPECTATION\_FAILED(417, "Expectation Failed"), IM\_A\_TEAPOT(418, "I'm a teapot"), MISDIRECTED\_REQUEST(421, "Misdirected Request"), UNPROCESSABLE\_ENTITY(422, "Unprocessable Entity"), LOCKED(423, "Locked"), FAILED\_DEPENDENCY(424, "Failed Dependency"), TOO\_EARLY(425, "Too Early"), UPGRADE\_REQUIRED(426, "Upgrade Required"), PRECONDITION\_REQUIRED(428, "Precondition Required"), TOO\_MANY\_REQUESTS(429, "Too Many Requests"), REQUEST\_HEADER\_FIELDS\_TOO\_LARGE(431, "Request Header Fields Too Large"), UNAVAILABLE\_FOR\_LEGAL\_REASONS(451, "Unavailable For Legal Reasons"),​ // 5xx: Server Error INTERNAL\_SERVER\_ERROR(500, "Internal Server Error"),NOT\_IMPLEMENTED(501, "Not Implemented"), BAD\_GATEWAY(502, "Bad Gateway"),SERVICE\_UNAVAILABLE(503, "Service Unavailable"), GATEWAY\_TIMEOUT(504, "Gateway Timeout"), HTTP\_VERSION\_NOT\_SUPPORTED(505, "HTTP Version Not Supported"), VARIANT\_ALSO\_NEGOTIATES(506, "Variant Also Negotiates"), INSUFFICIENT\_STORAGE(507, "Insufficient Storage"), LOOP\_DETECTED(508, "Loop Detected"), NOT\_EXTENDED(510, "Not Extended"), NETWORK\_AUTHENTICATION\_REQUIRED(511, "Network Authentication Required");

status Code가 이렇게나 많은데요. 다 외울필요는 없고, 필요할때마다 찾아서 보시면 될 것 같습니다! 중요한건 제가 표시해 두었는데요. 인터넷을 사용하면서 한번쯤 보셨을 상태코드라서 이미 외워짐을 방하셨으 수도 있겠습니다.

​

서버에서 서비스를 제공할 수 없는 이유를 간략하게 코드로 나타내어 통신한다는 생각은 매우 효율적인 것 같습니다. 또한 구체적인 이유를 숨기고 대략적으로 표현하는 걸 보면, 보안상의 이점도 가져갈 수 있겠다는 생각이드네요!

핵심개념

3. HTTP 1.0 vs HTTP 1.1

앞서 HTTP는 지속적으로 발전했다고 한 바 있습니다. 현재는 많은 웹서버들이 HTTP 1.1을 사용중에 있는데, HTTP 1.0의 치명적인 한계로 인해 1.1로 넘어오게 되었습니다.

Limitations of HTTP 1.0

HTTP는 기본적으로 TCP위에 올라가는데, Client와 Server가 서로 통신을 시작하기 위해 TCP연결을 먼저 합니다.

​

  1. Absense of persistent Connection

HTTP 1.0의 경우 하나의 요청마다 TCP Connection을 해야하는 non-Persistent connection입니다. 즉, 매번 요청마다 TCP 연결을 해줘야하는 번거로움이 있습니다.

​

  1. HOL

이런 특징은 더 큰 문제를 야기하는데요. 바로 HOL 문제입니다. HOL은 the Head-Of-Line blocking이라는 용어인데, 설명하자면, Client가 한번에 하나의 Request만을 할 수 있다보니, 해당 요청에 대한 Response가 도달하기전까지 다른 Request를 할 수가 없습니다. 그러면 어떤 문제가 발생할 까요?

이미지

Request A, B가 있다고 보면, Request A의 Response는 덩치가 매우커서, 응답이 오기까지 너무 오랜 시간이 걸립니다. 하지만 Reqeust B의 경우는 사이즈가 작은 Response로 빠르게 응답받을 수 있습니다. B -> A순으로 요청을 하면 좋겠지만, 현실적으로는 B->A의 요청 순서를 보장할 수 없습니다. 그러면 A응답이 오는 시간동안 B를 보내서 해결할 수 있지만, HTTP 1.0은 한번에 하나의 요청만 가능하기 때문에 A의 응답을 계속해서 기다려야 합니다. 이를 HOL promlem이라고 합니다.

​

HTTP 1.1

HTTP 1.0의 큰 문제 2개를 해결하기 위해 HTTP 1.1이 등장하였습니다.

​

  1. persistent Connection

우선 Client와 Server간에 한번의 TCP연결에도 여러번의 Reqeust가 가능하게 만들었습니다. 이를 통해 2번 HOL문제도 해결이 가능합니다.

​

이미지

  1. pipeLining

더 이상 Client는 이전 Request에 대한 response를 기다릴 필요 없이 여러 요청을 한번에 보낼 수 있습니다.

​

HTTP 1.1에도 여러 한계가 존재하지만, 서버측에서는 크게 고려할 필요가 없는 부분이라서 오늘날에 웹서버는 HTTP 1.1 프로토콜로 통신하고 있습니다.