HTTP 프로토콜 완벽 가이드 | HTTP/1.1·HTTP/2·HTTP/3·QUIC 비교

HTTP 프로토콜 완벽 가이드 | HTTP/1.1·HTTP/2·HTTP/3·QUIC 비교

이 글의 핵심

HTTP/1.1, HTTP/2, HTTP/3, QUIC의 동작 원리와 진화 과정. 멀티플렉싱, 헤더 압축, 0-RTT, 패킷 손실 복구까지. 실전 성능 비교와 최적화 전략 완벽 마스터.

들어가며: 웹의 진화

”웹 페이지가 왜 이렇게 느릴까?”

웹 페이지를 열 때 로딩 시간이 길거나, 모바일에서 끊김이 발생하거나, 이미지가 순차적으로 나타나는 경험을 해보셨나요? 이는 HTTP 프로토콜의 한계 때문입니다. HTTP/1.1은 1997년에 설계되어 현대 웹의 복잡성을 따라가지 못했고, HTTP/2는 멀티플렉싱으로 개선했지만 TCP의 HOL Blocking 문제가 남아있었습니다. HTTP/3와 QUIC은 UDP 기반으로 이 모든 문제를 해결했습니다.

HTTP 프로토콜의 진화:

  • HTTP/0.9 (1991): 단순 GET 요청
  • HTTP/1.0 (1996): 헤더, 메서드 추가
  • HTTP/1.1 (1997): Keep-Alive, 파이프라이닝
  • HTTP/2 (2015): 멀티플렉싱, 헤더 압축
  • HTTP/3 (2022): QUIC, UDP 기반

목차

  1. HTTP/1.1
  2. HTTPS와 TLS
  3. HTTP/2
  4. HTTP/3와 QUIC
  5. 프로토콜 비교
  6. 실전 구현
  7. 성능 최적화

1. HTTP/1.1

HTTP/1.1 기본 구조

sequenceDiagram
    participant C as Client
    participant S as Server

    Note over C,S: TCP 3-Way Handshake
    C->>S: SYN
    S-->>C: SYN-ACK
    C->>S: ACK
    
    Note over C,S: HTTP 요청/응답
    C->>S: GET /index.html HTTP/1.1<br/>Host: example.com
    S-->>C: HTTP/1.1 200 OK<br/>Content-Type: text/html<br/><br/><html>...
    
    C->>S: GET /style.css HTTP/1.1
    S-->>C: HTTP/1.1 200 OK<br/>Content-Type: text/css<br/><br/>body {...}
    
    C->>S: GET /script.js HTTP/1.1
    S-->>C: HTTP/1.1 200 OK<br/>Content-Type: text/javascript<br/><br/>function...
    
    Note over C,S: 연결 종료 (또는 Keep-Alive)

HTTP/1.1 요청 메시지

GET /api/users/123 HTTP/1.1
Host: api.example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Accept: application/json
Accept-Language: ko-KR,ko;q=0.9,en;q=0.8
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Cookie: session_id=abc123; user_pref=dark
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

HTTP/1.1 응답 메시지

HTTP/1.1 200 OK
Date: Wed, 01 Apr 2026 10:00:00 GMT
Server: nginx/1.24.0
Content-Type: application/json; charset=utf-8
Content-Length: 1234
Connection: keep-alive
Cache-Control: max-age=3600
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
Last-Modified: Wed, 01 Apr 2026 09:00:00 GMT
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999

{
  "id": 123,
  "name": "John Doe",
  "email": "[email protected]"
}

HTTP/1.1 메서드

# GET (조회)
GET /api/users HTTP/1.1

# POST (생성)
POST /api/users HTTP/1.1
Content-Type: application/json

{
  "name": "John Doe",
  "email": "[email protected]"
}

# PUT (전체 수정)
PUT /api/users/123 HTTP/1.1
Content-Type: application/json

{
  "name": "Jane Doe",
  "email": "[email protected]"
}

# PATCH (부분 수정)
PATCH /api/users/123 HTTP/1.1
Content-Type: application/json

{
  "email": "[email protected]"
}

# DELETE (삭제)
DELETE /api/users/123 HTTP/1.1

# HEAD (헤더만 조회)
HEAD /api/users/123 HTTP/1.1

# OPTIONS (지원 메서드 확인)
OPTIONS /api/users HTTP/1.1

HTTP/1.1 상태 코드

1xx: 정보
  100 Continue
  101 Switching Protocols

2xx: 성공
  200 OK
  201 Created
  204 No Content
  206 Partial Content

3xx: 리다이렉션
  301 Moved Permanently
  302 Found (Temporary Redirect)
  304 Not Modified
  307 Temporary Redirect
  308 Permanent Redirect

4xx: 클라이언트 오류
  400 Bad Request
  401 Unauthorized
  403 Forbidden
  404 Not Found
  405 Method Not Allowed
  408 Request Timeout
  429 Too Many Requests

5xx: 서버 오류
  500 Internal Server Error
  502 Bad Gateway
  503 Service Unavailable
  504 Gateway Timeout

HTTP/1.1 문제점

flowchart TB
    subgraph Problems[HTTP/1.1 문제점]
        P1[🐌 HOL Blocking<br/>Head-of-Line Blocking]
        P2[📦 헤더 중복<br/>매 요청마다 반복]
        P3[🔗 연결 제한<br/>브라우저당 6-8개]
        P4[⏱️ 느린 시작<br/>TCP + TLS 핸드셰이크]
    end
    
    subgraph Impact[영향]
        I1[페이지 로딩 지연]
        I2[대역폭 낭비]
        I3[연결 관리 복잡]
        I4[모바일 성능 저하]
    end
    
    Problems --> Impact

HOL Blocking 예시:

연결 1: [HTML ████████████████] 완료
연결 2: [CSS ████░░░░░░░░░░░░] 대기 중... (패킷 손실)
연결 3: [JS ░░░░░░░░░░░░░░░░] 대기 중... (CSS 완료 대기)
연결 4: [IMG ░░░░░░░░░░░░░░░] 대기 중...

→ CSS 하나가 지연되면 나머지 모두 대기

2. HTTPS와 TLS

HTTPS란?

HTTPS는 HTTP over TLS/SSL로, HTTP 통신을 암호화하여 보안을 제공합니다.

TLS 핸드셰이크 (TLS 1.2)

sequenceDiagram
    participant C as Client
    participant S as Server

    Note over C,S: TCP 3-Way Handshake (1-RTT)
    C->>S: SYN
    S-->>C: SYN-ACK
    C->>S: ACK
    
    Note over C,S: TLS 핸드셰이크 (2-RTT)
    C->>S: ClientHello<br/>(지원 암호화 알고리즘)
    S-->>C: ServerHello<br/>(선택된 암호화, 인증서)
    S-->>C: Certificate<br/>(서버 인증서)
    S-->>C: ServerHelloDone
    
    C->>S: ClientKeyExchange<br/>(암호화된 세션 키)
    C->>S: ChangeCipherSpec
    C->>S: Finished
    
    S-->>C: ChangeCipherSpec
    S-->>C: Finished
    
    Note over C,S: 암호화된 HTTP 통신
    C->>S: GET /index.html (암호화)
    S-->>C: HTTP/1.1 200 OK (암호화)

