컴퓨터 네트워크 4편

HTTP 메시지, 쿠키, 웹 캐시 — 연결 방식부터 캐싱 계산까지

suhyun·2026년 10월 12일·읽는 데 약 17분
한 줄로: 웹 캐시는 요청 일부를 기관 안에서 끝내 접속 회선의 트래픽 강도를 낮추고, 그래서 더 빠른 회선을 사는 것보다 싸게 평균 응답 시간을 줄입니다.

1. 비지속 연결과 지속 연결

앞 글에서 HTTP가 TCP 연결 위에서 요청과 응답을 주고받는다는 데까지 보았습니다. TCP 연결 하나로 객체를 몇 개 보내느냐에 따라 HTTP 연결은 두 가지로 나뉩니다.

비지속 HTTP (non-persistent)지속 HTTP (persistent)
TCP 연결을 열고, 객체를 최대 하나 보내고, 연결을 닫는다. 객체가 여럿이면 연결도 여러 번 맺는다TCP 연결을 열고, 그 연결 하나로 여러 객체를 보낸 뒤 닫는다

비지속 연결로 텍스트와 이미지 4장이 든 페이지를 받는 과정은 이렇습니다. 클라이언트가 서버의 80번 포트로 TCP 연결을 시작하고, 기다리던 서버가 연결을 받아들입니다. 클라이언트가 HTML 파일을 요청하는 메시지를 소켓에 넣고, 서버가 파일을 담은 응답을 보낸 뒤 연결을 닫습니다. 클라이언트는 HTML을 읽다가 이미지 4장의 참조를 발견하고, 이미지마다 이 과정을 처음부터 되풀이합니다.

응답 시간을 RTT로 세기

RTT(round trip time, 왕복 시간)는 작은 패킷이 클라이언트에서 서버로 갔다가 돌아오는 시간입니다. 비지속 HTTP에서 객체 하나를 받는 시간은 세 부분입니다.

  • TCP 연결을 맺는 데 RTT 1번. 클라이언트가 연결을 요청하고 서버의 답을 받습니다.
  • HTTP 요청을 보내고 응답의 첫 바이트를 받는 데 RTT 1번.
  • 객체 파일을 링크에 싣는 전송 시간.

비지속 HTTP 응답 시간 = 2 RTT + 파일 전송 시간

객체마다 RTT 2번에 연결마다 운영체제가 자원을 잡는 부담까지 듭니다. 그래서 브라우저는 TCP 연결을 여러 개 동시에 열어 객체들을 함께 받기도 합니다. 이것을 병렬 연결(parallel connection)이라고 합니다. 지속 HTTP는 응답을 보낸 뒤에도 연결을 열어 두고 같은 서버에 보내는 다음 요청을 그 연결로 받습니다. 연결을 다시 맺지 않으므로 객체 하나에 RTT 1번이면 됩니다. 응답을 기다리지 않고 요청 여러 개를 연달아 보내는 파이프라이닝(pipelining)까지 쓰면 참조된 객체 전체를 RTT 1번에 받을 수도 있습니다. 지금은 거의 항상 지속 연결을 씁니다. 지속 연결의 대가는 서버가 다음 요청이 올 때까지 연결을 붙들고 있어야 한다는 점입니다.

문제 1

클라이언트와 서버 사이의 RTT가 40 ms입니다. HTML 파일 하나가 같은 서버의 아주 작은 이미지 5장을 참조합니다. 전송 시간을 무시할 때 페이지 전체를 받는 시간을 네 방식으로 구하십시오. (가) 비지속, 연결 하나씩 차례로 (나) 비지속, 병렬 연결 최대 5개 (다) 지속, 파이프라이닝 없음 (라) 지속, 파이프라이닝 있음.

(가) 파일 6개가 각각 연결 RTT 1번과 요청 RTT 1번을 씁니다. 6 × 2 × 40 = 480 ms입니다. (나) HTML을 받는 데 2 × 40 = 80 ms, 그다음 이미지 5장을 연결 5개로 동시에 받는 데 다시 80 ms가 들어 160 ms입니다. (다) 연결 40 ms, HTML 40 ms, 이미지는 한 장씩 요청하고 응답을 받은 뒤 다음을 요청하므로 5 × 40 = 200 ms, 합해서 280 ms입니다. (라) 연결 40 ms, HTML 40 ms, 이미지 요청 5개를 한꺼번에 보내고 응답을 모두 받는 데 40 ms, 합해서 120 ms입니다. (가)와 (다)를 비교하면 연결을 다시 맺지 않는 효과가, (다)와 (라)를 비교하면 파이프라이닝의 효과가 보입니다.

