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
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.1 | HTTP/2 | HTTP/3 |
|---|---|---|---|
| 전송 계층 | TCP | TCP | UDP (QUIC) |
| 암호화 | 선택적 | 사실상 필수 | 필수 |
| 멀티플렉싱 | ❌ | ✅ | ✅ |
| 헤더 압축 | ❌ | ✅ HPACK | ✅ QPACK |
| 서버 푸시 | ❌ | ✅ | ✅ |
| HOL Blocking | ✅ 있음 | ⚠️ TCP 레벨 | ❌ 없음 |
| 연결 설정 | 3-RTT | 2-RTT | 1-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% (빠르게 증가 중)
참고 자료
- RFC 9114 - HTTP/3
- RFC 9000 - QUIC
- RFC 7540 - HTTP/2
- RFC 2616 - HTTP/1.1
- HTTP/3 Explained
- QUIC Working Group
- Can I Use HTTP/3
한 줄 요약: HTTP/2는 멀티플렉싱으로 성능을 개선했고, HTTP/3는 QUIC으로 HOL Blocking을 해결하고 연결 설정을 더 빠르게 만들었습니다. 현대 웹사이트는 HTTP/2를 기본으로, 모바일 트래픽이 많으면 HTTP/3를 추가하세요.