총 지연: 3-RTT (TCP 1-RTT + TLS 2-RTT)

TLS 1.3 (개선)

sequenceDiagram
    participant C as Client
    participant S as Server

    Note over C,S: TCP 3-Way Handshake (1-RTT)
    C->>S: SYN
    S-->>C: SYN-ACK
    C->>S: ACK
    
    Note over C,S: TLS 1.3 핸드셰이크 (1-RTT)
    C->>S: ClientHello<br/>(암호화 알고리즘 + 키 공유)
    S-->>C: ServerHello + Certificate + Finished<br/>(한 번에 전송)
    
    Note over C,S: 즉시 암호화 통신 가능
    C->>S: GET /index.html (암호화)
    S-->>C: HTTP/1.1 200 OK (암호화)

총 지연: 2-RTT (TCP 1-RTT + TLS 1-RTT)

HTTPS 구현 (Node.js)

const https = require('https');
const fs = require('fs');
const express = require('express');

const app = express();

// SSL/TLS 인증서 로드
const options = {
  key: fs.readFileSync('/etc/ssl/private/server.key'),
  cert: fs.readFileSync('/etc/ssl/certs/server.crt'),
  
  // TLS 1.3 강제
  minVersion: 'TLSv1.3',
  
  // 암호화 스위트 설정
  ciphers: [
    'TLS_AES_128_GCM_SHA256',
    'TLS_AES_256_GCM_SHA384',
    'TLS_CHACHA20_POLY1305_SHA256'
  ].join(':'),
  
  // HSTS (HTTP Strict Transport Security)
  honorCipherOrder: true
};

app.get('/', (req, res) => {
  res.send('Hello HTTPS!');
});

// HTTPS 서버 시작
https.createServer(options, app).listen(443, () => {
  console.log('✅ HTTPS Server running on port 443');
});

// HTTP → HTTPS 리다이렉트
const http = require('http');
http.createServer((req, res) => {
  res.writeHead(301, { Location: `https://${req.headers.host}${req.url}` });
  res.end();
}).listen(80);

Let’s Encrypt (무료 SSL)

# Certbot 설치
sudo apt install certbot python3-certbot-nginx

# Nginx용 인증서 발급
sudo certbot --nginx -d example.com -d www.example.com

# 수동 발급
sudo certbot certonly --standalone -d example.com

# 자동 갱신 (90일마다)
sudo certbot renew --dry-run

# Cron 등록
0 0 * * * certbot renew --quiet --post-hook "systemctl reload nginx"

3. HTTP/2

HTTP/2란?

HTTP/2는 2015년에 표준화된 프로토콜로, 멀티플렉싱, 헤더 압축, 서버 푸시 등으로 성능을 대폭 개선했습니다.

HTTP/1.1 vs HTTP/2

flowchart TB
    subgraph HTTP1[HTTP/1.1]
        direction TB
        C1[Connection 1] --> R1[HTML]
        C2[Connection 2] --> R2[CSS]
        C3[Connection 3] --> R3[JS]
        C4[Connection 4] --> R4[IMG1]
        C5[Connection 5] --> R5[IMG2]
        C6[Connection 6] --> R6[IMG3]
    end
    
    subgraph HTTP2[HTTP/2]
        direction TB
        MC[Single Connection<br/>Multiplexing]
        MC --> S1[Stream 1: HTML]
        MC --> S2[Stream 2: CSS]
        MC --> S3[Stream 3: JS]
        MC --> S4[Stream 4: IMG1]
        MC --> S5[Stream 5: IMG2]
        MC --> S6[Stream 6: IMG3]
    end
    
    style HTTP1 fill:#fcc
    style HTTP2 fill:#cfc

HTTP/2 주요 기능

1. 멀티플렉싱 (Multiplexing)

sequenceDiagram
    participant C as Client
    participant S as Server

    Note over C,S: 단일 TCP 연결
    
    par Stream 1 (HTML)
        C->>S: HEADERS (Stream 1)
        S-->>C: HEADERS + DATA (Stream 1)
    and Stream 2 (CSS)
        C->>S: HEADERS (Stream 2)
        S-->>C: HEADERS + DATA (Stream 2)
    and Stream 3 (JS)
        C->>S: HEADERS (Stream 3)
        S-->>C: HEADERS + DATA (Stream 3)
    end
    
    Note over C,S: 동시에 여러 리소스 전송

2. 헤더 압축 (HPACK)

HTTP/1.1 (반복되는 헤더):
GET /page1 HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0...
Accept: text/html...
Cookie: session=abc123...
[총 500 bytes]

GET /page2 HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0...
Accept: text/html...
Cookie: session=abc123...
[총 500 bytes]

→ 1000 bytes 전송

HTTP/2 (HPACK 압축):
Stream 1: [전체 헤더] 500 bytes
Stream 2: [차이만] 50 bytes

→ 550 bytes 전송 (45% 절감)

3. 서버 푸시 (Server Push)

sequenceDiagram
    participant C as Client
    participant S as Server

    C->>S: GET /index.html
    
    Note over S: HTML 파싱 후<br/>필요한 리소스 예측
    
    S-->>C: PUSH_PROMISE (Stream 2: /style.css)
    S-->>C: PUSH_PROMISE (Stream 4: /script.js)
    
    S-->>C: HEADERS + DATA (Stream 1: HTML)
    S-->>C: HEADERS + DATA (Stream 2: CSS)
    S-->>C: HEADERS + DATA (Stream 4: JS)
    
    Note over C: 클라이언트 요청 전에<br/>리소스 수신 완료

4. 스트림 우선순위

flowchart TB
    Root[Root]
    
    Root -->|Weight: 256| HTML[HTML<br/>Stream 1]
    Root -->|Weight: 128| CSS[CSS<br/>Stream 2]
    Root -->|Weight: 64| JS[JS<br/>Stream 3]
    Root -->|Weight: 32| IMG[Images<br/>Stream 4-10]
    
    style HTML fill:#f96
    style CSS fill:#fc6
    style JS fill:#6cf
    style IMG fill:#9c6

HTTP/2 프레임 구조

+-----------------------------------------------+
|                 Length (24)                   |
+---------------+---------------+---------------+
|   Type (8)    |   Flags (8)   |
+-+-------------+---------------+-------------------------------+
|R|                 Stream Identifier (31)                      |
+=+=============================================================+
|                   Frame Payload (0...)                      ...
+---------------------------------------------------------------+

