C.W.K.
Stream
Lesson 04 of 05 · published

Compression & Keep-Alive — 공짜 byte 와 공짜 round trip

~10 min · caching-perf, compression, gzip, brotli, keep-alive

Level 0HTTP Newbie
0 XP0/46 lessons0/12 achievements
0/120 XP to next level120 XP to go0% complete
"HTTP 의 가장 오래된 성능 승리 둘이 여전히 가장 큰 거: body 압축, 연결 재사용. 둘 다 wire 위 negotiate; 둘 다 활성에 거의 비용 영."

Content 압축 — negotiate, 투명

HTTP body 압축이 header 둘로 negotiate:

  • Accept-Encoding (request) — client 가 decode 할 수 있는 압축: gzip, br, zstd, identity.
  • Content-Encoding (response) — server 가 실제 쓴 거.

브라우저가 항상 Accept-Encoding 보냄. Server 가 best 지원 encoding 선택하고 보내기 전 body 압축. Client 가 투명 decompress — application 코드가 decoded 텍스트 봄.

현역 algorithm 셋:

  • gzip — 보편 지원 (모든 HTTP client 와 server). Text content 에 50-70% 감소. 빠른 encode/decode. 안전 기본.
  • Brotli (br) — 더 좋은 압축 (text 에 gzip 보다 15-25% 더), 비교 가능한 decode 속도, 더 느린 고품질 encode. 2026 년 거의 보편 지원 (모든 현대 브라우저 + 대부분 CDN).
  • Zstandard (zstd) — 더 새로움, 중간 품질에 매우 빠름, HTTP 지원 성장 중 보편 아직 아님.

Static 사전 압축, 동적 per-request 압축

Encoding 과 decoding 사이 비용 비대칭 중요:

  • Static asset — 최고 품질 Brotli 로 build time 사전 압축. Accept-Encoding 허용 시 .br 파일 직접 ship. 1회 CPU 비용을 모든 request 에 분할상환.
  • 동적 response — 중간 품질 gzip 이나 Brotli (level 4-6) 로 per-request 압축. CPU/bandwidth tradeoff 가 보통 ~1KB 넘는 text/JSON 에 압축 선호.

이미 압축된 content (이미지, 비디오, .tar.gz) 압축 안 함 — 이득 영, CPU 낭비, 재압축이 가끔 크기 증가.

Keep-Alive — 안 버리는 연결

HTTP/1.0 기본은 "TCP 연결 열고, request 하나 보내고, response 하나 받고, 닫기." TCP handshake (1 RTT) + TLS handshake (1-2 RTTs) 가 모든 request 먹음. 60 asset 가진 페이지면 180+ RTT 의 순수 연결 setup.

HTTP/1.1 의 Connection: keep-alive (기본) 가 response 후 TCP 연결 열어둠, 후속 request 가 재사용. 한 handshake 를 N request 에 분할상환. 숫자: 100ms RTT 의 remote server 가 request 당 ~300ms (HTTP/1.0) 에서 다른 거 안 바꾸고 request 당 ~100ms (HTTP/1.1 keep-alive) 됨.

HTTP/2 가 multiplexing 으로 더 (다음 lesson) — 한 연결이 여러 parallel request 운반. HTTP/3 가 QUIC 와 함께 연결 migration 추가 (폰이 세션 안 잃고 Wi-Fi 에서 LTE 로 전환 가능).

압축과 연결 재사용이 HTTP 성능 80%. 더 화려한 최적화 (server push, HTTP/3, edge function) 잡기 전 둘 다 켜졌는지 확인. 공짜고 큼.

Connection-header gotcha

1. 모든 response 에 Connection: close. 일부 legacy server (혹은 proxy) 가 모든 request 후 Connection: close 발신, 새 TCP setup 강제. 찾아; fix.

2. 장기 연결이 server resource 누설. Server 쪽 keep-alive 가 TTL (보통 60-300s) 있음. TTL 후 server 가 idle 연결 닫음. Client 패턴 기반 튜닝.

3. SSL session resumption. Keep-alive 있어도 새 연결이 TLS handshake 발생. 현대 TLS (1.3) 가 session resumption 지원 — 돌아오는 client 가 key exchange 건너뜀. Server 가 활성됐는지 확인.

