About→

← Studies

01. 네트워크 — 브라우저는 서버와 어떻게 통신하는가?

앞선 글에서는 브라우저에 https://yong-hee.com을 입력했을 때 어떤 큰 흐름이 시작되는지 살펴봤다.

이번에는 그중에서도 네트워크 통신만 자세히 들여다보려고 한다.

브라우저가 웹페이지를 요청하려면 먼저 서버가 어디에 있는지 알아야 하고, 그 서버와 통신할 방법을 만들어야 한다. 그리고 실제 데이터를 주고받을 때는 어떤 규칙을 사용할지도 필요하다.

이 과정에서 우리가 자주 듣는 개념들이 등장한다.

text
DNS
 ↓
IP
 ↓
TCP / QUIC
 ↓
TLS
 ↓
HTTP
 ↓
HTTP/1.1 / HTTP/2 / HTTP/3

각각 따로 보면 전혀 다른 기술처럼 보이지만, 실제로는 하나의 네트워크 통신 과정 안에서 서로 다른 문제를 해결하고 있다.

1. DNS

DNS가 필요한 이유

브라우저에 다음과 같이 입력한다고 해보자.

text
https://yong-hee.com

사람 입장에서는 yong-hee.com이라는 도메인 이름을 사용한다.

하지만 네트워크에서 서버를 찾을 때는 결국 IP 주소가 필요하다.

예를 들어 어떤 서버의 IP 주소가 다음과 같다고 해보자.

text
203.0.113.10

브라우저가 실제로 통신할 대상은 이 IP 주소를 가진 서버다.

그렇다면 이런 관계가 필요하다.

text
yong-hee.com
      ↓
203.0.113.10

이 역할을 하는 시스템이 DNS(Domain Name System)다.

DNS는 흔히

도메인 이름을 IP 주소로 변환하는 시스템

이라고 설명한다.

이 설명은 맞지만, DNS가 단순히 "이름을 IP로 바꾸는 하나의 서버"라고 생각하면 부족하다.

DNS는 도메인과 관련된 다양한 정보를 분산해서 관리하는 계층적인 시스템이다.

DNS의 계층 구조

DNS는 크게 다음과 같은 계층 구조를 가진다.

text
Root DNS
   ↓
TLD DNS
   ↓
Authoritative DNS

예를 들어 yong-hee.com을 조회한다고 해보자.

Root DNS

가장 상위에는 Root DNS가 있다.

Root DNS가 yong-hee.com의 실제 IP 주소를 모두 가지고 있는 것은 아니다.

대신 .com과 같은 최상위 도메인을 어디에서 찾을 수 있는지 알려준다.

text
yong-hee.com
      ↓
Root DNS
      ↓
".com을 담당하는 DNS는 여기 있어"

TLD DNS

TLD(Top-Level Domain)는 .com, .org, .net 같은 최상위 도메인을 의미한다.

Resolver가 .com을 담당하는 TLD DNS에 질문하면,

text
yong-hee.com의 DNS 서버가 어디야?

TLD DNS는 해당 도메인을 관리하는 Authoritative DNS 정보를 알려준다.

Authoritative DNS

Authoritative DNS는 해당 도메인에 대한 실제 DNS 레코드를 관리한다.

예를 들어 다음과 같은 정보를 가지고 있을 수 있다.

text
yong-hee.com
→ 203.0.113.10

DNS Resolver

그렇다면 브라우저가 Root DNS부터 직접 찾아가는 것일까?

보통 그렇지 않다.

브라우저와 운영체제 등의 요청을 받은 DNS Resolver가 DNS 조회를 대신 수행한다.

개념적으로 보면 다음과 같다.

text
브라우저
   ↓
DNS Resolver
   ↓
Root DNS
   ↓
TLD DNS
   ↓
Authoritative DNS
   ↓
IP 주소

다만 이 과정이 매번 전부 실행되는 것은 아니다.

DNS에는 캐시(Cache)가 존재하기 때문이다.

이미 yong-hee.com → IP 정보를 알고 있다면 Resolver는 다시 Root DNS부터 조회하지 않고 캐시에 저장된 결과를 사용할 수 있다.

DNS Cache와 TTL

DNS 결과는 일정 시간 동안 캐시될 수 있다.

이때 사용되는 개념이 TTL(Time To Live)이다.

