
리얼월드 HTTP 를 학습하고 정리한 글입니다.
초창기 HTTP의 발전 과정에서 중요한 것은 다음 4가지이다.
HTTP 0.9 -> 1.0 -> 1.1 의 과정은 위의 차이에서 기인하니, 이를 중심으로 발전을 이해해보자.
유럽입자 물리학 연구소 (CERN)의 팀 버너스리가 최초의 웹 서버를 구현(1990)하였다.
이를 바탕으로 발전 시킨 것이 HTTP/0.9 이다.
HTTP/0.9의 목적
HTTP/0.9 구조
한계
HTTP/1.0 에서는 HTTP/0.9와 달리 새롭게 추가된 것들이 있습니다.

여러 헤더들 (Host, User-Agent, Accept) 들이 추가된 것이 보입니다.
> 는 클라이언트 → 서버 전달하는 요청< 는 서버→클라이언트 로 전달받는 응답> 를 보게되면 다음이 추가된 것을 볼 수 있습니다.
이들을 헤더 라고 하는데, 이를 중심으로 큰 차이를 먼저 확인하겠습니다.
// echo-server.js
const http = require("http");
const PORT = 3000;
const server = http.createServer((req, res) => {
let body = [];
req.on("data", (chunk) => {
body.push(chunk);
});
req.on("end", () => {
body = Buffer.concat(body).toString();
// 요청 전체 dump 비슷하게 출력
console.log("===== Request Dump =====");
console.log(`${req.method} ${req.url} HTTP/${req.httpVersion}`);
for (const [key, value] of Object.entries(req.headers)) {
console.log(`${key}: ${value}`);
}
console.log(""); // 헤더 끝
console.log(body); // body 출력
console.log("========================");
// 응답: hello! html
res.writeHead(200, { "Content-Type": "text/html" });
res.end("<html><body>hello!</body></html>\n");
});
req.on("error", (err) => {
console.error("Request Error:", err);
res.writeHead(500, { "Content-Type": "text/plain" });
res.end("Internal Server Error");
});
});
server.listen(PORT, () => {
console.log(`✅ Echo server running at http://localhost:${PORT}`);
});

위의 이미지의 >, < 로 출력되어있는 라인들이 Body외의 HTTP에서 추가적으로 전달되는 정보입니다.
HTTP/0.9 에서 어떤 파일을 전송하는지, 목적지가 어디인지 등을 같이 전달하기 위해서 헤더를 추가하였는데요,
자주 사용되는 헤더에 대해 알아보겠습니다.
요청 헤더 (client → server)
응답 헤더 (server → client)
X- 로 시작하는 헤더 : 사용자가 자유롭게 붙여서 사용할 수 있는 헤더Content-Type은 브라우저에서 받은 파일을 렌더링할때 중요한 역할을 합니다. 만약에 image인데, test/plain으로 전달을 해버리면 브라우저는 text라고 판단하여, 이미지를 text로 변환한 값을 화면에 그립니다.
저희가 예전에 사용하던 인터넷 익스플로러는 조금 특이하게도 Content-Type 의 MIME 타입을 확인하지 않았습니다. 위와 같은 문제가 발생할 수도 있다고 생각하여 전달되는 내용을 보고 파일 형식을 추측했어요. 이를 Content sniffing 이라고 합니다.
하지만 위 방식의 단점은 직접 파일을 추측해야한다는 것과 이 과정에서 잘못된 추측이 발생할 수 있다는 것이었습니다. text/plain 의 HTML + js 인데, 이를 멋대로 해석해서 HTML/JS 로 판단하는 예시가 있겠네요.
Header, Body의 시초
HTTP/1.0 이전에 전자메일을 네트워크 상으로 전달하곤 했었습니다. 전자메일은 수신자와 같은 관련 정보와 메일 내용을 담는 body로 구성되어 전달되어있었습니다. 이에 착안하여 HTTP에 전자메일과 비슷한 Header와 Body를 추가하게 되었습니다.
// 전자메일
From: alice@example.com
To: bob@example.com
Subject: Meeting Tomorrow
Date: Sun, 21 Sep 2025 10:00:00 +0900
Content-Type: text/plain; charset="utf-8"
Hi Bob,
Let's meet tomorrow at 2 PM.
Thanks,
Alice
// HTTP 요청
POST /login HTTP/1.0
Host: www.example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 32
username=seokchan&password=1234
전자메일의 From:, To: 등의 값들을 헤더로 변환하였고, 아래의 개행문자와 함께 분리된 메일 내용을 Body로 변환한 것입니다. 그래서 HTTP와 전자메일의 구조자체는 비슷해요.
다만 2가지 큰 차이가 있습니다.
위 2가지 사항을 제외하면 HTTP는 고속으로 전자메일을 왕복시키는 것이라고 표현해도 무방할 정도로 닮아있습다.
HTTP의 메서드와 상태 코드는 뉴스 그룹 에서 착안해 온 것입니다. 뉴스 그룹의 메서드로는 LIST, HEAD, BODY, POST 등이 사용되었습니다. LIST는 서버에 있는 모든 뉴스 그룹 목록을 가져오는데, HTTP의 GET과 비슷한 역할을 했습니다.
이에 착안하여 HTTP에 수많은 메서드가 제안되었고, 다음 세 가지가 POST, GET, HEAD 가 흔히 사용되는 메서드로 사용됩니다. 이후에 HTTP 1.1에 가서야 PUT, DELETE 등이 추가로 지원되었습니다.
HTML Form으로 사용하는 <Form>은 POST, GET만 지원합니다.
상태 코드

