Skip to content
HTTP 기초제 7장

헤더와 쿠키는 본문 밖에서 어떤 문맥을 전달할까?

07. 헤더와 쿠키


학습 목표

  1. HTTP 헤더가 메시지 바디를 설명하고 동작을 조정하는 역할을 한다는 점을 이해할 수 있다.
  2. Content-Type, Content-Encoding, Content-Language, Content-Length를 구분할 수 있다.
  3. Accept, Accept-Encoding, Accept-Language가 왜 “협상” 헤더라고 불리는지 설명할 수 있다.
  4. Host, Authorization, WWW-Authenticate가 어떤 상황에서 쓰이는지 이해할 수 있다.
  5. Set-CookieCookie가 무상태 HTTP 위에서 어떻게 상태를 이어붙이는지 설명할 수 있다.
  6. 쿠키 속성(Expires/Max-Age, Domain, Path, Secure, HttpOnly, SameSite)의 목적을 구분할 수 있다.

전체 구조


1. 헤더는 왜 바디만큼 중요할까?

HTTP 메시지를 떠올리면 구조는 단순하다.

  • 시작 라인
  • 헤더
  • 공백 라인
  • 바디

그런데 실제 동작을 바꾸는 정보는 상당수가 헤더에 있다.

  • 바디가 JSON인지 HTML인지
  • 압축되었는지
  • 어떤 언어를 선호하는지
  • 어느 도메인으로 보낸 요청인지
  • 인증 정보가 있는지
  • 로그인 상태를 이어갈 쿠키가 있는지

즉, 바디가 “실제 데이터”라면 헤더는 그 데이터를 해석하고, 요청/응답 동작을 제어하는 메타데이터라고 보면 된다.

이번 장에서 집중할 범위

헤더는 종류가 매우 많다.
이번 장에서는 실무에서 흐름을 가장 많이 바꾸는 핵심만 잡는다.

  • 표현 헤더
  • 협상 헤더
  • 필수/인증 헤더
  • 쿠키

캐시와 조건부 요청 헤더는 다음 장에서 따로 다룬다.


2. 표현 헤더: 지금 이 바디가 무엇인지 설명한다

HTTP에서 리소스는 그대로 전송되지 않는다.
실제로는 리소스를 표현(representation) 형태로 바꿔서 주고받는다.

예를 들어 “회원”이라는 리소스는 이렇게 표현될 수 있다.

  • HTML 문서
  • JSON 데이터
  • XML 데이터

즉, 같은 리소스라도 클라이언트와 서버가 주고받는 실제 형태는 달라질 수 있다.

자주 보는 표현 헤더

헤더역할예시한 줄 직관
Content-Type바디 형식application/json이 바디는 JSON이다
Content-Encoding압축 방식gzip이 바디는 gzip으로 압축됐다
Content-Language자연 언어ko이 본문은 한국어다
Content-Length바디 길이(바이트)1234바디 크기는 1234바이트다

2-1. Content-Type

이 헤더는 표현 데이터의 형식을 알려준다.

예:

http
Content-Type: application/json

혹은:

http
Content-Type: text/html; charset=utf-8

이 정보가 없으면 받는 쪽은 바디를 어떻게 해석해야 할지 알기 어렵다.

  • JSON으로 파싱해야 하는가
  • HTML로 렌더링해야 하는가
  • 이미지로 처리해야 하는가

2-2. Content-Encoding

이 헤더는 바디가 어떤 방식으로 압축되었는지 알려준다.

예:

http
Content-Encoding: gzip

서버가 바디를 압축해서 보내면 네트워크 비용이 줄어든다.
하지만 클라이언트는 무엇으로 압축되었는지 알아야 풀 수 있다.
그 역할을 이 헤더가 맡는다.

2-3. Content-Language

이 헤더는 표현 데이터가 어떤 자연 언어로 작성되었는지 알려준다.

예:

http
Content-Language: ko

예를 들어 다국어 사이트라면:

  • 한국어 응답인지
  • 영어 응답인지