예를 들어 DNS 레코드의 TTL이 300초라면 해당 결과를 일정 시간 동안 캐시해서 사용할 수 있다.

text
DNS 조회
   ↓
IP 주소 획득
   ↓
캐시 저장
   ↓
일정 시간 동안 재사용

DNS 캐시가 존재하기 때문에 실제 DNS 조회 과정은 상황에 따라 달라진다.

브라우저나 운영체제, DNS Resolver 등에 이미 결과가 있다면 더 상위의 DNS 서버까지 조회하지 않을 수도 있다.

DNS Record

DNS가 관리하는 정보가 IP 주소만 있는 것도 아니다.

DNS에는 여러 종류의 Record가 있다.

Record역할
A도메인 → IPv4 주소
AAAA도메인 → IPv6 주소
CNAME다른 도메인을 가리키는 별칭
NS해당 도메인의 DNS 서버 정보
MX메일 서버 정보
TXT텍스트 형태의 정보

예를 들어 A Record는 다음과 같다.

text
yong-hee.com
      ↓
203.0.113.10

AAAA Record는 IPv6 주소를 사용한다.

text
yong-hee.com
      ↓
2001:db8::1

DNS를 이해할 때 중요한 것은 단순히 "도메인을 IP로 바꾼다"가 아니다.

사람이 사용하는 도메인 이름을 네트워크에서 사용할 수 있는 정보로 연결해주는 분산 시스템이라는 관점으로 이해하는 것이 좋다.

2. IP

DNS를 통해 서버의 IP 주소를 알아냈다고 해보자.

text
yong-hee.com
      ↓
203.0.113.10

이제 브라우저는 통신할 대상의 주소를 알게 됐다.

그렇다면 다음 질문이 생긴다.

IP 주소는 정확히 무엇이고, 이 주소를 가지고 어떻게 서버까지 찾아가는 걸까?

IP 주소란?

IP(Internet Protocol)는 네트워크에서 데이터를 목적지까지 전달하기 위한 프로토콜이다.

그리고 IP 주소는 네트워크에서 통신 대상의 주소를 식별하기 위해 사용되는 주소라고 생각할 수 있다.

대표적으로 IPv4에서는 다음과 같은 형태를 사용한다.

text
192.168.0.10

IPv4는 32비트 주소를 사용한다.

반면 IPv6는 128비트 주소를 사용한다.

text
2001:db8:85a3::8a2e:370:7334

IPv4 주소 공간이 부족해지면서 IPv6가 등장하게 됐다.

공인 IP와 사설 IP

우리가 사용하는 컴퓨터의 IP가 항상 인터넷에서 직접 접근 가능한 것은 아니다.

대표적으로 다음과 같이 구분할 수 있다.

text
공인 IP
→ 인터넷에서 식별 가능한 주소

사설 IP
→ 내부 네트워크에서 사용하는 주소

예를 들어 집이나 회사의 내부 네트워크에서 사용하는

text
192.168.x.x
10.x.x.x
172.16.x.x ~ 172.31.x.x

등은 사설 IP 영역이다.

내 컴퓨터가

text
192.168.0.10

이라는 사설 IP를 사용한다고 해서 인터넷에 있는 모든 컴퓨터가 이 주소로 내 컴퓨터를 찾을 수 있는 것은 아니다.

Router

서로 다른 네트워크 사이에서 데이터를 전달하는 역할을 하는 장비가 Router다.

예를 들어 내 컴퓨터가 다른 네트워크에 있는 서버에 데이터를 보내려고 한다고 해보자.

text
내 컴퓨터
192.168.0.10
      ↓
Router
      ↓
인터넷
      ↓
서버
203.0.113.10

Router는 패킷의 목적지 IP 주소를 보고 어느 방향으로 전달할지 결정한다.

이러한 과정을 라우팅(Routing)이라고 한다.

Subnet

네트워크를 이해하다 보면 Subnet(서브넷)이라는 개념도 등장한다.

IP 주소는 단순히 하나의 숫자로만 사용되는 것이 아니라 네트워크 영역과 호스트 영역을 구분할 수 있다.

예를 들어 다음과 같은 네트워크가 있다고 해보자.

text
192.168.0.0/24

여기서 /24는 앞쪽 24비트가 네트워크 부분이라는 의미다.