2. HTTP 요청 메시지

HTTP 메시지는 ASCII 문자로 쓰여 있어 사람이 읽을 수 있습니다. 브라우저가 보내는 요청 메시지는 예를 들어 이렇게 생겼습니다.

GET /notes/net.html HTTP/1.1\r\n
Host: www.example.com\r\n
User-Agent: Mozilla/5.0\r\n
Accept: text/html\r\n
Accept-Language: ko-KR,ko;q=0.9\r\n
Connection: keep-alive\r\n
\r\n
  • 요청 줄(request line) — 첫 줄입니다. 메서드(GET), URL(/notes/net.html), HTTP 버전(HTTP/1.1)이 공백으로 나뉘어 차례로 옵니다.
  • 헤더 줄(header lines) — 둘째 줄부터입니다. "필드 이름: 값" 꼴로 씁니다. Host는 객체가 있는 호스트, User-Agent는 요청을 보낸 브라우저 종류, Accept-Language는 받고 싶은 언어, Connection: keep-alive는 연결을 유지해 달라는 뜻입니다.
  • \r\n — 캐리지 리턴(carriage return)과 라인 피드(line feed)입니다. 줄마다 끝에 붙어 줄을 나눕니다. 헤더가 다 끝나면 \r\n만 있는 빈 줄을 하나 더 넣어 헤더의 끝을 알립니다.

빈 줄 뒤에는 본문(entity body)이 올 수 있습니다. 본문은 서버에 보낼 데이터가 있을 때만 붙입니다. 일반 형식으로 쓰면 이렇습니다. 꺾쇠 안은 실제 값으로 바뀌는 자리입니다.

<메서드> <URL> <버전>\r\n
<필드 이름>: <값>\r\n
...
\r\n
<본문 (없을 수도 있음)>

요청 메서드

메서드하는 일
GET객체를 가져온다. 가장 흔한 요청이고 본문이 없다. 사용자가 입력한 값은 URL의 ? 뒤에 붙여 보낼 수 있다. 예: www.example.com/search?q=router
HEAD같은 URL을 GET으로 요청했을 때 돌아올 헤더만 요청한다. 서버는 최종 수정 날짜나 크기 같은 정보만 보내고 객체 자체는 보내지 않는다
POST폼에 입력한 값 같은 데이터를 본문에 담아 서버로 보낸다
PUT본문에 담은 객체를 지정한 URL에 올린다. 그 URL에 이미 파일이 있으면 새 내용으로 통째로 바꾼다

POST와 PUT은 둘 다 서버로 데이터를 보냅니다. POST는 서버가 처리할 데이터를 건네는 용도이고, PUT은 지정한 자리의 파일을 새 파일로 갈아 끼우는 용도입니다.

3. HTTP 응답 메시지와 상태 코드

서버가 돌려주는 응답 메시지는 예를 들어 이렇게 생겼습니다.

HTTP/1.1 200 OK\r\n
Date: Mon, 19 Oct 2026 03:12:45 GMT\r\n
Server: nginx\r\n
Last-Modified: Fri, 02 Oct 2026 09:30:00 GMT\r\n
Content-Length: 5120\r\n
Content-Type: text/html; charset=UTF-8\r\n
\r\n
(HTML 파일 내용)

첫 줄은 상태 줄(status line)입니다. 프로토콜 버전, 상태 코드(status code), 상태 문구(status phrase)가 차례로 옵니다. 그다음 헤더 줄에는 응답을 만든 시각(Date), 서버 소프트웨어(Server), 객체를 마지막으로 고친 시각(Last-Modified), 객체 크기(Content-Length), 객체 종류(Content-Type)가 들어갑니다. 빈 줄 뒤에 요청한 객체가 실립니다.