프레임 타입:
- DATA: 실제 데이터
- HEADERS: HTTP 헤더
- PRIORITY: 스트림 우선순위
- RST_STREAM: 스트림 종료
- SETTINGS: 연결 설정
- PUSH_PROMISE: 서버 푸시
- PING: 연결 확인
- GOAWAY: 연결 종료
- WINDOW_UPDATE: 흐름 제어

HTTP/2 서버 설정 (Nginx)

# /etc/nginx/nginx.conf
server {
    listen 443 ssl http2;
    server_name example.com;
    
    # SSL 인증서
    ssl_certificate /etc/ssl/certs/example.com.crt;
    ssl_certificate_key /etc/ssl/private/example.com.key;
    
    # TLS 설정
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;
    
    # HTTP/2 설정
    http2_push_preload on;
    
    location / {
        root /var/www/html;
        index index.html;
        
        # 서버 푸시
        add_header Link "</style.css>; rel=preload; as=style";
        add_header Link "</script.js>; rel=preload; as=script";
    }
}

HTTP/2 클라이언트 (Python)

import httpx
import asyncio

async def http2_example():
    """HTTP/2 클라이언트"""
    
    async with httpx.AsyncClient(http2=True) as client:
        # 단일 요청
        response = await client.get('https://example.com/')
        print(f"Protocol: {response.http_version}")
        print(f"Status: {response.status_code}")
        
        # 병렬 요청 (멀티플렉싱)
        urls = [
            'https://example.com/page1',
            'https://example.com/page2',
            'https://example.com/page3',
        ]
        
        tasks = [client.get(url) for url in urls]
        responses = await asyncio.gather(*tasks)
        
        for i, resp in enumerate(responses):
            print(f"URL {i+1}: {resp.status_code} ({len(resp.content)} bytes)")

asyncio.run(http2_example())

4. HTTP/3와 QUIC

HTTP/3란?

HTTP/3는 TCP 대신 QUIC(UDP 기반)을 사용하는 차세대 프로토콜로, 2022년에 표준화되었습니다.

TCP vs QUIC 연결 설정

sequenceDiagram
    participant C as Client
    participant S as Server

    Note over C,S: HTTP/2 over TLS 1.3 (2-RTT)
    
    C->>S: TCP SYN
    S-->>C: TCP SYN-ACK
    C->>S: TCP ACK + TLS ClientHello
    S-->>C: TLS ServerHello + Certificate + Finished
    C->>S: HTTP Request
    S-->>C: HTTP Response
    
    Note over C,S: 총 2-RTT 후 데이터 전송
sequenceDiagram
    participant C as Client
    participant S as Server

    Note over C,S: HTTP/3 over QUIC (1-RTT)
    
    C->>S: QUIC Initial Packet<br/>(ClientHello + 연결 설정)
    S-->>C: QUIC Handshake Packet<br/>(ServerHello + Certificate + HTTP)
    C->>S: HTTP Request (암호화)
    S-->>C: HTTP Response (암호화)
    
    Note over C,S: 총 1-RTT 후 데이터 전송
sequenceDiagram
    participant C as Client
    participant S as Server

    Note over C,S: HTTP/3 0-RTT (재연결)
    
    C->>S: QUIC 0-RTT Packet<br/>(이전 세션 키 + HTTP Request)
    S-->>C: HTTP Response (암호화)
    
    Note over C,S: 즉시 데이터 전송 (0-RTT)

QUIC 주요 특징

1. HOL Blocking 해결

flowchart TB
    subgraph TCP[HTTP/2 (TCP)]
        direction LR
        T1[TCP Stream]
        T1 --> P1[Packet 1 ✅]
        T1 --> P2[Packet 2 ❌ Lost]
        T1 --> P3[Packet 3 ⏸️ Blocked]
        T1 --> P4[Packet 4 ⏸️ Blocked]
    end
    
    subgraph QUIC[HTTP/3 (QUIC)]
        direction LR
        Q1[Stream 1] --> QP1[Packet 1 ✅]
        Q2[Stream 2] --> QP2[Packet 2 ❌ Lost]
        Q3[Stream 3] --> QP3[Packet 3 ✅ Continue]
        Q4[Stream 4] --> QP4[Packet 4 ✅ Continue]
    end
    
    style TCP fill:#fcc
    style QUIC fill:#cfc

TCP: 하나의 패킷 손실이 모든 스트림을 차단
QUIC: 각 스트림이 독립적, 손실된 스트림만 영향

2. 연결 마이그레이션

flowchart LR
    subgraph Before[Wi-Fi 연결]
        C1[Client<br/>IP: 192.168.1.100]
        S1[Server]
        C1 -->|QUIC Connection ID: abc123| S1
    end
    
    subgraph After[4G 전환]
        C2[Client<br/>IP: 203.0.113.50]
        S2[Server]
        C2 -->|Same Connection ID: abc123| S2
    end
    
    Before -.->|Network Switch| After
    
    style After fill:#cfc

TCP: IP 변경 시 연결 끊김
QUIC: Connection ID로 연결 유지

3. 패킷 손실 복구

TCP:
  Send: [1] [2] [3] [4] [5]
  Lost:      ❌
  Recv: [1]     [3] [4] [5]
  Wait: ⏸️ 재전송 대기 (모든 데이터 차단)
  Retx:      [2]
  
QUIC:
  Send: [1] [2] [3] [4] [5]
  Lost:      ❌
  Recv: [1]     [3] [4] [5] (계속 처리)
  Retx:      [2] (새 패킷 번호로 재전송)
  Recv:      [2]

QUIC 프로토콜 스택

flowchart TB
    subgraph App[Application Layer]
        HTTP3[HTTP/3]
    end
    
    subgraph Transport[Transport Layer]
        QUIC[QUIC]
        subgraph QF[QUIC Features]
            Crypto[암호화<br/>TLS 1.3]
            Stream[스트림<br/>멀티플렉싱]
            Flow[흐름 제어]
            Congestion[혼잡 제어]
            Loss[손실 복구]
        end
    end
    
    subgraph Network[Network Layer]
        UDP[UDP]
        IP[IP]
    end
    
    HTTP3 --> QUIC
    QUIC --> QF
    QUIC --> UDP
    UDP --> IP

HTTP/3 서버 설정 (Nginx)

# Nginx 1.25.0+ (HTTP/3 지원)
server {
    listen 443 ssl;
    listen 443 quic reuseport;
    
    http2 on;
    http3 on;
    
    server_name example.com;
    
    # SSL 인증서
    ssl_certificate /etc/ssl/certs/example.com.crt;
    ssl_certificate_key /etc/ssl/private/example.com.key;
    
    # TLS 1.3 (HTTP/3 필수)
    ssl_protocols TLSv1.3;
    
    # QUIC 설정
    quic_retry on;
    
    # Alt-Svc 헤더 (HTTP/3 지원 알림)
    add_header Alt-Svc 'h3=":443"; ma=86400';
    
    location / {
        root /var/www/html;
        index index.html;
    }
}
# 방화벽 설정 (UDP 포트 443 열기)
sudo ufw allow 443/udp