따라서 같은 네트워크에 속하는 주소들을 하나의 범위로 관리할 수 있다.

Subnet은 네트워크를 효율적으로 나누고 관리하기 위해 사용된다.

NAT

그렇다면 사설 IP를 사용하는 컴퓨터는 어떻게 인터넷에 접속할 수 있을까?

여기서 NAT(Network Address Translation)가 사용된다.

예를 들어 내부 컴퓨터가

text
192.168.0.10

이라는 사설 IP를 사용하고 있고 공유기가 공인 IP

text
203.0.113.5

를 가지고 있다고 해보자.

외부 서버와 통신할 때 공유기는 내부의 사설 IP와 외부에서 사용하는 공인 IP 사이를 변환한다.

text
내 컴퓨터
192.168.0.10
      ↓
   Router / NAT
      ↓
203.0.113.5
      ↓
인터넷

이 덕분에 하나의 공인 IP를 여러 내부 장치가 공유할 수 있다.

3. TCP

이제 DNS를 통해 IP 주소를 알아냈고, IP를 통해 목적지까지 데이터를 전달할 수 있다는 것도 알았다.

그런데 여기서 또 하나의 문제가 생긴다.

IP는 데이터를 목적지까지 전달하는 역할을 하지만, 데이터가 정확하게 도착했는지까지 보장하지 않는다.

네트워크에서 패킷은 손실될 수도 있고 순서가 바뀔 수도 있다.

그래서 필요한 것이 TCP(Transmission Control Protocol)다.

IP와 TCP의 역할은 다르다

이 둘을 같이 이해하는 것이 중요하다.

text
IP
→ 어디로 보낼 것인가?

TCP
→ 데이터를 어떻게 안정적으로 전달할 것인가?

IP는 목적지까지 패킷을 전달하는 데 집중하고,

TCP는 연결과 데이터 전달의 신뢰성을 관리한다.

Port

IP 주소만으로는 부족한 경우가 있다.

하나의 컴퓨터에서 여러 프로그램이 동시에 네트워크 통신을 할 수 있기 때문이다.

예를 들어 하나의 서버에서

웹 서버

데이터베이스

메일 서버

등이 동시에 동작할 수 있다.

그래서 IP 주소와 함께 Port를 사용한다.

text
IP
203.0.113.10

Port
443

이 둘을 통해 네트워크상에서 특정 통신 대상을 식별할 수 있다.

쉽게 생각하면,

text
IP
→ 어느 컴퓨터인가?

Port
→ 그 컴퓨터의 어떤 네트워크 서비스인가?

라고 이해할 수 있다.

TCP 3-way Handshake

TCP는 데이터를 보내기 전에 연결을 설정한다.

이때 대표적으로 사용하는 과정이 3-way Handshake다.

text
Client                  Server

   SYN  ───────────────→
        ←──────── SYN + ACK
   ACK  ───────────────→

#### 1. SYN

클라이언트가 서버에게 연결을 요청한다.

text
Client → Server
SYN

#### 2. SYN + ACK

서버는 요청을 받았다는 의미로 응답하고 자신의 연결 요청도 전달한다.

text
Server → Client
SYN + ACK

#### 3. ACK

클라이언트가 다시 응답한다.

text
Client → Server
ACK

이 과정을 거쳐 TCP 연결이 설정된다.

TCP는 데이터를 어떻게 안정적으로 전달할까?

TCP는 데이터를 여러 개의 세그먼트로 나누어 전송할 수 있다.

그리고 각 데이터에 Sequence Number를 부여한다.

text
데이터 1 → Sequence 1
데이터 2 → Sequence 2
데이터 3 → Sequence 3

수신자는 데이터를 받으면 ACK를 통해 정상적으로 받은 내용을 알려준다.

text
Client → Server
데이터

Server → Client
ACK

만약 특정 데이터가 정상적으로 도착하지 않았다면 재전송할 수 있다.

이런 과정을 통해 TCP는

  • 데이터 순서 보장
  • 손실된 데이터 재전송
  • 중복 데이터 처리
  • 수신 측의 처리 속도에 맞춘 흐름 제어

등을 제공한다.

흐름 제어와 혼잡 제어

TCP에는 흐름 제어(Flow Control)와 혼잡 제어(Congestion Control)라는 개념도 있다.