상태 코드뜻
200 OK요청이 성공했고, 요청한 객체가 메시지 뒤쪽에 실려 있다
301 Moved Permanently객체가 다른 주소로 영구히 옮겨졌다. 새 주소를 Location: 헤더에 적어 주므로 클라이언트는 그 주소로 다시 요청한다
400 Bad Request서버가 요청 메시지를 이해하지 못했다. 형식이 틀렸거나 메시지가 손상된 경우다
404 Not Found요청한 객체가 이 서버에 없다
505 HTTP Version Not Supported요청에 쓴 HTTP 버전을 서버가 지원하지 않는다

301과 404는 둘 다 그 주소에 객체가 없다는 뜻입니다. 301은 서버가 새 위치를 알고 알려 주는 경우이고, 404는 서버도 위치를 몰라 객체를 줄 수 없는 경우입니다.

4. 무상태 HTTP와 쿠키

HTTP는 무상태입니다. 요청 하나하나가 서로 독립이고, 서버는 이전 요청을 기억하지 않습니다. 여러 단계에 걸친 거래의 진행 상황을 서버와 클라이언트가 함께 추적할 필요가 없고, 중간에 끊긴 거래를 되살리는 절차도 필요 없습니다. 그래서 서버가 수많은 클라이언트를 단순하게 상대할 수 있습니다.

그런데 로그인 상태나 장바구니처럼 사용자를 기억해야 하는 기능이 있습니다. 이때 쿠키(cookie)를 씁니다. 쿠키는 웹사이트와 브라우저가 요청과 요청 사이에 상태를 이어 가도록 쓰는 작은 데이터입니다.

쿠키가 오가는 순서

지수의 브라우저는 이미 한 영화 예매 사이트에서 받은 쿠키를 갖고 있습니다. 오늘 처음으로 한 온라인 서점에 접속합니다.

  1. 브라우저가 서점 서버에 첫 요청을 보냅니다. 이 사이트의 쿠키는 아직 없습니다.
  2. 서버가 지수에게 고유 번호 4821을 정하고, 서버 뒤의 데이터베이스에 4821번 항목을 새로 만듭니다.
  3. 서버가 응답에 Set-Cookie: 4821 헤더를 붙여 보냅니다. 브라우저는 쿠키 파일에 서점 주소와 4821을 함께 적어 둡니다.
  4. 그 뒤 서점에 보내는 모든 요청에 브라우저가 Cookie: 4821 헤더를 붙입니다. 서버는 이 번호로 데이터베이스를 찾아, 지수가 담은 책이나 둘러본 분야를 알아냅니다.
  5. 열흘 뒤 다시 접속해도 쿠키 파일이 남아 있으면 같은 번호가 가고, 서버는 지수를 알아봅니다.

쿠키를 이루는 요소는 넷입니다. 응답 메시지의 Set-Cookie 헤더 줄, 그다음 요청 메시지의 Cookie 헤더 줄, 사용자 호스트에 있고 브라우저가 관리하는 쿠키 파일, 웹사이트 쪽의 백엔드 데이터베이스입니다. HTTP는 번호를 헤더에 실어 나를 뿐이고, 상태를 기억하고 관리하는 쪽은 브라우저와 웹 서버입니다. 그래서 쿠키를 쓰더라도 HTTP 자체는 여전히 무상태입니다. 사용자가 쿠키를 지우면 보낼 번호가 없어지므로 서버는 그 사용자를 알아보지 못합니다.

쓰임과 프라이버시

쿠키로 로그인 유지(인증), 장바구니, 추천, 웹 메일 같은 사용자 세션 상태를 만듭니다. 대가는 프라이버시입니다. 사이트는 쿠키로 사용자의 행동을 계속 모읍니다. 특히 광고 회사처럼 여러 사이트에 함께 끼어 있는 제3자가 심는 쿠키를 제3자 지속 쿠키(third party persistent cookie), 또는 추적 쿠키라고 합니다. 같은 쿠키 번호가 여러 사이트에서 보이면, 사용자가 어느 사이트를 거쳐 다녔는지가 하나로 묶입니다.

5. 웹 캐시 — 프록시 서버