를 클라이언트가 명확히 이해할 수 있다.

2-4. Content-Length

이 헤더는 바디 길이를 바이트 단위로 알려준다.

예:

http
Content-Length: 3421

중요한 점은 문자 수가 아니라 바이트 수라는 것이다.
다만 모든 상황에서 항상 쓰이는 것은 아니고, 전송 방식을 다르게 쓰는 경우에는 다른 메커니즘을 사용하기도 한다.

핵심 직관: 표현 헤더는 “이 바디를 어떻게 읽어야 하는가?”에 대한 설명서다.


3. 협상 헤더: 클라이언트가 무엇을 원하는지 말한다

표현 헤더가 “실제로 무엇을 보냈는가”라면, 협상 헤더는 “클라이언트가 무엇을 선호하는가”에 가깝다.

즉, 협상은 요청에서 시작된다.

  • 나는 JSON을 더 원해
  • 나는 gzip 압축을 받을 수 있어
  • 나는 한국어를 선호해

이를 헤더로 서버에 알려주는 것이다.

자주 보는 협상 헤더

헤더의미예시
Accept선호 미디어 타입Accept: application/json
Accept-Encoding선호 압축 방식Accept-Encoding: gzip, br
Accept-Language선호 언어Accept-Language: ko-KR, ko;q=0.9, en;q=0.7

3-1. Accept

클라이언트가 선호하는 미디어 타입을 보낸다.

예:

http
Accept: application/json

혹은 여러 형식을 우선순위와 함께 보낼 수도 있다.

http
Accept: text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8

이 말은 “나는 HTML을 가장 선호하지만, 필요하면 XML도 되고, 최악에는 아무거나 받을 수 있어”에 가깝다.

3-2. Accept-Encoding

클라이언트가 어떤 압축 인코딩을 처리할 수 있는지 알린다.

예:

http
Accept-Encoding: gzip, br

그러면 서버는:

  • gzip으로 압축해서 보낼 수 있고
  • 그 경우 응답에 Content-Encoding: gzip을 붙인다

즉, Accept-EncodingContent-Encoding은 짝으로 이해하면 쉽다.

3-3. Accept-Language

클라이언트가 선호하는 언어를 알려준다.

예:

http
Accept-Language: ko-KR, ko;q=0.9, en;q=0.7

이 뜻은 대략 이렇다.

  1. 한국어(대한민국)를 가장 선호
  2. 그다음은 한국어 일반
  3. 그것도 안 되면 영어

q 값은 어떻게 읽을까?

q는 quality value, 즉 우선순위다.

  • 범위는 0에서 1
  • 클수록 우선순위가 높다
  • 생략하면 1로 본다

즉:

  • ko-KR1.0
  • ko;q=0.9
  • en;q=0.7

이런 순서다.

구체적인 값이 더 우선한다

단순 우선순위뿐 아니라, 더 구체적인 표현이 일반 표현보다 우선하는 경우가 많다.