cwkPippa 의 압축 현실

cwkPippa 의 FastAPI/Uvicorn 이 Starlette 의 GZipMiddleware 통해 gzip 압축 지원 — 1KB 최소 threshold 로 활성, 작은 response 가 압축 안 됨 (CPU/byte tradeoff 가 ~1KB 아래에 안 payoff). Vite 가 서빙하는 static asset 이 build 출력에서 사전 압축된 Brotli 파일 받음; dev server 가 적절한 Content-Encoding 으로 서빙. Uvicorn 의 keep-alive 가 기본 켜져 있음. cwk-site 의 Vercel 이 자동으로 같은 거 — text 에 Brotli, gzip fallback, 어디나 keep-alive. 승리 실제인데 application 코드엔 invisible; framework 가 처리하는 transport-층 관심사.

Code

curl --compressed 가 압축 승리 측정 가능·bash
# curl 로 압축 negotiate
curl -v --compressed https://creativeworksofknowledge.com/ \
  2>&1 | grep -E '(Accept-Encoding|Content-Encoding)'
# > Accept-Encoding: deflate, gzip, br, zstd
# < content-encoding: br
# (curl 가 자동 decompress; 보는 body 가 plaintext)

# --compressed 없으면 curl 가 압축 안 요청
curl -v https://creativeworksofknowledge.com/ 2>&1 | grep -i encoding
# > (Accept-Encoding 안 보냄 → server 가 plain text 돌려줌)

# 승리 측정: 압축 vs 없이 byte 크기
echo 'brotli 와:'  ; curl -s --compressed https://creativeworksofknowledge.com/ | wc -c
echo '없이:'     ; curl -s              https://creativeworksofknowledge.com/ | wc -c
FastAPI GZipMiddleware — min-size guard 가진 자동 압축·python
# FastAPI — response 에 gzip 압축 활성
from fastapi import FastAPI
from starlette.middleware.gzip import GZipMiddleware

app = FastAPI()

# minimum_size 가 작은 response 압축 CPU 비용 피함
app.add_middleware(GZipMiddleware, minimum_size=1000, compresslevel=6)

@app.get('/api/posts')
async def list_posts():
    return {'items': [...]}  # response body > 1KB → 자동 gzipped

# Brotli 위해선 brotli-asgi 쓰거나 reverse proxy 통해 사전 압축 파일 서빙
Client 연결 재사용 — Session/Client 패턴이 handshake 절약·python
# Client 쪽 — httpx.Client 로 연결 재사용 (권장 패턴)
import httpx
import time

# 잘못: 호출 당 새 연결
start = time.perf_counter()
for i in range(5):
    httpx.get('https://api.example.com/users/' + str(i))  # 매번 새 TCP+TLS
print(f'재사용 없음: {(time.perf_counter() - start) * 1000:.0f}ms')

# 맞음: Client 통해 한 연결 재사용
start = time.perf_counter()
with httpx.Client() as c:
    for i in range(5):
        c.get('https://api.example.com/users/' + str(i))  # 총 TCP+TLS handshake 하나
print(f'재사용:    {(time.perf_counter() - start) * 1000:.0f}ms')

# HTTP/2 multiplexing 이득 위해 http2=True 추가
with httpx.Client(http2=True) as c:
    c.get('https://api.example.com/users/42')

External links

Exercise

5KB 넘는 데이터 서빙하는 어느 JSON endpoint (네 거나 어느 공개 API) 골라. 세 방식으로 치기: (1) encoding negotiation 없이 curl (plain), (2) --compressed (gzip/br negotiation) 가진 curl, (3) body+header 크기 측정에 curl -w '%{size_download}/%{size_header}\n'. 압축 비율 계산. 그 다음 100 request 간 연결 재사용에 httpx.Client() 쓴 Python client 써; 매번 새 client 만드는 100-호출 loop 와 wall-clock 시간 비교.
Hint
전형적 JSON list endpoint 에 압축으로 60-80% body 크기 감소 기대. Remote server (>50ms RTT) 의 연결 재사용이 첫 후 request 당 보통 80-100ms 절약 — 100 request 면 차이 극적 (제거된 총 handshake 시간 10+ 초). 둘 다 승리가 application 코드에 완전 투명; 그냥 활성하고 일어남.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.