HTTP/3 클라이언트 (curl)

# curl 8.0+ (HTTP/3 지원)
curl --http3 https://example.com/

# HTTP/3 강제
curl --http3-only https://example.com/

# 프로토콜 확인
curl -I --http3 https://cloudflare.com/
# HTTP/3 200

# 상세 정보
curl -v --http3 https://example.com/
# * using HTTP/3
# * Connected to example.com (203.0.113.1) port 443
# * using HTTP/3 stream 0

HTTP/3 클라이언트 (Python)

import httpx

async def http3_example():
    """HTTP/3 클라이언트"""
    
    # HTTP/3 지원 클라이언트
    async with httpx.AsyncClient(http2=True) as client:
        response = await client.get('https://cloudflare.com/')
        
        print(f"Protocol: {response.http_version}")
        print(f"Status: {response.status_code}")
        print(f"Headers: {response.headers}")
        
        # Alt-Svc 헤더 확인
        alt_svc = response.headers.get('alt-svc')
        if alt_svc and 'h3' in alt_svc:
            print("✅ Server supports HTTP/3")

import asyncio
asyncio.run(http3_example())

5. 프로토콜 비교

종합 비교표

특성HTTP/1.1HTTP/2HTTP/3
전송 계층TCPTCPUDP (QUIC)
암호화선택적사실상 필수필수
멀티플렉싱
헤더 압축✅ HPACK✅ QPACK
서버 푸시
HOL Blocking✅ 있음⚠️ TCP 레벨❌ 없음
연결 설정3-RTT2-RTT1-RTT (0-RTT)
연결 마이그레이션
패킷 손실 영향중간작음
브라우저 지원100%97%75%

성능 비교 (실제 측정)

환경: 100ms RTT, 1% 패킷 손실

페이지 로딩 시간:
  HTTP/1.1: 3.2초
  HTTP/2:   1.8초 (44% 개선)
  HTTP/3:   1.2초 (63% 개선)

모바일 (200ms RTT, 2% 패킷 손실):
  HTTP/1.1: 6.5초
  HTTP/2:   4.2초 (35% 개선)
  HTTP/3:   2.8초 (57% 개선)

연결 설정 시간:
  HTTP/1.1 + TLS 1.2: 300ms (3-RTT)
  HTTP/2 + TLS 1.3:   200ms (2-RTT)
  HTTP/3 (첫 연결):   100ms (1-RTT)
  HTTP/3 (재연결):    0ms (0-RTT)

프로토콜 진화 타임라인

timeline
    title HTTP 프로토콜 진화
    1991 : HTTP/0.9
         : 단순 GET 요청
    1996 : HTTP/1.0
         : 헤더, POST 추가
    1997 : HTTP/1.1
         : Keep-Alive
         : 파이프라이닝
    2015 : HTTP/2
         : 멀티플렉싱
         : 헤더 압축
         : 서버 푸시
    2022 : HTTP/3
         : QUIC (UDP)
         : 0-RTT
         : 연결 마이그레이션

6. 실전 구현

HTTP/2 서버 (Node.js)

const http2 = require('http2');
const fs = require('fs');
const path = require('path');

const server = http2.createSecureServer({
  key: fs.readFileSync('/etc/ssl/private/server.key'),
  cert: fs.readFileSync('/etc/ssl/certs/server.crt')
});

server.on('stream', (stream, headers) => {
  const reqPath = headers[':path'];
  console.log(`📥 Request: ${reqPath}`);
  
  if (reqPath === '/') {
    // HTML 응답
    stream.respond({
      'content-type': 'text/html',
      ':status': 200
    });
    
    // 서버 푸시 (CSS, JS)
    stream.pushStream({ ':path': '/style.css' }, (err, pushStream) => {
      if (!err) {
        pushStream.respond({
          'content-type': 'text/css',
          ':status': 200
        });
        pushStream.end(fs.readFileSync('./public/style.css'));
      }
    });
    
    stream.pushStream({ ':path': '/script.js' }, (err, pushStream) => {
      if (!err) {
        pushStream.respond({
          'content-type': 'application/javascript',
          ':status': 200
        });
        pushStream.end(fs.readFileSync('./public/script.js'));
      }
    });
    
    stream.end('<html><head><link rel="stylesheet" href="/style.css"><script src="/script.js"></script></head><body><h1>Hello HTTP/2!</h1></body></html>');
  } else {
    stream.respond({ ':status': 404 });
    stream.end('Not Found');
  }
});

server.listen(443, () => {
  console.log('✅ HTTP/2 Server running on port 443');
});

HTTP/3 서버 (Go)

package main

import (
    "fmt"
    "log"
    "net/http"
    
    "github.com/quic-go/quic-go/http3"
)

func main() {
    mux := http.NewServeMux()
    
    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello HTTP/3!\n")
        fmt.Fprintf(w, "Protocol: %s\n", r.Proto)
    })
    
    mux.HandleFunc("/api/data", func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "application/json")
        fmt.Fprintf(w, `{"message": "HTTP/3 response"}`)
    })
    
    // HTTP/3 서버
    server := http3.Server{
        Addr:    ":443",
        Handler: mux,
    }
    
    log.Println("✅ HTTP/3 Server running on :443")
    err := server.ListenAndServeTLS("/etc/ssl/certs/server.crt", "/etc/ssl/private/server.key")
    if err != nil {
        log.Fatal(err)
    }
}

프로토콜 협상 (Protocol Negotiation)

sequenceDiagram
    participant C as Client
    participant S as Server

    Note over C,S: 첫 방문 (HTTP/1.1 또는 HTTP/2)
    
    C->>S: GET / HTTP/1.1<br/>Host: example.com
    S-->>C: HTTP/1.1 200 OK<br/>Alt-Svc: h3=":443"; ma=86400
    
    Note over C: Alt-Svc 헤더로<br/>HTTP/3 지원 확인
    
    Note over C,S: 다음 요청 (HTTP/3)
    
    C->>S: QUIC Initial (UDP:443)
    S-->>C: QUIC Handshake
    C->>S: HTTP/3 Request
    S-->>C: HTTP/3 Response

7. 성능 최적화

HTTP/1.1 최적화

<!-- 1. 리소스 번들링 -->
<link rel="stylesheet" href="bundle.css">
<script src="bundle.js"></script>

<!-- 2. 도메인 샤딩 (연결 제한 우회) -->
<img src="https://cdn1.example.com/img1.jpg">
<img src="https://cdn2.example.com/img2.jpg">
<img src="https://cdn3.example.com/img3.jpg">

<!-- 3. 인라인 CSS/JS (작은 리소스) -->
<style>
  body { margin: 0; }
</style>