웹 캐시(Web cache)는 원 서버(origin server)까지 가지 않고 클라이언트 요청을 처리하려고 둔 중간 서버입니다. 프록시 서버(proxy server)라고도 합니다. 브라우저가 캐시를 거치도록 설정해 두면 모든 HTTP 요청이 캐시로 갑니다.

  • 요청한 객체가 캐시에 있으면 캐시가 바로 돌려줍니다.
  • 없으면 캐시가 원 서버에 요청해 객체를 받아 사본을 저장하고, 클라이언트에게 돌려줍니다.

같은 객체를 다른 사용자가 다시 요청하면 이번에는 원 서버까지 가지 않고 캐시에서 끝납니다. 그래서 캐시는 클라이언트이면서 서버입니다. 요청한 브라우저에게는 서버이고, 원 서버에게는 클라이언트입니다. 대학, 회사, 가정용 ISP처럼 사용자가 모인 곳에서 흔히 설치합니다.

  • 응답 시간이 줄어듭니다. 캐시는 원 서버보다 클라이언트에 훨씬 가까워 전파 지연과 거쳐 가는 링크가 줄어듭니다. 캐시에 객체가 있을 때만 빨라집니다.
  • 기관 접속 회선의 트래픽이 줄어듭니다. 캐시가 답한 요청은 기관 네트워크 안에서 끝나 바깥으로 나가는 회선을 쓰지 않습니다.
  • 작은 콘텐츠 제공자도 콘텐츠를 잘 전달할 수 있습니다. 인터넷 곳곳의 캐시가 사본을 나눠 들고 있기 때문입니다.

캐시도 결국 서버 하나이므로 요청이 한꺼번에 몰리면 과부하가 걸릴 수 있습니다. 저장 공간이 차면 오래 쓰이지 않은 객체부터 지웁니다.

6. 캐싱 계산 — 회선을 바꿀까, 캐시를 둘까

한 기관의 상황을 세웁니다.

조건값
기관 라우터와 인터넷을 잇는 접속 회선 (access link)20 Mbps
기관 LAN1 Gbps
웹 객체 크기 L1,000,000비트 (1 Mb)
브라우저들이 원 서버로 보내는 평균 요청 수 a초당 18개
기관 라우터에서 원 서버까지 갔다 오는 인터넷 지연2초

링크마다 이용률(utilization)을 구합니다. 이용률은 앞에서 본 트래픽 강도 La / R입니다. 들어오는 데이터는 초당 1 Mb × 18 = 18 Mbps입니다.

LAN 이용률 = 18 Mbps / 1 Gbps = 0.018, 접속 회선 이용률 = 18 / 20 = 0.9

LAN은 한가하고 접속 회선은 꽉 차 있습니다. 큐잉 지연을 숫자로 구하려면 큐잉 이론에서 나온 근사식을 씁니다. t를 객체 하나를 링크에 싣는 평균 시간 L / R, a를 그 링크에 도착하는 객체 수(개/초)라고 하면, 큐에서 기다리는 시간과 전송 시간을 합한 평균 접속 지연(average access delay)은 이렇습니다.

평균 접속 지연 = t / (1 − t × a)

분모의 t × a = (L / R) × a가 바로 트래픽 강도입니다. 이 값이 1에 다가가면 분모가 0에 가까워져 지연이 급격히 커집니다. 앞 글에서 본 경향을 식으로 옮긴 것입니다.

지금 상태

접속 회선에서 t = 106 / (20 × 106) = 0.05초, t × a = 0.05 × 18 = 0.9입니다. 평균 접속 지연은 0.05 / (1 − 0.9) = 0.5초입니다. LAN에서는 t = 106 / 109 = 0.001초, t × a = 0.018이라 지연이 약 0.001초로 무시할 만합니다. 응답 시간은 세 구간의 합입니다.

응답 시간 = 인터넷 지연 + 접속 회선 지연 + LAN 지연 ≈ 2 + 0.5 + 0.001 ≈ 2.5초

방법 1 — 접속 회선을 100 Mbps로 바꾼다

