쿠키부터 HTTPS까지, HTTP 활용 기술
1. 개요
웹 개발을 하면서 가장 자주 접하게 되는 프로토콜은 HTTP(Hypertext Transfer Protocol) 입니다.
우리는 흔히 이 프로토콜을 통해 데이터를 주고받고 있지만,
단순히 텍스트를 요청하고 응답하는 것 외에도,
사용자 상태 유지, 콘텐츠 최적화, 보안 통신, 인증 처리 등 매우 다양한 기능을 제공한다는 점은 간과하기 쉽습니다.
이번 글에서는 실전 프로젝트에서 꼭 필요한 다음과 같은 HTTP 활용 기술들을 정리해봅니다.
- 사용자의 로그인 상태를 유지하는 쿠키
- 서버 응답을 효율적으로 재활용하는 캐시
- 클라이언트와 서버가 언어, 포맷을 조율하는 콘텐츠 협상
- 사용자의 신원을 검증하는 인증
- 전송 중 데이터의 보안을 책임지는 HTTPS(보안 통신)
2. 쿠키 (Cookie)
2.1 쿠키란?
HTTP는 무상태(stateless) 프로토콜입니다.
즉, 클라이언트와 서버가 통신을 할 때,
이전 요청과 현재 요청 간의 상태가 연결되어 있지 않습니다.
이를 해결하기 위해 등장한 것이 쿠키(Cookie) 입니다.
2.2 Set-Cookie 헤더의 동작 방식
Set-Cookie: session_id=abcd1234; Path=/; HttpOnly
클라이언트는 이후 요청마다 다음과 같은 헤더를 전송합니다.
Cookie: session_id=abcd1234
2.3 쿠키의 주요 속성
속성 | 설명 |
---|---|
Name=Value |
쿠키의 이름과 값 |
Path |
어떤 경로에 대해 쿠키를 전송할지 지정 |
Domain |
쿠키를 전송할 도메인 지정 |
Expires / Max-Age |
쿠키의 유효 시간 |
Secure |
HTTPS 통신에서만 쿠키 전송 |
HttpOnly |
JavaScript에서 접근 불가 → 보안 강화 |
2.4 쿠키의 활용과 문제점
✅ 활용 사례
- 로그인 유지 (
session_id
) - 방문자 추적 (GA, 광고 등)
- 사용자 설정 저장 (언어, 테마 등)
❌ 문제점
- 보안 위험: 쿠키 탈취(XSS), 세션 하이재킹 등
- 용량 제한: 도메인당 4KB 이하
- 무조건 자동 전송: 필요 없는 요청에도 함께 전송
✍️ 실전 예시 (가상의 사례)
Set-Cookie: session_id=abc123; HttpOnly; Secure; Path=/; Max-Age=3600
보안 강화를 위해 HttpOnly와 Secure 옵션을 추가하고, 세션 만료 시간 설정으로 안정성 확보.
2.5 웹 스토리지: 로컬 스토리지와 세션 스토리지
쿠키 외에도 브라우저는 클라이언트 측에 데이터를 저장하기 위해 Web Storage API를 제공합니다.
대표적으로 로컬 스토리지(LocalStorage)와 세션 스토리지(SessionStorage)가 있으며, 쿠키보다 용량이 크고, 단순한 구조를 가집니다.
항목 | 쿠키 | 로컬 스토리지 | 세션 스토리지 |
용도 | 상태 유지, 인증 | 클라이언트 데이터 저장 | 탭 단위 임시 저장 |
저장 위치 | 서버/브라우저 | 브라우저 | |
자동 전송 | ✅ 서버에 매 요청마다 전송 | ❌ 서버에 전송 안 됨 | |
만료 시점 | 지정 가능 | 직접 삭제 전까지 | 탭 종료 시 소멸 |
저장 용량 | 약 4KB | 수 MB |
📌 보안이 중요한 정보(예: JWT)는 쿠키 + HttpOnly 조합으로 저장하고, 일반 UI 상태 정보(예: 다크 모드)는 로컬 스토리지를 활용하는 방식이 이상적입니다.
3. 캐시(Cache)
3.1 웹 캐시란?
웹 캐시는 서버로부터 받은 리소스를
클라이언트(브라우저)나 중간 서버(CDN, 프록시 등)에 저장해두었다가,
같은 요청이 반복될 때 재사용하는 기술입니다.
- 목적: 성능 향상, 트래픽 감소, 서버 부하 완화
- 예: 이미지, JS, CSS, API 응답 등을 반복 요청 없이 재사용
3.2 캐시의 장점
이점 | 설명 |
✅ 성능 향상 | 로딩 속도 개선 (사용자 체감 속도 ↑) |
✅ 트래픽 절감 | 동일 리소스 요청 생략 |
✅ 서버 부하 감소 | 동시 접속 수 증가에도 안정성 확보 |
✅ 비용 최적화 | CDN + 캐시로 네트워크 요금 절감 가능 |
3.3 캐시 저장 위치
- 브라우저(로컬) : 사용자 개인 장치에 저장 (private cache)
- 중간 서버(프록시, CDN) : 다수 사용자 간 공유 캐시 (public cache)
📝 Cache-Control: private은 브라우저 캐시에만 저장되고, public은 공유 캐시도 허용합니다.
3.3 캐시 제어 HTTP 헤더
Cache-Control: max-age=1200, public
Expires: Tue, 06 Feb 2024 12:00:00 GMT
ETag: "v1.3"
Last-Modified: Mon, 05 Feb 2024 10:00:00 GMT
🔧 주요 캐시 관련 헤더
헤더 | 설명 |
Cache-Control | 캐시 동작 제어의 핵심 |
Expires | 만료 시점 설정 (HTTP/1.0) |
ETag | 컨텐츠 식별용 버전 태그 |
Last-Modified | 리소스 최종 수정 시각 |
📘 Cache-Control 디렉티브
디렉티브 | 설명 |
max-age=3600 | 1시간 동안 fresh |
no-cache | 캐시 사용 전 서버에 재검사 필요 |
no-store | 캐시에 절대 저장 금지 (민감 정보) |
private | 개인 클라이언트 전용 캐시 (공유 캐시 금지) |
public | 누구나 캐시 가능 (프록시, CDN 허용) |
must-revalidate | 만료 시 재검사 반드시 수행 |
Cache-Control: public, max-age=600, must-revalidate
3.4 Expires vs Cache-Control
항목 | 설명 |
Expires | 절대 시간 기반 만료 (HTTP/1.0 스타일) |
Cache-Control: max-age | 상대 시간 기반 만료 (HTTP/1.1 이후 권장) |
📌 두 개가 함께 있을 경우, Cache-Control이 우선 적용됩니다.
3.5 ETag vs Last-Modified
둘 다 조건부 요청(Validation)에 사용되는 헤더입니다.
항목 | 설명 |
ETag | 리소스의 고유 버전 ID (정밀 비교) |
Last-Modified | 최종 수정 시각 (초 단위, 정밀도 낮음) |
If-None-Match: "v1.2.3" → 서버가 ETag 비교
If-Modified-Since: Tue, 25 Mar 2025 10:00:00 GMT
📌 둘 다 있다면 브라우저는 ETag를 우선적으로 사용합니다.
3.6 캐시 신선도(freshness)
캐시가 fresh(신선)한지, stale(만료)됐는지를 판단하는 기준
현재 시각 - 응답 수신 시각 < max-age → fresh
- fresh: 서버 요청 없이 캐시 사용
- stale: 재검사 필요 → 304 or 새로 받음
3.7 캐시 동작 방식: 3가지 케이스
① 캐시 그대로 사용 (fresh)
• 캐시가 아직 유효함
• 서버 요청 없이 클라이언트가 그대로 사용
② 조건부 요청 (stale, 변경 없음)
• 캐시가 만료됐지만 서버 리소스가 변경되지 않음
• 304 Not Modified 응답
• 기존 캐시 재사용
If-None-Match: "v1.2.3"
→ 304 Not Modified
③ 캐시 무효화 (stale, 변경됨)
• 캐시 만료 + 리소스 변경됨
• 서버가 새 리소스를 200 OK로 재전송
3.8 프록시 캐시 vs 브라우저 캐시
구분 | 브라우저 캐시 | 프록시 캐시 / CDN |
대상 | 개인 사용자 | 여러 사용자 공유 |
private | ✅ 허용 | ❌ 금지 |
public | ✅ 허용 | ✅ 허용 |
사용 예 | 로그인 상태 유지 | 정적 리소스 CDN 제공 |
✍️ 실전 예시 (가상의 사례)
사용자 프로필 이미지가 변경되었는데도, 브라우저에선 이전 이미지를 계속 보여주는 문제가 발생.
🔧 해결 방안
1. 이미지 URL에 버전 쿼리 추가 → /profile.jpg?v=20250325
2. 서버 응답 헤더 설정:
Cache-Control: private, max-age=300
ETag: "profile-img-v2"
브라우저는 캐시 유효 시간이 지나면 서버에 If-None-Match로 조건부 요청 →
변경 없음: 304 → 재사용 / 변경됨: 200 → 새로 받음
이후 브라우저는 적절히 캐시를 활용하면서도 최신 이미지 동기화를 유지할 수 있습니다.
✅ 캐시 요약 정리
요소 | 역할 |
Cache-Control | 캐시 정책 제어 (핵심) |
ETag / Last-Modified | 변경 여부 판단용 |
Expires | HTTP/1.0 호환 만료 시간 |
Fresh / Stale | 신선도 판단 기준 |
304 응답 | 조건부 요청의 결과 |
4. 콘텐츠 협상 (Content Negotiation)
4.1 콘텐츠 협상이란?
클라이언트가 요청 시 수신 가능한 포맷/언어/인코딩을 제안하고, 서버가 최적 콘텐츠를 제공하는 기능
4.2 콘텐츠 협상의 유형
유형 | 설명 |
---|---|
서버 주도 | Accept 헤더 기반 |
에이전트 주도 | 클라이언트 UI 선택 |
투명 협상 | 프록시가 판단 |
4.3 주요 헤더
Accept: application/json
Accept-Language: ko-KR, en-US;q=0.8
Accept-Encoding: gzip, deflate
✍️ 실전 예시 (가상의 사례)
브라우저 언어 설정 기반으로 자동 언어 제공
const lang = req.headers['accept-language'];
[여기서 잠깐!] 인증(Authentication)
방식 | 설명 | 보안성 |
---|---|---|
Basic | ID/PW를 base64 인코딩 | 낮음 |
Form 기반 | 세션과 쿠키 조합 | 중간 |
JWT | 토큰 기반 stateless 인증 | 높음 |
주의: HTTPS 미적용 시 인증 정보 노출 위험! 반드시 보안 채널에서만 사용하세요.
5. 보안 (HTTPS, TLS)
5.1 HTTPS가 필요한 이유
평문 전송 문제 해결 (도청, 위조, 변조 방지)
기본 HTTP 통신은 평문(Plain Text)입니다.
즉, 누군가가 네트워크 중간에서 통신을 엿본다면 다음과 같은 정보가 그대로 노출됩니다.
- 로그인 ID, 비밀번호
- 카드번호, 주소 등 민감 정보
- 쿠키 값 → 세션 하이재킹 위험
이러한 문제를 해결하기 위해 사용되는 것이 바로 HTTPS (HTTP Secure) 입니다.
5.2 HTTPS의 핵심: TLS 프로토콜
HTTPS는 내부적으로 TLS(Transport Layer Security) 프로토콜을 사용하여 다음 3가지를 보장합니다.
- 기밀성 : 전송되는 데이터가 암호화되어 도청 불가
- 무결성 : 중간에 데이터가 변경되었는지 여부 확인 가능
- 인증성 : 서버(또는 클라이언트)의 신원을 검증 가능
5.3 TLS 통신 흐름 요약
HTTPS 통신이 시작되면, 클라이언트와 서버는 TLS 핸드셰이크 과정을 통해
서로의 신원을 확인하고 안전하게 대칭키를 공유하게 됩니다.
아래 이미지는 TLS 1.3 기준의 핸드셰이크 흐름입니다.
📘 단계별 설명
1. ClientHello : 클라이언트가 지원하는 프로토콜 버전, 암호화 스위트, key_share(선택적) 등을 서버에 전달
2. ServerHello : 서버가 선택한 암호화 방식과 자신의 key_share(선택적)을 응답
3. EncryptedExtensions : 서버의 추가 옵션 정보를 클라이언트에게 전달
4. Certificate : 서버의 공개키 인증서를 클라이언트에게 전달
5. CertificateVerify : 서버가 해당 인증서의 개인키로 서명해 신뢰성을 증명
6. Finished : 서버가 핸드셰이크 완료를 알림
7. Finished : 클라이언트가 인증 확인 후 핸드셰이크 완료
📌 TLS 1.3에서는 보안성과 성능을 모두 고려하여, 핸드셰이크가 1 RTT(왕복 시간)만에 완료됩니다. 또한, 일부 메시지는 선택적으로 포함되며, 이전 버전보다 더 간결하고 안전한 구조를 제공합니다.
* TLS 핸드셰이크는 전송 계층의 TCP 핸드셰이크가 끝난 후에 진행되는 보안 절차임.
5.4 대칭키 vs 공개키 암호화
🗝️ 대칭키 암호화란?
“열쇠 하나로 잠그고 여는 방식”
대칭키 암호화는 하나의 동일한 비밀키(key)를 사용하여
데이터를 암호화하고 복호화합니다.
보내는 사람과 받는 사람이 같은 키를 공유해야 하기 때문에 빠르고 효율적하지만,
그 키를 안전하게 전달하는 것이 큰 과제입니다.
📦 예시
편지를 보낼 때, 자물쇠로 잠근 뒤 그 열쇠를 따로 전달해야 한다면?
→ 열쇠가 도중에 유출될 위험이 큼
🔧 사용 알고리즘
: AES, DES, RC4 등
🔓 공개키 암호화란?
“잠그는 열쇠는 공개, 여는 열쇠는 비밀”
공개키 암호화는 열쇠가 두 개입니다.
- 공개키(Public Key): 누구나 볼 수 있음 → 데이터를 잠그는 용도
- 개인키(Private Key): 서버만 가지고 있음 → 데이터를 여는 용도
클라이언트는 서버의 공개키로 데이터를 암호화해서 전송하고,
서버는 그에 상응하는 개인키로만 이를 복호화할 수 있습니다.
→ 키를 미리 공유할 필요가 없으므로 안전하지만, 처리 속도가 느림
📨 예시
어떤 금고를 누구나 잠글 수 있지만, 여는 열쇠는 금고 주인만 가지고 있음
🔧 사용 알고리즘
RSA, ECC 등
🔁 TLS에서 어떻게 쓰일까?
TLS에서는 두 방식의 장점을 모두 활용합니다.
1. 클라이언트가 서버의 공개키로 Pre-Master Key를 암호화해서 전송 (공개키 암호화)
2. 서버는 개인키로 이를 복호화하고, 클라이언트와 동일한 대칭키를 생성
3. 이후 실제 데이터는 대칭키로 암호화하여 빠르게 통신
✅ 결론
- 대칭키는 빠르지만 키 전달이 문제
- 공개키는 안전하지만 느림
→ 그래서 TLS에서는 공개키로 안전하게 대칭키를 주고받고, 대칭키로 데이터를 주고받는다.
6. 정리 및 배운 점
주제 | 키워드 | 실전 활용 |
---|---|---|
쿠키 | 세션, 상태 유지 | 로그인 관리 |
캐시 | Cache-Control, ETag | 정적 자산 최적화 |
콘텐츠 협상 | Accept-* 헤더 | 다국어, API 대응 |
인증 | Basic, JWT | 사용자 검증 |
보안 | HTTPS, TLS, 인증서 | 민감 정보 보호 |
🔗 참고 자료
- 『이것이 취업을 위한 컴퓨터 과학이다』, p.464~479
- https://developer.mozilla.org/ko/docs/Web/HTTP/Cookies
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation
- https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html
'Network' 카테고리의 다른 글
프록시와 스케일링: 안정적인 트래픽 처리 (0) | 2025.03.26 |
---|---|
[2편] HTTP 메서드부터 상태 코드, 주요 헤더까지 (0) | 2025.03.26 |
[1편] 브라우저는 어떻게 서버를 찾을까? – DNS와 HTTP 메시지 구조까지 (0) | 2025.03.26 |
URL 입력부터 웹 페이지 렌더링까지 동작 (현대 브라우저 기준) (1) | 2025.03.05 |