흐름 제어는 수신자가 감당할 수 있는 양보다 데이터를 너무 많이 보내지 않도록 조절한다.

반면 혼잡 제어는 네트워크 자체가 혼잡해졌을 때 전송량을 조절하는 개념이다.

즉,

text
흐름 제어
→ 상대방이 감당할 수 있는가?

혼잡 제어
→ 네트워크가 감당할 수 있는가?

라는 차이가 있다.

4. QUIC

TCP는 오랫동안 인터넷 통신의 핵심적인 역할을 해왔다.

하지만 웹이 발전하면서 TCP 기반 통신에서도 개선하고 싶은 부분들이 생겼다.

특히 연결 설정 과정과 TCP의 전송 구조에서 발생하는 여러 제약을 개선하기 위해 등장한 것이 QUIC이다.

UDP는 왜 등장하는가?

QUIC을 이해하려면 먼저 UDP를 알아야 한다.

TCP는 신뢰성 있는 데이터 전달을 위해 많은 기능을 제공한다.

반면 UDP(User Datagram Protocol)는 훨씬 단순하다.

text
UDP
→ 데이터를 보내는 기본적인 기능만 제공

TCP처럼 연결 설정, 재전송, 순서 보장 등을 기본적으로 제공하지 않는다.

그래서 UDP 자체가 TCP보다 "더 좋은 프로토콜"이라는 의미는 아니다.

각각 목적이 다르다.

QUIC은 UDP 위에서 동작한다

QUIC은 UDP를 기반으로 만들어졌다.

하지만 단순히 UDP로 데이터를 보내는 것이 아니다.

QUIC은 UDP 위에 TCP가 제공하던 신뢰성 있는 데이터 전달과 연결 관리 등의 기능을 구현한다.

text
HTTP/3
   ↓
QUIC
   ↓
UDP
   ↓
IP

그리고 QUIC에는 TLS가 깊게 통합되어 있다.

QUIC의 Stream

QUIC의 중요한 특징 중 하나가 Stream이다.

하나의 QUIC 연결 안에서 여러 개의 독립적인 Stream을 사용할 수 있다.

text
하나의 QUIC Connection
 ├─ Stream 1
 ├─ Stream 2
 ├─ Stream 3
 └─ Stream 4

각 Stream의 데이터가 독립적으로 처리될 수 있기 때문에 TCP의 단일 바이트 스트림 구조에서 발생하는 문제를 완화할 수 있다.

특히 하나의 패킷 손실이 다른 독립적인 Stream의 데이터 처리까지 막는 문제를 줄일 수 있다.

다만 QUIC도 네트워크에서 패킷 손실 자체가 사라지는 것은 아니다.

Connection Migration

QUIC의 또 다른 특징은 Connection Migration이다.

TCP 연결은 IP와 Port의 조합에 영향을 받기 때문에 네트워크 환경이 바뀌면 연결을 다시 만들어야 하는 상황이 발생할 수 있다.

예를 들어 스마트폰이

text
Wi-Fi
 ↓
LTE / 5G

로 전환되는 경우다.

QUIC은 Connection ID를 활용하여 네트워크 경로가 변경되는 상황에서도 연결을 유지할 수 있도록 설계되어 있다.

5. TLS

이제 서버와 통신할 수 있는 연결이 만들어졌다고 해보자.

그런데 데이터를 그대로 보내도 괜찮을까?

예를 들어 로그인 요청을 보낸다고 생각해보자.

text
id=hello
password=1234

이 데이터가 네트워크에서 아무런 보호 없이 전달된다면 중간에서 데이터를 볼 수 있는 문제가 생길 수 있다.

또한 서버와 통신하고 있다고 생각했는데 실제로는 공격자가 만든 서버일 수도 있다.

이런 문제를 해결하기 위해 사용하는 것이 TLS(Transport Layer Security)다.

TLS가 제공하는 것

TLS의 핵심적인 목적은 크게 세 가지로 볼 수 있다.

text
기밀성
→ 다른 사람이 데이터를 쉽게 볼 수 없도록 한다.

무결성
→ 데이터가 통신 중 변조되지 않았는지 확인한다.

인증
→ 통신 상대가 누구인지 확인한다.

대칭키와 공개키

TLS를 이해할 때 자주 등장하는 것이 대칭키와 공개키 암호화다.

#### 대칭키

