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

HTTP/2 Multiplexing — Client 설계가 뭐 바뀌나

~10 min · caching-perf, http2, multiplexing, client-design

Level 0HTTP Newbie
0 XP0/46 lessons0/12 achievements
0/120 XP to next level120 XP to go0% complete
"10년의 'HTTP request 줄여' 조언이 HTTP/1.1 의 per-connection 비용 우회. HTTP/2 multiplexing 이 수학 바꿈. 일부 옛 best practice 가 이제 적극 anti-pattern."

모든 거 모양 잡은 HTTP/1.1 제약

브라우저가 역사적으로 origin 당 ~6 parallel TCP 연결 열음. 그 너머는 request queue. 이미지 60개 가진 페이지 그릴 때 브라우저 선택 셋: origin 더 열기 (sharding), 이미지 inline, 모든 거 메가-bundle 로 연결.

뒤따른 front-end 최적화 시대 — CSS sprite sheet, 이미지 inline, JS bundling, 도메인 sharding — 이 거의 전적으로 이 제약에 대한 응답. Request 수 줄이기 → 연결 비용 줄이기 → 더 빠른 페이지.

HTTP/2 가 바꾼 것

Origin 당 TCP 연결 하나, 위에 multiplex 된 여러 동시 stream. Per-request 비용 증발. 수백 작은 asset 이 wire 위 parallel 로 날아감. Header 압축 (HPACK) 이 같은 연결 간 반복 request header 중복 제거.

Client 설계 결과:

  • 세밀 asset 이김. 모든 거 한 큰 bundle 로 연결이 이제 HURTS — byte 변화 하나가 모든 user 의 전체 bundle 무효화. 더 작은 파일이 더 효율 캐시고 incremental update 허용.
  • Sharding 아픔. 여러 subdomain 에 asset 나누기가 subdomain 당 별개 TCP+TLS handshake 필요 — HTTP/1.1 의 승리, HTTP/2 의 적극 낭비.
  • Inline 아픔. CSS 나 이미지를 HTML 에 inline 이 request 가 연결 비용일 때 의미. 이제 inline 된 asset 이 따로 캐시 못 됨, 그래서 HTML 변경 시 재전송.
  • 이미지 sprite 구식. 같은 로직 — 독립적으로 캐시되고 update 되는 개별 이미지 서빙이 더 나음.

HTTP/2 에서 여전히 중요한 것

  • 압축 — 여전히 큰 승리; multiplexing 과 직교.
  • Caching — 여전히 가장 중요한 최적화; 직교.
  • Asset 크기 — 여전히 중요; HTTP/2 가 50 request 빠르게 만들지만 각각 여전히 전송할 body 있음.
  • Critical-path rendering — 첫 paint 에 필요한 거 최소화.
  • 연결 priority — HTTP/2 가 per-stream priority 지원. 브라우저가 이거 써서 offscreen 이미지보다 critical CSS 우선.
최적화가 그 시대 제약의 우회. 제약 변하면 우회가 문제 됨. HTTP/2 가 그냥 빠르게만 안 한 거 — 비용 모델 뒤집음. Tutorial 의 옛 조언이 이제 적극 잘못; 2020 전 setup 된 build pipeline 재방문.

HTTP/2 실용 체크리스트

  • HTTP/2 위에 서빙. Server 나 CDN 이 자동 negotiate; curl --http2 -v 로 검증.
  • 모든 거 bundling build 버리기. 현대 Vite/esbuild/Rollup 가 세밀 chunking 지원; 그렇게 둬.
  • Sharding 멈춰. Service 당 origin 하나. Subdomain sharding 이 적극 아픔.
  • ETag / Cache-Control 적극 써. Per-asset, 세밀 cache 무효화가 이제 bundle-busting 이김.
  • Critical CSS 절제 inline. 첫 paint 에 필요한 <8KB 만 inline; 나머지는 cacheable 파일 통해 로드.

Head-of-line blocking 주의

HTTP/2 의 multiplexing 이 HTTP 층에서 일어나는데 TCP 가 여전히 아래. 떨어진 패킷 하나가 전체 TCP 연결 stall — 그래서 위에 multiplex 된 모든 stream — 재전송까지. 손실 많은 네트워크 (모바일, 혼잡 Wi-Fi) 에 아픔. HTTP/3 (QUIC 위) 가 loss 를 per-stream 처리로 fix, 다른 stream 계속 흘러. 모바일-heavy 트래픽엔 HTTP/3 가 다음 migration step.

cwkPippa 의 HTTP/2 현실

