topic난이도 · 약 35

HTTP · HTTPS · HTTP/2 · WebSocket

요청·응답 구조, 주요 헤더, 쿠키, CORS, TLS 핸드셰이크, HTTP/2 멀티플렉싱, WebSocket까지.

#HTTP#HTTPS#TLS#쿠키#CORS#WebSocket#헤더
왜 배우는가

배포 후 CORS 에러, 로그인 후 쿠키 사라짐, '로컬은 되는데 프로덕션은 안 됨' — 전부 HTTP 메시지 층의 문제다. REST API 호출이 GET/POST만이 아니라는 걸 알면 AI에게 정확한 수정 지시를 할 수 있다.

HTTP는 편지다. 요청도 응답도 같은 구조로 생겼다. ① 시작줄 (메서드+URL 또는 상태코드) → ② 헤더 여러 줄 (메타데이터) → ③ 빈 줄④ 본문(body). 이 4단 구조가 요청·응답 모두 똑같다.

HTTP 편지 구조 — 요청과 응답 모두 '시작줄 + 헤더 + 빈줄 + 본문' 4단
http
# ━━━ 요청 (Request) ━━━
POST /api/login HTTP/1.1
Host: jit-learning.com
Content-Type: application/json
Authorization: Bearer eyJhbGc...
Cookie: theme=dark; sid=abc123

{"email": "kim@example.com", "password": "****"}

# ━━━ 응답 (Response) ━━━
HTTP/1.1 200 OK
Content-Type: application/json
Set-Cookie: sid=xyz789; HttpOnly; Secure; SameSite=Lax
Access-Control-Allow-Origin: https://jit-learning.com
Cache-Control: no-store

{"user": {"id": 42, "name": "김성훈"}}

`curl -v https://api...`로 이 전문을 직접 볼 수 있다. Claude에게 버그 제보할 때 이 블록을 그대로 붙이면 원인 파악이 10배 빠르다.

헤더는 편지의 봉투다. 본문을 열지 않고도 누가·어떤 형식으로·어떤 권한으로 보냈는지 알 수 있게 한다. 바이브코더가 만나는 헤더는 사실상 이 10개뿐이다.

헤더방향예시
Content-Type요청·응답본문 포맷`application/json`, `multipart/form-data`
Authorization요청인증 정보`Bearer <JWT>`, `Basic base64(id:pw)`
Cookie요청클라이언트 쿠키 묶음`sid=abc; theme=dark`
Set-Cookie응답브라우저에 쿠키 저장 지시`sid=xyz; HttpOnly; Secure`
Cache-Control응답캐시 정책`no-store`, `max-age=3600`
Access-Control-Allow-Origin응답CORS 허용 출처`https://jit.co.kr`, `*`
User-Agent요청클라이언트 종류`Mozilla/5.0 (...) Chrome/...`
Referer요청직전 페이지`https://google.com/`
Content-Length양쪽본문 바이트 수`1287`
Location응답리다이렉트 대상`https://...` (3xx와 함께)

쿠키(Cookie)는 브라우저에 저장되는 작은 텍스트다. 서버가 `Set-Cookie`로 저장 지시를 내리면, 이후 같은 도메인 요청마다 `Cookie` 헤더에 자동으로 따라간다. 로그인 유지의 대부분이 이 메커니즘이다.

플래그왜 중요?
HttpOnlyJavaScript로 읽기 금지XSS 공격자가 토큰 훔치지 못함
SecureHTTPS로만 전송중간자가 평문으로 못 봄
SameSite=Lax|Strict|None교차 사이트 요청 동작 제어CSRF 공격 방어
Max-Age / Expires유효기간세션 쿠키 vs 영구 쿠키
Path / Domain유효 범위`/admin`·서브도메인 제어
javascript
// 안전한 세션 쿠키 (로그인용 — 3종 플래그 필수)
res.setHeader("Set-Cookie", [
  "sid=abc123; HttpOnly; Secure; SameSite=Lax; Max-Age=86400; Path=/"
]);

// ❌ 위험한 쿠키 — XSS 한 방에 털림
// document.cookie = "token=..."  ← JS에서 읽히는 토큰은 이미 XSS 취약

// 프론트에서 쿠키 읽기 (HttpOnly 빼면 아래처럼 읽기는 가능)
const sid = document.cookie
  .split("; ")
  .find(c => c.startsWith("sid="))
  ?.split("=")[1];

로그인 세션 쿠키는 무조건 HttpOnly + Secure + SameSite 3종 세트. 이 중 하나라도 빠지면 공격 면이 열린다.

CORS는 브라우저가 다른 출처 응답을 막는 보안 정책이다. ch08에서 겉만 다뤘으니 심화한다. 출처(Origin)는 `스킴://호스트:포트`의 3종 세트 — 하나라도 다르면 CORS. `https://jit.co.kr`과 `https://api.jit.co.kr`도 다른 출처다.

CORS 흐름 — 브라우저가 요청 전에 OPTIONS 프리플라이트를 먼저 쏘고, 허용 헤더를 받아야 본 요청을 보낸다
응답 헤더주의
Access-Control-Allow-Origin허용할 출처`*`은 쿠키 전송 불가
Access-Control-Allow-Credentials쿠키 전송 허용프론트에서도 `credentials: 'include'` 필요
Access-Control-Allow-Methods허용 메서드프리플라이트 응답에 명시
Access-Control-Allow-Headers허용 커스텀 헤더`Authorization`, `Content-Type` 등
Access-Control-Max-Age프리플라이트 캐시 초반복 요청 최적화