t = 106 / 108 = 0.01초, t × a = 0.18, 평균 접속 지연 = 0.01 / 0.82 ≈ 0.012초입니다. 응답 시간은 약 2.01초가 됩니다. 남은 2초는 원 서버까지의 인터넷 지연이라 회선을 아무리 빠르게 해도 줄지 않습니다. 그리고 빠른 접속 회선은 비쌉니다.

방법 2 — 기관 LAN에 웹 캐시를 둔다

캐시 적중률(hit rate)을 0.5로 둡니다. 요청의 절반은 캐시에서 끝나고, 나머지 절반만 접속 회선을 지나 원 서버로 갑니다. 실패율(miss rate)은 1 − 적중률 = 0.5입니다.

  • 캐시 실패(50%) — 접속 회선에 도착하는 객체가 초당 18 × 0.5 = 9개로 줄어듭니다. t는 0.05초 그대로이고 t × a = 0.45, 평균 접속 지연 = 0.05 / 0.55 ≈ 0.091초입니다. 이 요청은 원 서버까지 가야 하므로 응답 시간은 인터넷 지연을 더한 2 + 0.091 = 2.091초입니다.
  • 캐시 적중(50%) — LAN만 지납니다. t = 0.001초, a = 9, t × a = 0.009, 지연은 약 0.001초입니다.

평균 응답 시간은 두 경우의 가중 평균입니다.

평균 응답 시간 = 0.5 × 2.091 + 0.5 × 0.001 ≈ 1.05초

실패한 요청에 인터넷 지연 2초를 빠뜨리지 않도록 주의합니다. 접속 지연 0.091초만 곱하면 평균이 0.05초쯤으로 터무니없이 작게 나옵니다.

지금방법 1: 100 Mbps 회선방법 2: 캐시 (적중률 0.5)
접속 회선 이용률0.90.180.45
평균 응답 시간약 2.5초약 2.01초약 1.05초
비용—비싸다싸다

회선을 다섯 배로 바꿔도 2초 밑으로는 내려가지 못하지만, 캐시는 요청 절반의 인터넷 지연 2초를 통째로 없애므로 평균이 더 낮아집니다. 프록시 서버 한 대는 새 접속 회선보다 훨씬 쌉니다.

문제 2

위 기관에서 캐시 적중률이 0.3밖에 나오지 않는다면 평균 응답 시간은 얼마입니까. 이때도 캐시가 방법 1보다 낫습니까.

실패율은 0.7입니다. 접속 회선에 오는 객체는 초당 18 × 0.7 = 12.6개, t × a = 0.05 × 12.6 = 0.63, 평균 접속 지연 = 0.05 / 0.37 ≈ 0.135초입니다. 실패한 요청의 응답 시간은 2 + 0.135 = 2.135초입니다. 적중한 요청은 LAN 지연 약 0.001초입니다. 평균은 0.7 × 2.135 + 0.3 × 0.001 ≈ 1.50초입니다. 방법 1의 2.01초보다 여전히 짧습니다. 적중률이 높을수록 접속 회선 지연과 인터넷 지연을 함께 피하는 요청이 늘어나 평균이 더 빨리 줄어듭니다.

정리

비지속 HTTP는 객체마다 RTT 2번이 들고, 지속 HTTP와 파이프라이닝은 연결을 다시 쓰고 요청을 몰아 보내 RTT를 아낍니다. HTTP 메시지는 사람이 읽을 수 있는 ASCII 텍스트로, 요청은 요청 줄과 헤더 줄과 본문, 응답은 상태 줄과 헤더 줄과 객체로 이루어집니다. 무상태인 HTTP 위에서 사용자를 기억할 때는 Set-Cookie와 Cookie 헤더로 번호를 주고받는 쿠키를 쓰고, 그 대가로 추적의 위험이 따릅니다. 웹 캐시는 요청 일부를 기관 안에서 끝내 접속 회선의 트래픽 강도를 낮춥니다. 평균 접속 지연 t / (1 − ta)로 계산하면, 캐시가 더 빠른 회선보다 싸면서도 평균 응답 시간을 더 줄일 수 있습니다. 다음 글에서는 캐시가 가진 사본이 낡았는지 확인하는 조건부 GET과 HTTP/2, HTTP/3를 보고 전자우편과 DNS로 넘어갑니다.

dev-news학습 노트소개개인정보 처리