테마
07. 헤더와 쿠키
학습 목표
- HTTP 헤더가 메시지 바디를 설명하고 동작을 조정하는 역할을 한다는 점을 이해할 수 있다.
Content-Type,Content-Encoding,Content-Language,Content-Length를 구분할 수 있다.Accept,Accept-Encoding,Accept-Language가 왜 “협상” 헤더라고 불리는지 설명할 수 있다.Host,Authorization,WWW-Authenticate가 어떤 상황에서 쓰이는지 이해할 수 있다.Set-Cookie와Cookie가 무상태 HTTP 위에서 어떻게 상태를 이어붙이는지 설명할 수 있다.- 쿠키 속성(
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-Encoding과 Content-Encoding은 짝으로 이해하면 쉽다.
3-3. Accept-Language
클라이언트가 선호하는 언어를 알려준다.
예:
http
Accept-Language: ko-KR, ko;q=0.9, en;q=0.7이 뜻은 대략 이렇다.
- 한국어(대한민국)를 가장 선호
- 그다음은 한국어 일반
- 그것도 안 되면 영어
q 값은 어떻게 읽을까?
q는 quality value, 즉 우선순위다.
- 범위는
0에서1 - 클수록 우선순위가 높다
- 생략하면
1로 본다
즉:
ko-KR→1.0ko;q=0.9en;q=0.7
이런 순서다.
구체적인 값이 더 우선한다
단순 우선순위뿐 아니라, 더 구체적인 표현이 일반 표현보다 우선하는 경우가 많다.
예를 들어:
ko-KR은ko보다 구체적이다text/html은text/*보다 구체적이다
그래서 협상은 보통 이렇게 이해하면 된다.
클라이언트가 선호도를 보낸다 → 서버가 가능한 후보를 비교한다 → 가장 적절한 표현으로 응답한다
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는 기본적으로 무상태다.
즉, 한 번 요청이 끝나면 서버는 이전 요청 문맥을 기본적으로 기억하지 않는다.
그래서 이런 문제가 생긴다.
- 사용자가 로그인한다
- 서버가 로그인 성공 응답을 보낸다
- 사용자가 다시
/welcome에 접근한다 - 서버는 “이 요청이 방금 로그인한 그 사용자 것인지” 기본적으로 알 수 없다
그냥 요청 하나만 보면:
- 누가 보냈는지
- 로그인 상태인지
를 식별하기 어렵기 때문이다.
나쁜 해결책: 매 요청마다 사용자 정보를 직접 보내기
이론적으로는 매 요청마다 사용자 정보를 함께 보내면 된다.
하지만 이 방식은 문제가 많다.
- 개발이 불편하다
- 보안상 위험하다
- 링크와 요청마다 사용자 정보를 계속 싣게 된다
그래서 등장한 것이 쿠키다.
5-1. Set-Cookie와 Cookie의 흐름
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 GMT5-5. 쿠키 범위를 정하는 속성
쿠키는 아무 사이트, 아무 경로에나 무조건 전송되면 안 된다.
그래서 범위를 제한하는 속성이 있다.
| 속성 | 역할 | 한 줄 설명 |
|---|---|---|
Domain | 도메인 범위 | 어떤 도메인에 쿠키를 보낼지 제한 |
Path | 경로 범위 | 어떤 URL 경로 아래에서만 보낼지 제한 |
Expires / Max-Age | 수명 | 언제 삭제할지 결정 |
예:
http
Set-Cookie: sessionId=abc123; Domain=example.org; Path=/; Max-Age=36005-6. 쿠키 보안을 위한 속성
보안과 관련해서는 특히 아래 세 가지를 꼭 기억해야 한다.
| 속성 | 목적 | 핵심 효과 |
|---|---|---|
Secure | HTTPS 전용 | 암호화되지 않은 HTTP로는 보내지 않음 |
HttpOnly | XSS 방어 보조 | 자바스크립트에서 접근하지 못하게 함 |
SameSite | CSRF 방어 보조 | 교차 사이트 요청에서 쿠키 전송을 제어 |
예:
http
Set-Cookie: sessionId=abc123; Path=/; Secure; HttpOnly; SameSite=LaxSameSite=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를 우선 떠올려야 한다.
확인 질문
- 왜 같은 “회원” 리소스라도 HTML이나 JSON처럼 다른 표현으로 전달될 수 있을까?
Content-Type과Accept의 차이는 무엇일까?Accept-Language: ko-KR, ko;q=0.9, en;q=0.7는 어떤 의미일까?Host헤더가 없으면 하나의 IP에서 여러 도메인을 처리할 때 왜 문제가 생길까?Authorization과WWW-Authenticate는 각각 누구의 입장에서 사용하는 헤더일까?- 쿠키에 사용자 이름 대신 세션 ID만 저장하는 이유는 무엇일까?
Secure,HttpOnly,SameSite는 각각 어떤 보안 위험을 줄이기 위한 속성일까?