<!-- 4. 스프라이트 이미지 -->
<div class="icon icon-home"></div>
# Nginx 설정
server {
    # Keep-Alive
    keepalive_timeout 65;
    keepalive_requests 100;
    
    # Gzip 압축
    gzip on;
    gzip_types text/plain text/css application/json application/javascript;
    gzip_min_length 1000;
    
    # 캐싱
    location ~* \.(jpg|jpeg|png|gif|css|js)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
}

HTTP/2 최적화

<!-- ❌ HTTP/1.1 방식 (불필요) -->
<link rel="stylesheet" href="bundle.css">

<!-- ✅ HTTP/2 방식 (개별 파일) -->
<link rel="stylesheet" href="reset.css">
<link rel="stylesheet" href="layout.css">
<link rel="stylesheet" href="components.css">

<!-- 리소스 힌트 -->
<link rel="preconnect" href="https://cdn.example.com">
<link rel="dns-prefetch" href="https://api.example.com">
<link rel="preload" href="/critical.css" as="style">
<link rel="prefetch" href="/next-page.html">
# Nginx HTTP/2 최적화
server {
    listen 443 ssl http2;
    
    # HTTP/2 푸시
    location = /index.html {
        http2_push /style.css;
        http2_push /script.js;
        http2_push /logo.png;
    }
    
    # 우선순위 설정
    location ~* \.(css)$ {
        add_header Link "</style.css>; rel=preload; as=style";
    }
}

HTTP/3 최적화

# Nginx HTTP/3 설정
server {
    listen 443 ssl;
    listen 443 quic reuseport;
    
    http2 on;
    http3 on;
    
    # Alt-Svc 헤더
    add_header Alt-Svc 'h3=":443"; ma=86400';
    
    # QUIC 설정
    ssl_early_data on;
    quic_retry on;
    
    # 0-RTT 보안 (Replay 공격 방지)
    location /api/ {
        # POST 요청은 0-RTT 비활성화
        if ($request_method = POST) {
            add_header Early-Data "0";
        }
    }
}

실전 시나리오

시나리오 1: 성능 측정

// performance-test.js
const https = require('https');
const http2 = require('http2');

async function measureHTTP1(url) {
  const start = Date.now();
  
  return new Promise((resolve) => {
    https.get(url, (res) => {
      let data = '';
      res.on('data', (chunk) => data += chunk);
      res.on('end', () => {
        const elapsed = Date.now() - start;
        resolve({ protocol: 'HTTP/1.1', elapsed, size: data.length });
      });
    });
  });
}

async function measureHTTP2(url) {
  const start = Date.now();
  
  return new Promise((resolve) => {
    const client = http2.connect(url);
    const req = client.request({ ':path': '/' });
    
    let data = '';
    req.on('data', (chunk) => data += chunk);
    req.on('end', () => {
      const elapsed = Date.now() - start;
      client.close();
      resolve({ protocol: 'HTTP/2', elapsed, size: data.length });
    });
  });
}

async function benchmark() {
  const url = 'https://example.com';
  
  console.log('🚀 Starting benchmark...\n');
  
  // HTTP/1.1
  const http1 = await measureHTTP1(url);
  console.log(`HTTP/1.1: ${http1.elapsed}ms (${http1.size} bytes)`);
  
  // HTTP/2
  const http2 = await measureHTTP2(url);
  console.log(`HTTP/2: ${http2.elapsed}ms (${http2.size} bytes)`);
  
  const improvement = ((http1.elapsed - http2.elapsed) / http1.elapsed * 100).toFixed(1);
  console.log(`\n✅ HTTP/2 is ${improvement}% faster`);
}

benchmark();

시나리오 2: 프로토콜 감지

// detect-protocol.js
const http = require('http');
const https = require('https');

function detectProtocol(url) {
  return new Promise((resolve) => {
    const client = url.startsWith('https') ? https : http;
    
    const req = client.request(url, { method: 'HEAD' }, (res) => {
      const protocol = res.httpVersion === '2.0' ? 'HTTP/2' : `HTTP/${res.httpVersion}`;
      const altSvc = res.headers['alt-svc'];
      const supportsHTTP3 = altSvc && altSvc.includes('h3');
      
      resolve({
        protocol,
        supportsHTTP3,
        altSvc,
        statusCode: res.statusCode
      });
    });
    
    req.end();
  });
}

async function checkSites() {
  const sites = [
    'https://www.google.com',
    'https://www.cloudflare.com',
    'https://www.facebook.com',
    'https://www.youtube.com'
  ];
  
  for (const site of sites) {
    const info = await detectProtocol(site);
    console.log(`\n${site}`);
    console.log(`  Protocol: ${info.protocol}`);
    console.log(`  HTTP/3: ${info.supportsHTTP3 ? '✅ Supported' : '❌ Not supported'}`);
    if (info.altSvc) {
      console.log(`  Alt-Svc: ${info.altSvc}`);
    }
  }
}

checkSites();

시나리오 3: HTTP/2 서버 푸시 최적화

const http2 = require('http2');
const fs = require('fs');
const path = require('path');

const server = http2.createSecureServer({
  key: fs.readFileSync('/etc/ssl/private/server.key'),
  cert: fs.readFileSync('/etc/ssl/certs/server.crt')
});

// 리소스 의존성 맵
const pushMap = {
  '/': ['/style.css', '/script.js', '/logo.png'],
  '/about': ['/style.css', '/about.js'],
  '/products': ['/style.css', '/products.js', '/product-images.css']
};

server.on('stream', (stream, headers) => {
  const reqPath = headers[':path'];
  
  // 서버 푸시 (의존 리소스)
  if (pushMap[reqPath]) {
    pushMap[reqPath].forEach(resource => {
      const resourcePath = path.join('./public', resource);
      
      if (fs.existsSync(resourcePath)) {
        stream.pushStream({ ':path': resource }, (err, pushStream) => {
          if (err) return;
          
          const ext = path.extname(resource);
          const contentType = {
            '.css': 'text/css',
            '.js': 'application/javascript',
            '.png': 'image/png',
            '.jpg': 'image/jpeg'
          }[ext] || 'application/octet-stream';
          
          pushStream.respond({
            'content-type': contentType,
            ':status': 200
          });
          
          pushStream.end(fs.readFileSync(resourcePath));
          console.log(`📤 Pushed: ${resource}`);
        });
      }
    });
  }
  
  // 메인 응답
  const filePath = path.join('./public', reqPath === '/' ? '/index.html' : reqPath);
  
  if (fs.existsSync(filePath)) {
    const content = fs.readFileSync(filePath);
    stream.respond({
      'content-type': 'text/html',
      ':status': 200
    });
    stream.end(content);
  } else {
    stream.respond({ ':status': 404 });
    stream.end('Not Found');
  }
});

server.listen(443, () => {
  console.log('✅ HTTP/2 Server with Server Push running on port 443');
});

시나리오 4: 프로토콜 폴백

// multi-protocol-server.js
const http = require('http');
const https = require('https');
const http2 = require('http2');
const fs = require('fs');