하나의 키를 사용해 암호화와 복호화를 한다.

text
평문
 ↓
대칭키
 ↓
암호문

대칭키 암호화는 실제 데이터를 빠르게 암호화하는 데 적합하다.

하지만 상대방과 처음에 같은 키를 안전하게 공유해야 한다는 문제가 있다.

#### 공개키 암호화

공개키와 개인키라는 한 쌍의 키를 사용한다.

text
공개키
→ 공개 가능

개인키
→ 서버가 비밀리에 보관

공개키 암호화는 대칭키보다 상대적으로 연산 비용이 크기 때문에 실제 모든 데이터를 공개키 암호화로 처리하는 방식은 효율적이지 않다.

TLS에서는 안전한 연결을 설정하는 과정에서 암호학적 기능을 사용하고, 실제 데이터 통신에서는 효율적인 대칭키 기반 암호화를 사용한다.

인증서와 CA

그렇다면 브라우저는 어떻게 서버가 진짜 yong-hee.com인지 확인할까?

여기서 인증서(Certificate)와 CA(Certificate Authority)가 등장한다.

서버는 자신의 공개키와 도메인 정보 등이 포함된 인증서를 가지고 있다.

그리고 이 인증서를 신뢰할 수 있는 CA가 서명한다.

브라우저는 신뢰할 수 있는 CA 목록을 기반으로 인증서의 신뢰성을 확인할 수 있다.

text
브라우저
   ↓
서버 인증서 확인
   ↓
신뢰할 수 있는 CA인가?
   ↓
도메인 정보가 맞는가?
   ↓
인증서가 유효한가?

이 과정을 통해 브라우저는 연결하려는 서버의 신원을 검증할 수 있다.

TLS Handshake

TLS에서는 안전한 통신을 시작하기 전에 서로 필요한 정보를 교환하고 암호화에 사용할 키를 안전하게 설정한다.

현대적인 TLS에서는 TLS 1.3이 널리 사용되고 있으며, 연결 설정 과정도 이전 버전보다 간소화되었다.

개념적으로는 다음과 같이 볼 수 있다.

text
Client
  ↓
TLS 연결 요청
  ↓
Server
  ↓
인증서 및 필요한 암호화 정보 전달
  ↓
Client
  ↓
서버 인증 및 키 설정
  ↓
암호화된 통신

결국 우리가 사용하는

text
https://yong-hee.com

에서 HTTPS는 HTTP를 TLS로 보호하여 전달하는 방식이라고 이해할 수 있다.

6. HTTP

이제 서버와 안전하게 통신할 수 있는 기반이 만들어졌다.

그렇다면 실제로 어떤 형식으로 데이터를 주고받을까?

여기서 등장하는 것이 HTTP(HyperText Transfer Protocol)다.

HTTP는 웹에서 클라이언트와 서버가 데이터를 주고받기 위한 애플리케이션 계층의 프로토콜이다.

Request와 Response

HTTP 통신의 가장 기본적인 형태는 다음과 같다.

text
Client
   │
   │ HTTP Request
   ↓
Server
   │
   │ HTTP Response
   ↓
Client

브라우저가 웹페이지를 요청하면 HTTP Request를 보내고, 서버는 HTTP Response를 반환한다.

HTTP Request

예를 들어 브라우저가 다음 URL을 요청한다고 해보자.

text
GET /users HTTP/1.1
Host: yong-hee.com

HTTP Request에는 여러 정보가 들어간다.

대표적으로

  • Method
  • URL / Path
  • Header
  • Body

등이 있다.

HTTP Method

HTTP에는 요청의 목적을 표현하기 위한 Method가 있다.

대표적으로

  • GET
  • POST
  • PUT
  • PATCH
  • DELETE

가 있다.

예를 들어

text
GET /users

는 사용자의 데이터를 가져오기 위한 요청으로 사용할 수 있다.

text
POST /users

는 새로운 사용자를 생성하는 요청으로 사용할 수 있다.

다만 HTTP Method 자체가 반드시 서버의 비즈니스 동작을 강제하는 것은 아니다. 실제 의미는 API 설계에 따라 결정된다.

HTTP Header

Header는 요청과 응답에 대한 부가적인 정보를 전달한다.

예를 들어

text
Content-Type: application/json
Authorization: Bearer ...
Accept: application/json

등이 있다.