여기에서 300번대의 상태코드가 조금 특이한데, 300번대는 서버가 브라우저에게 리다이렉트하도록 지시하는 status 코드를 의미한다.
URL vs URI
URL과 URI는 URI가 URL을 포함하는 관계로 조금 다릅니다. URI = URL+URN(명명 규칙) 인데, 웹에서는 URN을 잘 안써서 URL과 URI가 비슷한 의미를 갖는다고 합니다.
URL 구조는 다음과 같습니다.
스키마://호스트명/경로
조금더 구체적으로는 아래와 같다.
스키마://사용자:패스워드@호스트명:포트/경로#fragment?Query
https://jeong:1234@localhost:8080/books#fragment?query=1
각각의 기능을 아래와 같아요.
스키마
http, https, file 등으로 브라우저가 이 스키마를 해석하여 적절한 접속 방법을 선택합니다.
저희가 Chrome으로 로컬의 pdf를 열면 file:// 경로로 뜨는데요, 이는 크롬이 스키마를 읽고 file 방식으로 해당 파일에 접속했기 때문입니다.
호스트명
통신 대상이 되는 서버 주소를 의미합니다. 포트가 생략되면 스키마별 기본 포트를 사용해요.
사용자, 패스워드 : 보안 문제 때문에 웹에서는 해당 방식으로 사용하지 않음
프레드먼트 : 앵커 저장
HTTP/0.9 에서는 요청 시에, 서버로 데이터를 전달하기 어려웠다. 전달하는 유일한 방법은 URL에 Query를 실어서 전달하는 방법 뿐이었기에 파일을 전달할 수 없었습다.
HTTP/1.0에서는 Body에 데이터를 넣어서 전달할 수 있다. 헤더 끝에 빈 줄을 넣으면 그 아래부터는 Body로 인식한다.
헤더1: 헤더 값1
헤더2: 헤더 값2
Content_Length: 바디의 길이
지정된 바이트 수많큼의 바디 데이터
curl로 body를 같이 서버에 전송하고 싶으면 -d 를 사용하면 된다.
$ curl -d "{\"name\":\"Jeong\"} -H "Content-Type: application/json"
http://localhost:8080
사실 GET 요청 시에도 Body에 값을 넣어서 전달할 수 있습니다. 전달하는 데이터 헤더 끝에 빈줄을 넣고 Body를 실어서 전달하면 GET 요청에도 Body를 전달할 수 있습니다.
GET /search?q=hello HTTP/1.0
Host: www.example.com
Content-Type: application/json
Content-Length: 27
{
"filter": "recent",
"limit": 10
}
하지만 RFC 7231(HTTP/1.1) (HTTP 사용 지침)에 따르면 이 방식은 추천하지 않고, 서버에 따라서 GET 요청인 경우 Body를 무시하는 경우도 있으니 자제하라고 합니다.
여기에서 말하고 싶은 것은 어떤 HTTP 요청이라도 body 메세지를 추가해서 전달할 수 있다는 것입니다. HTTP 메서드에 따라서 형식이 달라지는 것은 아니라는 것입니다.
아 나도 책 읽어야 되는데