웹의 대화 규약. 메서드 + URL + 헤더 + 본문의 요청과 상태 코드 + 헤더 + 본문의 응답 한 쌍이 전부다.
GET /users/1 HTTP/1.1
Host: api.shop.com
Accept: application/json
HTTP/1.1 200 OK
Content-Type: application/json
{ "id": 1 }
메서드에는 의미상의 약속이 있다
-
안전(safe) — 서버 상태를 바꾸지 않는다 GET · HEAD · OPTIONS
-
멱등(idempotent) 여러 번 보내도 결과가 같다 — GET · PUT · DELETE · HEAD
-
POST 는 둘 다 아니다 → 두 번 보내면 자원이 둘 생길 수 있다
- 재시도가 잦은 환경에서는 멱등 키 같은 보완이 필요하다
상태 코드
- 2xx 성공 — 200 OK · 201 Created · 204 No Content
- 3xx 리다이렉트 — 301 영구 · 302 임시 · 304 Not Modified(캐시 유효)
- 4xx 클라이언트 — 400 잘못된 요청 · 401 미인증 · 403 권한 없음 · 404 없음 · 429 과다 요청
- 5xx 서버 — 500 내부 오류 · 502 게이트웨이 오류 · 503 일시 불가 · 504 게이트웨이 타임아웃
401과 403의 구분이 자주 틀린다 — 401은 "누구인지 모른다", 403은 "누구인지는 아는데 권한이 없다"다.
무상태 — 그래서 쿠키
HTTP 는 각 요청이 서로를 모른다
- 어느 서버가 받아도 되므로 확장에 유리하다
- 대신 로그인 같은 연속성은 따로 얹어야 한다
Set-Cookie 로 식별자를 심으면 브라우저가 이후 요청마다 자동으로 실어 보낸다
- 이 '자동 전송' 성질이 CSRF 공격이 노리는 지점이다
버전 진화 = HoL 블로킹과의 싸움
-
HTTP/1.1 — 한 연결에서 요청을 한 줄로 처리한다
- 앞 요청이 느리면 뒤가 다 막힌다 (응용 계층 HoL)
- 브라우저는 연결을 6개쯤 열어 우회했다
-
HTTP/2 — 바이너리 프레이밍 + 한 연결에 여러 스트림 멀티플렉싱
- 응용 계층 HoL 해소. 헤더 압축(HPACK)도 추가
- 그러나 TCP 가 한 패킷을 잃으면 모든 스트림이 함께 막힌다 (전송 계층 HoL)
-
HTTP/3 — 전송을 QUIC(UDP 위)으로 바꿨다
- 스트림이 진짜 독립적이라 한 스트림의 손실이 다른 걸 안 막는다
- TLS 1.3 통합으로 연결 수립이 빠르다 (0-RTT)
서버가 먼저 말하려면
| 폴링 | 주기적으로 되묻는다 | 트래픽↔지연 맞바꿈 |
|---|---|---|
| 롱 폴링 | 응답을 붙잡고 기다린다 | 이벤트마다 연결이 끝난다 |
| SSE | 응답을 끝내지 않는다 | 단방향 · 자동 재연결이 규격에 있다 |
| WebSocket | 101 로 프로토콜을 바꾼다 | 양방향 · 재연결은 직접 구현 |
캐시 헤더
Cache-Control: max-age=3600, public 얼마나 캐시할까
- ETag / If-None-Match — 안 바뀌었으면 304 로 본문을 생략 Last-Modified / If-Modified-Since 시각 기반 조건부 요청
면접 함정
- ❌ "HTTP/2면 연결을 여러 개 열 필요가 없어 항상 빠르다" → 손실이 있는 회선에서는 TCP HoL 때문에 오히려 1.1의 다중 연결이 나을 수 있다.
- ❌ "POST는 캐시되지 않는다" → 조건이 맞으면 캐시할 수 있다. 실무에서 드물 뿐이다.
헤더로 성능을 조율한다
curl -I -H "Accept-Encoding: gzip, br" https://example.com
# content-encoding: br 압축
# cache-control: max-age=31536000 캐시 기간
# etag: "a1b2c3" 조건부 요청용 지문
# vary: Accept-Encoding 이 헤더가 다르면 다른 캐시 항목
Vary를 빠뜨리면 압축된 응답을 압축을 못 받는 클라이언트에게 주는 사고가 난다.
조건부 요청
-
클라이언트 — If-None-Match: "a1b2c3"
-
서버 — 304 Not Modified (본문 없음)
-
본문 전송이 통째로 생략된다. 대역폭을 크게 아낀다
버전 확인
curl -sI --http2 https://example.com | head -1 # HTTP/2 200
curl -sI --http3 https://example.com | head -1 # HTTP/3 200 (지원 시)
실무에서 자주 만나는 상태 코드
- 502 Bad Gateway — 프록시가 업스트림에서 잘못된 응답을 받았다 (앱이 죽었다)
- 503 Service Unavailable 일시적 과부하·점검 (Retry-After 를 함께 준다)
- 504 Gateway Timeout — 업스트림이 제때 응답하지 않았다 (앱이 느리다)
502 와 504 의 구분이 장애 원인을 가른다 — 죽었나 느린가