프리플라이트(Preflight) — 브라우저가 '복잡한' 요청(커스텀 헤더, PUT/DELETE 등)을 보내기 전에 OPTIONS 메서드로 허가를 먼저 묻는다. 서버가 허가 헤더를 응답해야 본 요청이 나간다. 서버 로그에 OPTIONS만 찍히고 GET/POST가 안 찍힌다면 이 단계에서 막힌 것.

javascript
// Next.js App Router — CORS 허용 (쿠키 포함 버전)
export async function GET(request: Request) {
  const data = { hello: "world" };
  return new Response(JSON.stringify(data), {
    headers: {
      "Content-Type": "application/json",
      "Access-Control-Allow-Origin": "https://jit-learning.com", // '*' 금지 (쿠키 못 씀)
      "Access-Control-Allow-Credentials": "true",
      "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
      "Access-Control-Allow-Headers": "Content-Type, Authorization",
    },
  });
}

// OPTIONS 프리플라이트도 별도 핸들러로 답해야 한다
export async function OPTIONS() {
  return new Response(null, { status: 204, headers: { /* 위와 동일 */ } });
}

// 프론트 측 fetch — 쿠키를 같이 보내려면 credentials 필수
await fetch("https://api.jit.co.kr/me", {
  credentials: "include",  // ← 빼먹으면 쿠키 안 감
});

`Allow-Origin: *`과 `Allow-Credentials: true`는 동시에 쓰면 브라우저가 거부한다. 쿠키를 쓸 땐 반드시 정확한 도메인을 적는다.

HTTPS = HTTP + TLS. TLS는 세 가지를 보장한다. ① 암호화(중간에서 못 엿봄), ② 무결성(중간에서 못 고침), ③ 인증(상대가 진짜 jit.co.kr인지 증명). Vercel·Netlify는 TLS 인증서를 Let's Encrypt로 자동 발급한다.

단계뭐 하나비유
① ClientHello브라우저가 지원 암호 목록 전송"이 언어들 중 뭐로 얘기할까요?"
② ServerHello + 인증서서버가 공개키 담긴 인증서 제시"저는 진짜입니다, 이 도장 보세요"
③ 키 교환비대칭으로 세션키 합의"이제부터 이 비밀키로 얘기합시다"
④ 대칭 암호화 통신AES 등으로 실제 데이터 전송일상 대화 시작

TLS가 보호하지 못하는 것 — URL 경로(`/admin/secret`)는 암호화되지만, 접속하는 도메인(jit.co.kr)은 SNI 때문에 평문으로 보인다. 즉 '이 사람이 어떤 사이트에 갔는지'는 ISP가 안다. VPN이 추가로 필요한 이유.

HTTP/1.1 — 요청 하나에 연결 하나. 페이지에 이미지 50개 있으면 느려짐(HOL blocking). HTTP/2 — 한 연결에 여러 요청을 멀티플렉싱으로 동시 전송 + 헤더 압축. HTTP/3 — 전송 계층을 TCP에서 QUIC(UDP 기반)로 바꿔 모바일·불안정 네트워크에서 빠름. Vercel·Cloudflare는 자동으로 H3까지 지원.

WebSocket은 양방향 실시간 채널이다. HTTP 요청으로 시작해 `Upgrade: websocket` 헤더로 전환(handshake)한 뒤, 같은 TCP 연결을 유지하면서 서버↔클라이언트가 아무 때나 메시지를 주고받는다. 채팅, 실시간 알림, 온라인 게임이 전부 이것.

javascript
// 브라우저 WebSocket 클라이언트
const ws = new WebSocket("wss://api.jit.co.kr/chat");

ws.onopen = () => ws.send(JSON.stringify({ type: "hello" }));
ws.onmessage = (e) => console.log("서버:", JSON.parse(e.data));
ws.onclose = () => console.log("연결 종료");

// 언제 뭘 쓸까?
// - 단방향 주기 조회        → REST (폴링)
// - 서버→클라이언트 알림만  → Server-Sent Events (SSE)
// - 양방향 실시간            → WebSocket
// - 초저지연 + 손실 허용     → WebRTC (화상·음성)

WebSocket은 시작만 HTTP다. 일단 연결되면 HTTP 규칙이 사라지고 메시지 기반 채널이 된다. 방화벽·프록시가 막는 경우도 있어 폴백(SSE)을 준비하는 게 안전.

Claude에게 이렇게 말하면 바로 고쳐준다. "이 fetch가 401을 받음. Authorization 헤더 확인하고, 쿠키가 필요하면 credentials: 'include' 추가, 서버엔 Access-Control-Allow-Credentials: true와 정확한 Origin 응답하게 수정해줘." — 헤더 이름이 들어가야 Claude가 맥락 잡기 쉽다.

실기 드릴 4문항
edit실기 드릴 · 단답형

로그인 세션 쿠키에 반드시 붙여야 할 3종 보안 플래그는?

check_circle실기 드릴 · OX

`Access-Control-Allow-Origin: *`으로 두면 쿠키를 자동으로 같이 보낼 수 있다.

edit실기 드릴 · 단답형

HTTPS 핸드셰이크에서 서버가 클라이언트에게 보내는 것 — 자신이 진짜임을 증명하는 '도장' 역할을 하는 것은?

check_circle실기 드릴 · OX

WebSocket은 연결 후에도 매번 HTTP 요청으로 메시지를 보낸다.