예를 들어:

  • ko-KRko보다 구체적이다
  • text/htmltext/*보다 구체적이다

그래서 협상은 보통 이렇게 이해하면 된다.

클라이언트가 선호도를 보낸다 → 서버가 가능한 후보를 비교한다 → 가장 적절한 표현으로 응답한다


4. 동작을 바꾸는 핵심 헤더

모든 헤더가 단순 정보만 담는 것은 아니다.
어떤 헤더는 요청이 어느 서비스로 가는지, 인증이 필요한지 같은 핵심 동작을 직접 바꾼다.

4-1. Host: 이 요청이 어느 도메인으로 가는가?

Host 헤더는 요청 대상 서버의 호스트 이름과 필요한 경우 포트를 지정한다.

예:

http
Host: developer.mozilla.org

혹은:

http
Host: example.com:8443

이 헤더가 중요한 이유는 하나의 IP가 여러 도메인을 처리할 수 있기 때문이다.

Host가 없다면 서버는 같은 IP 안에서 어느 사이트로 보내야 할지 구분하기 어렵다.
그래서 HTTP/1.1에서는 매우 중요한 헤더다.

4-2. Authorization: 나는 이런 자격 증명을 보낸다

이 헤더는 클라이언트가 인증 정보를 서버에 전달할 때 사용한다.

예:

http
Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l

실무에서는 Basic 인증 외에도 Bearer 토큰 등 다양한 방식이 있다.
핵심은 같다.

  • 클라이언트가 자격 증명을 싣는다
  • 서버가 이 값을 보고 인증을 수행한다

4-3. WWW-Authenticate: 어떻게 인증해야 하는지 알려준다

보호된 리소스에 인증 없이 접근하면 서버는 보통 401 Unauthorized와 함께
어떤 인증 방식이 필요한지 힌트를 준다.

예:

http
WWW-Authenticate: Bearer

즉, 이 헤더는 “이 리소스에 접근하려면 이런 방식으로 인증해”라는 안내문에 가깝다.

핵심 직관: Authorization은 클라이언트가 내는 증명이고, WWW-Authenticate는 서버가 요구하는 인증 방식 안내다.


5. 쿠키: 무상태 HTTP 위에 상태를 이어붙이는 장치

HTTP는 기본적으로 무상태다.
즉, 한 번 요청이 끝나면 서버는 이전 요청 문맥을 기본적으로 기억하지 않는다.

그래서 이런 문제가 생긴다.

  1. 사용자가 로그인한다
  2. 서버가 로그인 성공 응답을 보낸다
  3. 사용자가 다시 /welcome에 접근한다
  4. 서버는 “이 요청이 방금 로그인한 그 사용자 것인지” 기본적으로 알 수 없다

그냥 요청 하나만 보면:

  • 누가 보냈는지
  • 로그인 상태인지

를 식별하기 어렵기 때문이다.

나쁜 해결책: 매 요청마다 사용자 정보를 직접 보내기

이론적으로는 매 요청마다 사용자 정보를 함께 보내면 된다.

하지만 이 방식은 문제가 많다.

  • 개발이 불편하다
  • 보안상 위험하다
  • 링크와 요청마다 사용자 정보를 계속 싣게 된다

그래서 등장한 것이 쿠키다.

5-1. Set-CookieCookie의 흐름

  • Set-Cookie: 서버가 브라우저에게 쿠키를 내려준다
  • Cookie: 브라우저가 저장한 쿠키를 이후 요청에 다시 실어 보낸다

이 흐름을 보면 핵심은 단순하다.

  • 서버는 브라우저에 쿠키를 심는다
  • 브라우저는 그 쿠키를 이후 요청에 자동으로 붙인다
  • 서버는 그 값을 보고 사용자를 식별한다

5-2. 왜 세션 ID만 저장하는가?

쿠키에는 사용자 이름, 주민등록번호, 카드 정보 같은 민감한 데이터를 직접 넣으면 안 된다.

실무에서는 보통 이렇게 한다.

  • 서버가 무작위 sessionId를 만든다
  • 서버 내부 저장소에 sessionId -> 사용자 정보를 매핑한다
  • 쿠키에는 sessionId만 넣는다

즉, 쿠키는 사용자 전체 정보를 싣는 게 아니라 식별용 키만 싣는 쪽이 안전하다.

5-3. 쿠키는 자동 전송된다는 장점과 비용이 있다

쿠키의 큰 장점은 브라우저가 자동으로 처리해준다는 점이다.
개발자가 매 요청마다 따로 로그인 식별 정보를 붙이지 않아도 된다.

하지만 비용도 있다.

  • 요청마다 자동 전송되어 네트워크 트래픽이 늘어난다
  • 민감한 정보를 잘못 넣으면 위험하다

그래서 쿠키에는 최소한의 정보만 넣는 것이 원칙이다.

5-4. 쿠키 수명: 세션 쿠키와 영속 쿠키

쿠키는 영원히 남는 것이 아니다.

세션 쿠키

  • 만료 시간을 명시하지 않음
  • 브라우저 세션이 끝날 때까지 유지

즉, 브라우저를 종료하면 사라지는 쪽에 가깝다.

영속 쿠키

  • Expires 또는 Max-Age를 지정
  • 정해진 시점 또는 시간까지 유지

예:

http
Set-Cookie: sessionId=abc123; Max-Age=3600

혹은:

http
Set-Cookie: sessionId=abc123; Expires=Wed, 21 Oct 2026 07:28:00 GMT

5-5. 쿠키 범위를 정하는 속성

쿠키는 아무 사이트, 아무 경로에나 무조건 전송되면 안 된다.
그래서 범위를 제한하는 속성이 있다.

속성역할한 줄 설명
Domain도메인 범위어떤 도메인에 쿠키를 보낼지 제한
Path경로 범위어떤 URL 경로 아래에서만 보낼지 제한
Expires / Max-Age수명언제 삭제할지 결정

예:

http
Set-Cookie: sessionId=abc123; Domain=example.org; Path=/; Max-Age=3600

5-6. 쿠키 보안을 위한 속성

보안과 관련해서는 특히 아래 세 가지를 꼭 기억해야 한다.

속성목적핵심 효과
SecureHTTPS 전용암호화되지 않은 HTTP로는 보내지 않음
HttpOnlyXSS 방어 보조자바스크립트에서 접근하지 못하게 함
SameSiteCSRF 방어 보조교차 사이트 요청에서 쿠키 전송을 제어

예:

http
Set-Cookie: sessionId=abc123; Path=/; Secure; HttpOnly; SameSite=Lax

SameSite=None을 쓸 때는 보통 Secure와 함께 생각해야 한다는 점도 자주 등장한다.

핵심 직관: 쿠키는 상태 유지의 도구이지만, 동시에 범위와 보안을 세밀하게 제어해야 하는 민감한 메커니즘이다.


핵심 암기 포인트

  • 헤더는 바디를 설명하고 요청/응답 동작을 제어하는 메타데이터다.
  • 표현 헤더는 실제 바디가 무엇인지 설명하고, 협상 헤더는 클라이언트가 무엇을 원했는지 설명한다.
  • Content-Type은 형식, Content-Encoding은 압축, Content-Language는 언어, Content-Length는 바이트 길이다.
  • Accept, Accept-Encoding, Accept-Language는 요청에서 선호도를 전달하는 협상 헤더다.
  • Host는 같은 IP 안에서 어느 도메인으로 가야 하는지 구분하는 데 중요하다.
  • Authorization은 인증 정보를 보내는 헤더이고, WWW-Authenticate는 서버가 요구하는 인증 방식을 알려주는 헤더다.
  • Set-Cookie는 서버가 쿠키를 심는 응답 헤더이고, Cookie는 브라우저가 다시 보내는 요청 헤더다.
  • 쿠키에는 민감한 데이터 대신 세션 ID 같은 최소 식별 정보만 넣는 편이 안전하다.
  • 쿠키 보안에서는 Secure, HttpOnly, SameSite를 우선 떠올려야 한다.

확인 질문

  1. 왜 같은 “회원” 리소스라도 HTML이나 JSON처럼 다른 표현으로 전달될 수 있을까?
  2. Content-TypeAccept의 차이는 무엇일까?
  3. Accept-Language: ko-KR, ko;q=0.9, en;q=0.7는 어떤 의미일까?
  4. Host 헤더가 없으면 하나의 IP에서 여러 도메인을 처리할 때 왜 문제가 생길까?
  5. AuthorizationWWW-Authenticate는 각각 누구의 입장에서 사용하는 헤더일까?
  6. 쿠키에 사용자 이름 대신 세션 ID만 저장하는 이유는 무엇일까?
  7. Secure, HttpOnly, SameSite는 각각 어떤 보안 위험을 줄이기 위한 속성일까?