본문 바로가기
C.W.K.
Stream
Lesson 06 of 07 · published

HTTP 헤더 — 역할별로 묶어 읽기

~11 min · foundations, headers, metadata, taxonomy

Level 0HTTP 입문자
0 XP0/46 lessons0/12 achievements
0/120 XP to next level120 XP to go0% complete
"HTTP 헤더는 수백 개지만 전부 외울 필요는 없어. 역할별로 묶어 보면 낯선 헤더도 어디에 쓰이는지 빠르게 짐작할 수 있어."

실무의 대부분을 설명하는 다섯 범주

IANA 레지스트리에는 수많은 HTTP 필드가 등록되어 있고, 애플리케이션이 정의한 필드도 많아. 이름을 하나씩 외우기보다 역할을 기준으로 분류해 보자. 일부 헤더는 둘 이상의 역할을 맡지만, 다음 다섯 범주를 기준으로 보면 실제 요청과 응답을 훨씬 수월하게 읽을 수 있어.

  1. 요청 제어와 협상 — 클라이언트가 요청 처리 조건과 원하는 응답 형식을 서버에 전달해. Host, Authorization, Accept, Accept-Encoding, Accept-Language, User-Agent, Cookie, Origin, Referer가 여기에 들어가.
  2. 응답 제어 — 서버가 응답 처리 방법이나 이후 요청에 필요한 정보를 알려 줘. Server, Set-Cookie, WWW-Authenticate, Location, Vary, Allow, Retry-After가 대표적이야.
  3. 표현 메타데이터 — 요청이나 응답의 본문 표현을 설명해. Content-Type, Content-Length, Content-Encoding, Content-Language, Last-Modified, ETag를 자주 만나.
  4. 캐시와 조건부 요청 — 저장된 응답을 언제 재사용하고 다시 검증할지 조율해. Cache-Control, Expires, ETag, If-None-Match, If-Modified-Since, Vary가 이 역할을 맡아. ETag와 Vary처럼 다른 범주와 겹치는 필드도 있어. 트랙 5에서 자세히 다룰 거야.
  5. 연결과 전송 — HTTP/1.1 연결과 메시지 전송 방식을 제어해. Connection, Keep-Alive, Transfer-Encoding, Upgrade가 대표적이야. HTTP/2와 HTTP/3에서는 연결별 헤더 상당수가 금지되거나 이진 프레임 기능으로 대체돼.

실제 교환을 범주로 읽기

원시 요청이나 응답을 만나면 헤더를 하나씩 이 범주에 놓아 봐. 어떤 필드가 본문을 설명하고, 어떤 필드가 캐시 정책을 전하며, 어떤 필드가 인증과 세션을 이어 주는지 구분하면 문서를 열기 전에도 메시지의 의도를 상당 부분 파악할 수 있어.

헤더는 본문을 건드리지 않고 통신 조건을 조율하는 공간이야. Content-Type, 언어, 인코딩, 최신성, 인증, 세션 정보를 헤더에 담으면 본문은 업무 데이터 자체에 집중할 수 있어. 물론 업무 데이터로서 requested_language가 필요할 수는 있지만, HTTP의 콘텐츠 협상을 다시 발명하려고 같은 필드를 넣는다면 기존 헤더를 먼저 검토해야 해.

사용자 정의 헤더 — 새 필드에 X- 접두사를 붙이지 마

오랫동안 사용자 정의 헤더에는 X- 접두사를 붙이는 관례가 있었어(X-Request-ID, X-Forwarded-For). RFC 6648(2012)은 이 관례를 공식적으로 폐기하도록 권고했어. 새 필드는 Pippa-Request-ID, Stripe-Signature, cf-ray처럼 용도와 소유자가 드러나는 이름을 고르면 돼. 다만 기존 X- 헤더를 무턱대고 바꾸라는 뜻은 아니야. X-Forwarded-For처럼 널리 배포된 필드는 호환성을 위해 그대로 유지하고, 새 API에서 X- 접두사 관례를 되풀이하지 않는 것이 핵심이야.

cwkPippa에서 볼 수 있는 헤더