const options = {
  key: fs.readFileSync('/etc/ssl/private/server.key'),
  cert: fs.readFileSync('/etc/ssl/certs/server.crt'),
  allowHTTP1: true  // HTTP/1.1 폴백 허용
};

// HTTP/2 서버 (HTTP/1.1 폴백 지원)
const server = http2.createSecureServer(options);

server.on('request', (req, res) => {
  console.log(`📥 ${req.httpVersion} ${req.method} ${req.url}`);
  
  res.writeHead(200, {
    'Content-Type': 'text/plain',
    'Alt-Svc': 'h3=":443"; ma=86400'  // HTTP/3 지원 알림
  });
  
  res.end(`Hello from ${req.httpVersion}!\n`);
});

server.listen(443, () => {
  console.log('✅ Multi-protocol server running on port 443');
  console.log('   Supports: HTTP/1.1, HTTP/2');
});

// HTTP → HTTPS 리다이렉트
http.createServer((req, res) => {
  res.writeHead(301, { Location: `https://${req.headers.host}${req.url}` });
  res.end();
}).listen(80);

고급 주제

1. QUIC 연결 ID

TCP 연결:
  (Client IP, Client Port, Server IP, Server Port)
  → IP 변경 시 연결 끊김

QUIC 연결:
  Connection ID: 8바이트 랜덤 값
  → IP 변경해도 Connection ID로 연결 유지
  
예시:
  Wi-Fi:  192.168.1.100:50000 → Server (Connection ID: abc123)
  4G:     203.0.113.50:60000  → Server (Connection ID: abc123)
  
  → 동일한 Connection ID로 연결 유지

2. QPACK (헤더 압축)

HPACK (HTTP/2):
  정적 테이블 + 동적 테이블
  → HOL Blocking 발생 (헤더 순서 의존)

QPACK (HTTP/3):
  정적 테이블 + 동적 테이블 + 스트림 독립성
  → HOL Blocking 없음 (헤더 순서 독립)
  
예시:
  Stream 1: [Header 1] → 동적 테이블 참조
  Stream 2: [Header 2] → Stream 1 대기 불필요
  Stream 3: [Header 3] → 독립적 처리

3. 0-RTT 보안 고려사항

# 0-RTT 활성화
ssl_early_data on;

# Replay 공격 방지
location /api/ {
    # POST/PUT/DELETE는 0-RTT 비활성화
    if ($request_method !~ ^(GET|HEAD)$) {
        add_header Early-Data "0";
    }
}

# 애플리케이션 레벨 검증
location /api/payment {
    # 결제 API는 0-RTT 완전 차단
    ssl_early_data off;
}

4. 혼잡 제어

TCP 혼잡 제어:
  - Slow Start
  - Congestion Avoidance
  - Fast Retransmit
  - Fast Recovery

QUIC 혼잡 제어:
  - Cubic (기본)
  - BBR (Bottleneck Bandwidth and RTT)
  - Reno
  - NewReno
  
BBR 장점:
  - 패킷 손실에 덜 민감
  - 대역폭 활용 극대화
  - 버퍼 블로트 감소

프로토콜 마이그레이션 가이드

HTTP/1.1 → HTTP/2

# 1단계: HTTPS 활성화 (HTTP/2 필수)
server {
    listen 443 ssl;
    ssl_certificate /etc/ssl/certs/example.com.crt;
    ssl_certificate_key /etc/ssl/private/example.com.key;
}

# 2단계: HTTP/2 활성화
server {
    listen 443 ssl http2;
    # 나머지 동일
}

# 3단계: 최적화
server {
    listen 443 ssl http2;
    
    # 서버 푸시
    http2_push_preload on;
    
    # 스트림 제한
    http2_max_concurrent_streams 128;
    
    # 번들링 해제 (HTTP/2에서는 불필요)
    location / {
        # 개별 파일로 분리
    }
}

HTTP/2 → HTTP/3

# 1단계: HTTP/2 유지 + HTTP/3 추가
server {
    listen 443 ssl;
    listen 443 quic reuseport;
    
    http2 on;
    http3 on;
    
    # Alt-Svc 헤더 (HTTP/3 지원 알림)
    add_header Alt-Svc 'h3=":443"; ma=86400';
}

# 2단계: 방화벽 설정
# UDP 포트 443 열기
sudo ufw allow 443/udp

# 3단계: 모니터링
# HTTP/3 사용률 확인
tail -f /var/log/nginx/access.log | grep "HTTP/3"

문제 해결

문제 1: HTTP/2 연결 실패

# 증상
curl: (16) Error in the HTTP2 framing layer

# 원인 1: 서버가 HTTP/2를 지원하지 않음
# 확인
curl -I --http2 https://example.com/
# HTTP/1.1 200 OK (HTTP/2 미지원)

# 원인 2: ALPN (Application-Layer Protocol Negotiation) 문제
# 확인
openssl s_client -connect example.com:443 -alpn h2
# ALPN protocol: h2 (정상)
# ALPN protocol: http/1.1 (HTTP/2 미지원)

# 해결: Nginx 설정 확인
# listen 443 ssl http2; 확인

문제 2: HTTP/3 연결 안 됨

# 증상
curl --http3 https://example.com/
curl: (28) Failed to connect to example.com port 443: Connection timed out

# 원인 1: UDP 포트 443 차단
# 확인
sudo netstat -ulnp | grep :443
# (없으면 서버가 QUIC을 리스닝하지 않음)

# 방화벽 확인
sudo ufw status | grep 443
# 443/udp ALLOW Anywhere

# 원인 2: Alt-Svc 헤더 없음
# 확인
curl -I https://example.com/ | grep -i alt-svc
# Alt-Svc: h3=":443"; ma=86400 (정상)

# 해결: Nginx 설정
listen 443 quic reuseport;
http3 on;
add_header Alt-Svc 'h3=":443"; ma=86400';

문제 3: HTTP/2 서버 푸시 작동 안 함

# 증상
# 서버 푸시가 전송되지 않음

# 원인: 브라우저 캐시에 이미 존재
# 브라우저는 캐시된 리소스는 거부함

# 해결: Link 헤더 사용 (조건부 푸시)
add_header Link "</style.css>; rel=preload; as=style";
http2_push_preload on;

# 브라우저가 필요한 경우만 푸시 요청

보안 고려사항

HTTPS 보안 헤더

server {
    listen 443 ssl http2;
    
    # HSTS (HTTP Strict Transport Security)
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
    
    # CSP (Content Security Policy)
    add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';" always;
    
    # X-Frame-Options (클릭재킹 방지)
    add_header X-Frame-Options "SAMEORIGIN" always;
    
    # X-Content-Type-Options
    add_header X-Content-Type-Options "nosniff" always;
    
    # Referrer-Policy
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    
    # Permissions-Policy
    add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;
}

TLS 설정 최적화