프론트엔드 개발을 하면서 자주 보는

jsx
fetch('/api/users', {
  headers: {
    Authorization: `Bearer ${token}`
  }
});

도 HTTP Header를 설정하는 것이다.

HTTP Body

Body에는 실제로 전달할 데이터가 들어갈 수 있다.

예를 들어 JSON 데이터를 서버에 보낸다면

json
{
  "name": "Yonghee",
  "age": 30
}

같은 데이터가 Body에 들어갈 수 있다.

GET 요청은 일반적으로 Body를 사용하지 않고, POST나 PUT, PATCH 같은 요청에서 데이터를 전달할 때 Body를 사용하는 경우가 많다.

HTTP Status Code

서버는 Response와 함께 상태 코드도 반환한다.

대표적으로 다음과 같다.

text
2xx
→ 요청 성공

3xx
→ 추가적인 처리나 다른 위치로의 이동

4xx
→ 클라이언트 요청에 문제가 있음

5xx
→ 서버에서 요청을 처리하는 중 문제가 발생

예를 들어

  • 200 OK
  • 201 Created
  • 301 Moved Permanently
  • 400 Bad Request
  • 401 Unauthorized
  • 403 Forbidden
  • 404 Not Found
  • 500 Internal Server Error

등이 있다.

HTTP는 Stateless하다

HTTP의 중요한 특징 중 하나가 Stateless라는 것이다.

Stateless는 서버가 각각의 요청을 기본적으로 독립적인 요청으로 처리한다는 의미다.

예를 들어

text
Request 1
"내 사용자 정보 줘"

Request 2
"내 주문 정보 줘"

라고 요청한다고 해서 HTTP 자체가 두 요청을 자동으로 하나의 세션으로 기억하는 것은 아니다.

로그인 상태와 같은 정보를 유지하기 위해서는 Cookie, Session, Token 등의 별도 메커니즘을 사용할 수 있다.

7. HTTP/1.1

HTTP라는 프로토콜은 하나의 고정된 버전이 아니다.

시간이 지나면서 웹의 요구사항에 맞게 발전해왔다.

그중 오랫동안 널리 사용된 버전이 HTTP/1.1이다.

Persistent Connection

HTTP/1.0에서는 요청마다 연결을 새로 만드는 방식의 비효율이 있었다.

HTTP/1.1에서는 Persistent Connection을 통해 하나의 TCP 연결을 여러 HTTP 요청에서 재사용할 수 있게 됐다.

text
TCP 연결
 ├─ HTTP Request 1
 ├─ HTTP Response 1
 ├─ HTTP Request 2
 ├─ HTTP Response 2
 └─ ...

연결을 매번 새로 만들 필요가 없기 때문에 효율이 좋아졌다.

HTTP/1.1의 한계

하지만 HTTP/1.1에도 문제가 있었다.

HTTP/1.1의 기본적인 요청/응답 구조에서는 하나의 연결에서 요청이 순차적으로 처리되는 상황이 생길 수 있다.

예를 들어

text
Request 1
   ↓
Response 1
   ↓
Request 2
   ↓
Response 2

처럼 진행된다.

만약 앞의 요청 처리가 오래 걸리면 뒤의 요청도 영향을 받을 수 있다.

이를 Head-of-Line Blocking이라고 한다.

그래서 브라우저는 HTTP/1.1에서 여러 TCP 연결을 동시에 사용하는 방식으로 병렬 요청을 처리하기도 했다.

하지만 연결을 여러 개 만들면 TCP 연결과 TLS 연결에 대한 비용이 추가로 발생한다.

이런 문제들을 개선하기 위해 HTTP/2가 등장한다.

8. HTTP/2

HTTP/2의 핵심은 하나의 TCP 연결에서 여러 HTTP 요청과 응답을 동시에 처리할 수 있도록 구조를 개선했다는 것이다.

Binary Protocol

HTTP/1.1은 사람이 읽을 수 있는 텍스트 형태를 기반으로 한다.

반면 HTTP/2는 데이터를 Binary Frame 단위로 처리한다.

text
HTTP/2
   ↓
Frame
   ↓
Stream

이렇게 데이터를 구조화해서 처리한다.

Stream과 Multiplexing

HTTP/2의 핵심 기능 중 하나가 Multiplexing이다.