전형적인 cwkPippa 요청에는 Host: localhost:8000, Authorization: Bearer ..., Accept: application/json, Content-Type: application/json(POST 본문이 있을 때), User-Agent가 들어가. 응답에서는 Content-Type: application/jsonContent-Length 같은 표현 메타데이터를 보고, HTTP/1.1 연결에는 Connection: keep-alive가 나타날 수 있어. 정적 자산에는 Cache-Control이 붙기도 하지. HTTP/1.1 SSE 응답의 Content-Type: text/event-stream은 브라우저가 이벤트 스트림으로 해석해야 한다는 뜻이고, Transfer-Encoding: chunked는 본문을 길이가 정해진 한 덩어리가 아니라 청크로 전송한다는 뜻이야.

Code

실제에 가까운 요청 — 헤더를 역할별로 분류·http
GET /api/conversations HTTP/1.1                  <- request line
Host: localhost:8000                             <- request control
Authorization: Bearer eyJhbGciOi...              <- request control
Accept: application/json                         <- request control (negotiation)
Accept-Encoding: gzip, br                        <- request control (negotiation)
Accept-Language: ko-KR, en-US;q=0.8              <- request control (negotiation)
User-Agent: cwkPippa-WebUI/1.0                   <- request control
Origin: http://localhost:5173                    <- request control (CORS)
Cookie: session=abc123                           <- request control (session)
Pippa-Request-ID: req_xyz789                     <- request control (custom)
대응하는 응답 — 다섯 범주를 한눈에 확인·http
HTTP/1.1 200 OK                                  <- status line
Content-Type: application/json; charset=utf-8    <- representation metadata
Content-Length: 4823                             <- representation metadata
Content-Encoding: gzip                           <- representation metadata
ETag: "v17-abc123"                              <- representation + caching
Last-Modified: Sun, 25 May 2026 03:00:00 GMT     <- representation + caching
Cache-Control: private, max-age=60               <- caching
Vary: Accept-Encoding, Authorization             <- caching + response control
Set-Cookie: session=abc123; HttpOnly; Secure     <- response control
Server: uvicorn                                  <- response control
Connection: keep-alive                           <- connection
Pippa-Request-ID: req_xyz789                     <- custom (tracing 위해 echo)
응답 헤더를 프로그램으로 분류하기·python
import httpx

# 실제 exchange 의 모든 header 검사
resp = httpx.get('https://creativeworksofknowledge.com/')

print('--- response headers, 도착 순서 ---')
for key, value in resp.headers.items():
    print(f'{key}: {value}')

# 아는 것 분류
request_control = {'host', 'authorization', 'accept', 'accept-encoding',
                   'user-agent', 'cookie', 'origin', 'referer'}
representation  = {'content-type', 'content-length', 'content-encoding',
                   'content-language', 'last-modified', 'etag'}
caching         = {'cache-control', 'expires', 'if-none-match',
                   'if-modified-since', 'vary', 'age'}
response_ctrl   = {'server', 'set-cookie', 'www-authenticate', 'location',
                   'allow', 'retry-after'}
connection      = {'connection', 'keep-alive', 'transfer-encoding', 'upgrade'}

for key in resp.headers:
    lk = key.lower()
    if lk in representation: cat = 'representation'
    elif lk in caching:      cat = 'caching'
    elif lk in response_ctrl: cat = 'response-control'
    elif lk in connection:   cat = 'connection'
    else:                    cat = 'other/custom'
    print(f'{key:30s} -> {cat}')

External links

Exercise

접근하기 쉬운 응답 세 종류를 골라 봐. (1) JSON API(cwkPippa, GitHub, Stripe 등), (2) 정적 자산(이미지 URL 등), (3) 스트리밍 엔드포인트(SSE 또는 AI 채팅 API 등). 각 응답의 헤더를 모두 적고 다섯 범주로 분류해. 응답 종류마다 어떤 범주의 헤더가 두드러지는지, 특정 종류에서만 보이는 헤더가 있는지도 찾아봐.
Hint
정적 자산에서는 Cache-Control, ETag, Vary 같은 캐시 필드가 두드러져. JSON API는 표현 메타데이터와 인증, 요청 추적 필드를 자주 사용하고, SSE 응답에서는 Content-Type: text/event-stream과 Cache-Control: no-cache를 흔히 볼 수 있어. HTTP/1.1에서는 Transfer-Encoding: chunked도 나타날 수 있지만 HTTP/2와 HTTP/3에서는 전송 방식이 다르다는 점을 함께 기억해.

Progress

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

댓글 0

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

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