server {
    # TLS 1.3만 허용 (최신)
    ssl_protocols TLSv1.3;
    
    # 또는 TLS 1.2+ (호환성)
    ssl_protocols TLSv1.2 TLSv1.3;
    
    # 강력한 암호화 스위트
    ssl_ciphers 'TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256';
    ssl_prefer_server_ciphers off;
    
    # OCSP Stapling (인증서 검증 속도 향상)
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_trusted_certificate /etc/ssl/certs/ca-bundle.crt;
    
    # 세션 재사용
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    ssl_session_tickets off;
    
    # DH 파라미터
    ssl_dhparam /etc/ssl/certs/dhparam.pem;
}

모니터링 및 디버깅

Chrome DevTools

// Performance 탭에서 프로토콜 확인
// 1. F12 → Network 탭
// 2. Protocol 컬럼 추가 (우클릭 → Protocol)
// 3. 페이지 새로고침

// 결과:
// index.html    h2    200    1.2KB    50ms
// style.css     h2    200    5.4KB    52ms (pushed)
// script.js     h2    200    8.1KB    53ms (pushed)
// image.png     h2    200    45KB     120ms

// Timing 탭:
// - Queueing: 대기 시간
// - Stalled: 연결 대기
// - DNS Lookup: DNS 조회
// - Initial connection: TCP 핸드셰이크
// - SSL: TLS 핸드셰이크
// - Request sent: 요청 전송
// - Waiting (TTFB): 첫 바이트까지 대기
// - Content Download: 다운로드

curl 디버깅

# HTTP/1.1
curl -v https://example.com/
# > GET / HTTP/1.1
# < HTTP/1.1 200 OK

# HTTP/2
curl -v --http2 https://example.com/
# > GET / HTTP/2
# < HTTP/2 200

# HTTP/3
curl -v --http3 https://example.com/
# > GET / HTTP/3
# < HTTP/3 200

# 타이밍 정보
curl -w "time_namelookup: %{time_namelookup}\ntime_connect: %{time_connect}\ntime_appconnect: %{time_appconnect}\ntime_starttransfer: %{time_starttransfer}\ntime_total: %{time_total}\n" -o /dev/null -s https://example.com/

# 헤더만 확인
curl -I --http2 https://example.com/

# 서버 푸시 확인
curl -v --http2 https://example.com/ 2>&1 | grep -i push

Wireshark 패킷 분석

# HTTP/2 트래픽 캡처
sudo tcpdump -i eth0 -w http2.pcap port 443

# Wireshark 필터
# HTTP/2
http2

# QUIC
quic

# TLS 핸드셰이크
ssl.handshake.type == 1

# HTTP/2 프레임 타입
http2.type == 0  # DATA
http2.type == 1  # HEADERS
http2.type == 5  # PUSH_PROMISE

실전 벤치마크

Apache Bench

# HTTP/1.1
ab -n 1000 -c 10 https://example.com/

# HTTP/2 (h2load)
h2load -n 1000 -c 10 -m 10 https://example.com/

# 결과 비교
# HTTP/1.1:
#   Requests per second: 150
#   Time per request: 66.7ms
#
# HTTP/2:
#   Requests per second: 450
#   Time per request: 22.2ms
#   → 3배 향상

WebPageTest

# WebPageTest API
curl "https://www.webpagetest.org/runtest.php?url=https://example.com&k=API_KEY&f=json"

# 결과:
# HTTP/1.1:
#   Load Time: 3.2s
#   First Byte: 0.8s
#   Start Render: 1.5s
#
# HTTP/2:
#   Load Time: 1.8s
#   First Byte: 0.5s
#   Start Render: 0.9s
#
# HTTP/3:
#   Load Time: 1.2s
#   First Byte: 0.3s
#   Start Render: 0.6s

프로토콜 선택 가이드

의사 결정 플로우차트

flowchart TD
    Start[HTTP 프로토콜 선택] --> Q1{레거시 지원 필요?}
    
    Q1 -->|Yes| HTTP1[HTTP/1.1<br/>최대 호환성]
    Q1 -->|No| Q2{서버 인프라는?}
    
    Q2 -->|최신| Q3{모바일 트래픽 많음?}
    Q2 -->|구형| HTTP2[HTTP/2<br/>성능 개선]
    
    Q3 -->|Yes| HTTP3[✅ HTTP/3<br/>최고 성능<br/>연결 마이그레이션]
    Q3 -->|No| HTTP2
    
    Start --> Q4{보안 필요?}
    Q4 -->|Yes| HTTPS[✅ HTTPS 필수<br/>TLS 1.3]
    Q4 -->|No| WARN[⚠️ HTTP는<br/>권장하지 않음]

사용 사례별 추천

✅ 공개 웹사이트:
   → HTTP/3 (Cloudflare, Nginx 1.25+)
   → 폴백: HTTP/2

✅ 내부 API:
   → HTTP/2 (충분한 성능)

✅ 모바일 앱:
   → HTTP/3 (연결 마이그레이션)

✅ 실시간 스트리밍:
   → HTTP/3 (낮은 지연)

✅ 레거시 시스템:
   → HTTP/1.1 (호환성)

✅ IoT 디바이스:
   → HTTP/1.1 (낮은 리소스)

성능 최적화 체크리스트

HTTP/1.1

- [ ] Keep-Alive 활성화
- [ ] Gzip/Brotli 압축
- [ ] 리소스 번들링 (CSS, JS)
- [ ] 스프라이트 이미지
- [ ] 도메인 샤딩 (CDN)
- [ ] 캐싱 헤더 설정
- [ ] 조건부 요청 (ETag, Last-Modified)

HTTP/2

- [ ] HTTPS 활성화 (필수)
- [ ] 번들링 해제 (개별 파일)
- [ ] 서버 푸시 활용
- [ ] 리소스 힌트 (preload, prefetch)
- [ ] 스트림 우선순위 설정
- [ ] 헤더 압축 (HPACK)
- [ ] 도메인 샤딩 제거

HTTP/3

- [ ] UDP 포트 443 열기
- [ ] Alt-Svc 헤더 설정
- [ ] 0-RTT 활성화 (보안 고려)
- [ ] QUIC 재시도 활성화
- [ ] 연결 마이그레이션 테스트
- [ ] 패킷 손실 시나리오 테스트
- [ ] 모바일 환경 테스트

실전 예제

예제 1: HTTP/2 클라이언트 (Python)

import httpx
import asyncio
import time