하나의 TCP 연결 안에서 여러 Stream을 만들 수 있다.

text
하나의 TCP Connection
│
├── Stream 1 → Request A
├── Stream 3 → Request B
├── Stream 5 → Request C
└── Stream 7 → Request D

따라서 HTTP/1.1처럼 요청마다 별도의 TCP 연결을 만들어야 할 필요가 줄어든다.

HTTP/2의 Multiplexing

예를 들어 세 개의 리소스를 요청한다고 해보자.

HTTP/1.1에서는 여러 연결을 활용할 수 있다.

text
TCP 1 → Request A
TCP 2 → Request B
TCP 3 → Request C

HTTP/2에서는 하나의 TCP 연결 위에서 여러 Stream을 사용할 수 있다.

text
TCP
 │
 ├─ Stream A
 ├─ Stream B
 └─ Stream C

이것이 HTTP/2의 중요한 개선점이다.

Header Compression

HTTP 요청에는 Header가 반복적으로 포함되는 경우가 많다.

예를 들어 여러 요청에서

  • Cookie
  • User-Agent
  • Authorization
  • Accept

등이 반복될 수 있다.

HTTP/2에서는 HPACK이라는 Header Compression 방식을 사용해 헤더 전송의 중복을 줄인다.

HTTP/2에도 한계가 있다

HTTP/2는 HTTP/1.1의 여러 문제를 해결했지만 TCP 위에서 동작한다는 사실은 변하지 않는다.

여기서 중요한 문제가 하나 있다.

HTTP/2에서는 여러 Stream이 하나의 TCP 연결을 공유한다.

그런데 TCP는 데이터를 하나의 순서 있는 바이트 스트림으로 전달한다.

만약 TCP 레벨에서 특정 패킷이 손실되면 해당 패킷을 재전송하고 순서를 맞출 때까지 뒤에 있는 데이터의 전달이 영향을 받을 수 있다.

즉 HTTP/2의 Stream이 서로 독립적으로 보이더라도 TCP 계층의 Head-of-Line Blocking 영향을 받을 수 있다.

이 문제를 다른 방식으로 해결하기 위해 등장한 것이 QUIC이고, 그 위에서 동작하는 HTTP 버전이 HTTP/3이다.

9. HTTP/3

HTTP/3는 HTTP/2의 다음 버전이다.

가장 큰 차이는 전송 계층에서 TCP 대신 QUIC을 사용한다는 것이다.

text
HTTP/1.1
   ↓
TCP
   ↓
IP
text
HTTP/2
   ↓
TCP
   ↓
IP
text
HTTP/3
   ↓
QUIC
   ↓
UDP
   ↓
IP

HTTP/3는 HTTP 자체의 의미를 완전히 새롭게 만든 것이 아니라, HTTP를 전달하는 기반을 QUIC으로 변경한 것이다.

HTTP/3가 QUIC을 사용하는 이유

HTTP/2에서는 Multiplexing을 통해 여러 Stream을 하나의 TCP 연결에서 처리할 수 있었다.

하지만 TCP는 하나의 순서 있는 바이트 스트림이다.

따라서 특정 패킷이 손실되면 TCP 수준에서 해당 데이터를 복구하는 동안 다른 Stream의 데이터 처리에도 영향을 줄 수 있다.

QUIC은 Stream을 독립적으로 처리할 수 있도록 설계했다.

text
QUIC Connection
│
├── Stream 1
├── Stream 2
├── Stream 3
└── Stream 4

Stream 1에서 패킷 손실이 발생하더라도 Stream 2와 Stream 3의 데이터까지 TCP처럼 하나의 순서로 묶여 대기해야 하는 문제를 줄일 수 있다.

HTTP/2와 HTTP/3

두 버전을 비교하면 핵심적인 차이는 다음과 같다.

HTTP/2HTTP/3
전송 기반TCPQUIC
하위 프로토콜TCP → IPQUIC → UDP → IP
Multiplexing지원지원
Header CompressionHPACKQPACK
TCP Head-of-Line Blocking존재TCP 자체를 사용하지 않음
TLSTLS 사용QUIC에 TLS 1.3 통합

여기서 중요한 것은

HTTP/3가 단순히 HTTP/2보다 "빠른 HTTP"라는 것이 아니다.