cwkPippa backend 가 Uvicorn 에서 HTTP/1.1 서빙 — 로컬 + Tailscale 엔 괜찮 (빠른 LAN 에 항상 한 번에 client 하나) multiplexing 승리 작음. Vite 의 static asset 이 이미 많은 작은 파일로 chunked, HTTP/1.1 의 6-parallel limit 에서도 빨리 download. cwk-site 의 Vercel 이 edge 에서 자동 HTTP/2 와 HTTP/3 받음; Next.js build 출력이 HTTP/2 multiplexing 가정 (작은 chunk 많이). cwkPippa 를 공개 ship 하면 reverse proxy (nginx, Caddy, 혹은 Uvicorn 앞 Cloudflare) 에서 HTTP/2 가 application 변경 없는 1-일 migration.

Code

HTTP/2 검증 + parallel-request 승리 측정·bash
# Server 가 HTTP/2 말하는지 검증
curl -v --http2 -o /dev/null https://creativeworksofknowledge.com/ 2>&1 | grep -E '(HTTP|h2)' | head
# 봐:
# * Connected to ... via HTTP/2
# < HTTP/2 200

# 비교 위해 HTTP/1.1 강제
curl -v --http1.1 -o /dev/null https://creativeworksofknowledge.com/ 2>&1 | grep HTTP | head
# < HTTP/1.1 200 OK

# 차이 측정: parallel asset fetch
ASSETS=$(for i in {1..6}; do echo https://creativeworksofknowledge.com/asset$i.png; done)
time curl -s --http1.1 -o /dev/null $ASSETS    # 별개 TCP 연결 6 (순차 혹은 cap parallel)
time curl -s --http2     -o /dev/null $ASSETS  # 한 연결 위 multiplexed
60 parallel request: HTTP/1.1 순차 vs HTTP/2 multiplexed·python
# Python httpx — HTTP/2 opt-in + 연결 재사용
import httpx
import time

# 60 parallel asset request — 완벽한 HTTP/2 multiplexing 데모
URLS = [f'https://creativeworksofknowledge.com/asset_{i}.png' for i in range(60)]

# 순차 HTTP/1.1 — per-connection RTT 에 제한
start = time.perf_counter()
with httpx.Client() as c:  # 기본 HTTP/1.1
    for url in URLS:
        c.get(url)
print(f'순차 HTTP/1.1: {(time.perf_counter() - start):.1f}s')

# 동시 HTTP/2 — 한 연결 위 multiplexed
import asyncio

async def fetch_all():
    async with httpx.AsyncClient(http2=True) as c:
        return await asyncio.gather(*[c.get(url) for url in URLS])

start = time.perf_counter()
asyncio.run(fetch_all())
print(f'동시 HTTP/2:   {(time.perf_counter() - start):.1f}s')
Vite config: 세밀 chunk (HTTP/2-friendly 기본)·javascript
// 현대 Vite config — 작은 chunk, 큰 bundle NOT (HTTP/2-friendly)
// vite.config.js
export default {
  build: {
    target: 'es2022',
    rollupOptions: {
      output: {
        // 주요 라이브러리 당 vendor chunk 나누기 — 영원 캐시, 앱 코드 변경이 무효화 안 함
        manualChunks: {
          'react-vendor': ['react', 'react-dom'],
          'router':       ['react-router'],
          'state':        ['zustand', 'jotai'],
          // ... 작고 집중된 chunk
        },
      },
    },
  },
};

// HTTP/2 가 이 전략 이김: 각 chunk 가 별개 파일, 독립 캐시.
// 앱 코드 버그 fix 가 200KB react-vendor chunk 안 무효화.
// HTTP/2 없으면 request 최소화 위해 큰 bundle 하나 원함; 있으면 세밀 승리.

External links

Exercise

실제 HTTP/2-지원 사이트 (Cloudflare/Fastly/Vercel 가 앞에 있는 현대 사이트 아무거나) 골라. Curl 로 홈페이지 두 번 fetch: 한 번 --http1.1, 한 번 --http2. TCP 연결 몇 개 열렸는지 세고 ('Trying' 줄 봐) 전체 fetch 시간 재. 그 다음 페이지의 모든 asset download (HTML 에서 asset URL 찾아), protocol 당 한 번. HTTP/2 fetch 가 asset bundle 에 극적으로 더 빨라야. 보너스: HTTP/2 가진 실제 사이트에서 Chrome DevTools' Network 패널 열고 'Protocol' 열 봐 — 모든 거에 'h2' 라고 해야.
Hint
curl -v 의 'Trying X.X.X.X:443' 줄이 TCP 연결 몇 개 열렸는지 알려줘. HTTP/1.1 이 최대 N 열 (curl 기본 --parallel 에 1 cap; 브라우저가 6 cap); HTTP/2 가 1 열고 multiplex. Chrome DevTools 'Protocol' 열이 가장 쉬운 시각 확인. Asset-bundle 벤치마크가 가장 극적 — asset 50+ 가진 페이지가 HTTP/2 승리 분명히 보여줘.

Progress

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

댓글 0

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

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