async def compare_protocols():
    """HTTP/1.1 vs HTTP/2 성능 비교"""
    
    urls = [
        'https://example.com/page1',
        'https://example.com/page2',
        'https://example.com/page3',
        'https://example.com/page4',
        'https://example.com/page5',
    ]
    
    # HTTP/1.1
    start = time.time()
    async with httpx.AsyncClient(http2=False) as client:
        tasks = [client.get(url) for url in urls]
        responses = await asyncio.gather(*tasks)
    http1_time = time.time() - start
    
    # HTTP/2
    start = time.time()
    async with httpx.AsyncClient(http2=True) as client:
        tasks = [client.get(url) for url in urls]
        responses = await asyncio.gather(*tasks)
    http2_time = time.time() - start
    
    print(f"HTTP/1.1: {http1_time:.2f}s")
    print(f"HTTP/2:   {http2_time:.2f}s")
    print(f"Improvement: {(1 - http2_time/http1_time) * 100:.1f}%")

asyncio.run(compare_protocols())

예제 2: WebSocket over HTTP/2

// WebSocket은 HTTP/1.1만 지원
// HTTP/2에서는 Server-Sent Events 또는 gRPC 사용

// Server-Sent Events (HTTP/2)
const express = require('express');
const app = express();

app.get('/events', (req, res) => {
  res.setHeader('Content-Type', 'text/event-stream');
  res.setHeader('Cache-Control', 'no-cache');
  res.setHeader('Connection', 'keep-alive');
  
  const sendEvent = (data) => {
    res.write(`data: ${JSON.stringify(data)}\n\n`);
  };
  
  // 주기적 이벤트 전송
  const interval = setInterval(() => {
    sendEvent({ time: new Date().toISOString(), value: Math.random() });
  }, 1000);
  
  req.on('close', () => {
    clearInterval(interval);
  });
});

app.listen(443);

예제 3: HTTP/3 로드 밸런서

package main

import (
    "log"
    "net/http"
    "net/http/httputil"
    "net/url"
    
    "github.com/quic-go/quic-go/http3"
)

func main() {
    // 백엔드 서버 목록
    backends := []*url.URL{
        {Scheme: "https", Host: "backend1.example.com"},
        {Scheme: "https", Host: "backend2.example.com"},
        {Scheme: "https", Host: "backend3.example.com"},
    }
    
    var currentBackend int
    
    // 라운드 로빈 로드 밸런서
    handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        backend := backends[currentBackend]
        currentBackend = (currentBackend + 1) % len(backends)
        
        proxy := httputil.NewSingleHostReverseProxy(backend)
        proxy.ServeHTTP(w, r)
        
        log.Printf("📤 Proxied to %s", backend.Host)
    })
    
    // HTTP/3 서버
    server := http3.Server{
        Addr:    ":443",
        Handler: handler,
    }
    
    log.Println("✅ HTTP/3 Load Balancer running on :443")
    err := server.ListenAndServeTLS("/etc/ssl/certs/server.crt", "/etc/ssl/private/server.key")
    if err != nil {
        log.Fatal(err)
    }
}

프로토콜 협상 (ALPN)

ALPN (Application-Layer Protocol Negotiation)

sequenceDiagram
    participant C as Client
    participant S as Server

    C->>S: ClientHello<br/>ALPN: [h2, http/1.1]
    
    Note over S: 지원 프로토콜 확인<br/>우선순위: h2 > http/1.1
    
    S-->>C: ServerHello<br/>ALPN: h2
    
    Note over C,S: HTTP/2 사용

ALPN 확인

# OpenSSL로 ALPN 확인
openssl s_client -connect example.com:443 -alpn h2,http/1.1
# ALPN protocol: h2

# curl로 확인
curl -v --http2 https://example.com/ 2>&1 | grep ALPN
# * ALPN, server accepted to use h2

실전 배포

Cloudflare (HTTP/3 자동 지원)

// Cloudflare Workers (HTTP/3 자동)
export default {
  async fetch(request) {
    const url = new URL(request.url);
    
    // 프로토콜 확인
    const protocol = request.cf?.httpProtocol || 'HTTP/1.1';
    
    return new Response(JSON.stringify({
      protocol,
      url: url.pathname,
      headers: Object.fromEntries(request.headers)
    }), {
      headers: {
        'Content-Type': 'application/json',
        'Alt-Svc': 'h3=":443"; ma=86400'
      }
    });
  }
};

Docker Compose (HTTP/3 스택)

version: '3.8'

services:
  nginx:
    image: nginx:1.25-alpine
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"  # HTTP/3 (QUIC)
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./ssl:/etc/ssl:ro
      - ./html:/usr/share/nginx/html:ro
    restart: unless-stopped
    
  app:
    image: node:20-alpine
    working_dir: /app
    volumes:
      - ./app:/app
    command: node server.js
    environment:
      - NODE_ENV=production
    expose:
      - "3000"

정리

프로토콜 진화 요약

flowchart LR
    HTTP1[HTTP/1.1<br/>1997] -->|문제: HOL Blocking<br/>연결 제한| HTTP2[HTTP/2<br/>2015]
    HTTP2 -->|문제: TCP HOL<br/>느린 핸드셰이크| HTTP3[HTTP/3<br/>2022]
    
    HTTP1 -.->|개선| K1[Keep-Alive<br/>파이프라이닝]
    HTTP2 -.->|개선| K2[멀티플렉싱<br/>헤더 압축<br/>서버 푸시]
    HTTP3 -.->|개선| K3[QUIC<br/>0-RTT<br/>연결 마이그레이션]
    
    style HTTP3 fill:#6c6

핵심 명령어

# 프로토콜 확인
curl -I --http2 https://example.com/
curl -I --http3 https://example.com/

# 성능 측정
h2load -n 1000 -c 10 https://example.com/

# TLS 정보
openssl s_client -connect example.com:443 -alpn h2

# Nginx 설정 테스트
sudo nginx -t

# Nginx 재시작
sudo systemctl reload nginx

# 방화벽 (HTTP/3)
sudo ufw allow 443/udp

마이그레이션 로드맵

1단계: HTTPS 활성화 (Let's Encrypt)

2단계: HTTP/2 활성화 (Nginx 설정)

3단계: 최적화 (서버 푸시, 헤더 압축)

4단계: HTTP/3 추가 (Nginx 1.25+)

5단계: 모니터링 및 튜닝

프로토콜별 적용 시기

2026년 현재:

✅ HTTP/1.1:
   - 레거시 시스템
   - IoT 디바이스
   - 제한된 환경

✅ HTTP/2:
   - 대부분의 웹사이트 (표준)
   - API 서버
   - 내부 서비스

✅ HTTP/3:
   - 고성능 웹사이트
   - 모바일 우선 서비스
   - 실시간 애플리케이션
   - CDN (Cloudflare, Fastly)

채택률:
  HTTP/1.1: 40%
  HTTP/2:   55%
  HTTP/3:   5% (빠르게 증가 중)

참고 자료

한 줄 요약: HTTP/2는 멀티플렉싱으로 성능을 개선했고, HTTP/3는 QUIC으로 HOL Blocking을 해결하고 연결 설정을 더 빠르게 만들었습니다. 현대 웹사이트는 HTTP/2를 기본으로, 모바일 트래픽이 많으면 HTTP/3를 추가하세요.

---
... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3