HTTP/3는 QUIC이라는 새로운 전송 계층을 사용하면서 HTTP/2에서 남아 있던 전송 계층의 문제를 다른 방식으로 해결한 것에 가깝다.

전체를 다시 연결해보자

이제 각각의 개념을 하나씩 봤으니 처음의 https://yong-hee.com으로 다시 돌아가 보자.

브라우저가 웹사이트에 접속할 때 우리가 공부한 개념들은 서로 이렇게 연결된다.

text
https://yong-hee.com
          │
          ▼
        DNS
          │
          │ 도메인 → IP
          ▼
      IP 주소 확인
          │
          ▼
    TCP 또는 QUIC
          │
          │ 통신 기반 생성
          ▼
         TLS
          │
          │ 안전한 통신 설정
          ▼
        HTTP
          │
          ▼
 HTTP/1.1 / HTTP/2 / HTTP/3
          │
          ▼
   Request / Response
          │
          ▼
      서버 데이터

각각의 역할을 다시 정리하면 다음과 같다.

text
DNS
→ "서버가 어디에 있는지 찾아준다."

IP
→ "네트워크에서 목적지를 식별하고 패킷을 전달한다."

TCP
→ "데이터를 신뢰성 있게 전달한다."

QUIC
→ "UDP 기반에서 신뢰성 있는 전송과 독립적인 Stream 등을 제공한다."

TLS
→ "통신을 암호화하고 서버를 인증한다."

HTTP
→ "클라이언트와 서버가 어떤 형식으로 데이터를 주고받을지 정의한다."

HTTP/1.1
→ "HTTP 통신을 연결 재사용 방식으로 발전시켰다."

HTTP/2
→ "하나의 TCP 연결에서 여러 Stream을 처리할 수 있도록 개선했다."

HTTP/3
→ "QUIC을 기반으로 HTTP를 동작시켜 전송 계층의 한계를 개선했다."

그래서 HTTP/1.1 → HTTP/2 → HTTP/3는 왜 발전했을까?

이 흐름을 하나의 문제 해결 과정으로 보면 훨씬 이해하기 쉽다.

text
HTTP/1.1
   │
   ├─ 요청을 효율적으로 처리하고 싶다
   │
   ▼
HTTP/2
   │
   ├─ Multiplexing
   ├─ Header Compression
   │
   └─ 하지만 TCP의 구조적 한계는 남아 있다
   │
   ▼
HTTP/3
   │
   ├─ QUIC
   ├─ UDP 기반
   ├─ Stream 독립성
   └─ TCP 기반 전송의 한계를 다른 방식으로 해결

결국 네트워크 프로토콜의 발전을 단순히

HTTP/1.1보다 HTTP/2가 좋고, HTTP/2보다 HTTP/3가 좋다.

라고 외우는 것보다,

"기존 방식에서 어떤 문제가 있었고, 다음 기술이 그 문제를 어떻게 해결했는가?"

라는 관점으로 보는 것이 훨씬 중요하다.

마무리

처음에는

  • DNS
  • IP
  • TCP
  • QUIC
  • TLS
  • HTTP
  • HTTP/1.1
  • HTTP/2
  • HTTP/3

라는 단어들이 서로 아무 관계가 없는 기술처럼 보일 수 있다.

하지만 실제로는 하나의 통신 과정에서 서로 다른 문제를 해결하고 있다.

text
도메인을 알고 있다
      ↓
DNS
서버의 IP를 찾는다
      ↓
IP
목적지까지 패킷을 전달한다
      ↓
TCP / QUIC
신뢰성 있는 통신 기반을 만든다
      ↓
TLS
통신을 안전하게 만든다
      ↓
HTTP
데이터를 어떤 형식으로 주고받을지 정한다
      ↓
HTTP/1.1 → HTTP/2 → HTTP/3
웹 환경에 맞게 HTTP 통신 방식을 발전시켜 왔다

그리고 이 모든 과정은 결국 브라우저가 서버로부터 HTML, CSS, JavaScript와 같은 리소스를 받아오기 위한 준비 과정이다.

이제 네트워크를 지나 브라우저에 HTML이 도착했다.

그렇다면 다음 질문이 생긴다.

브라우저는 서버에서 받은 HTML과 CSS를 어떻게 우리가 보는 화면으로 만들어낼까?

다음에는 브라우저 렌더링 과정으로 넘어가 보자.