브라우저에 URL을 입력하고 Enter를 누르는 순간, 수십 개의 HTTP 요청이 오가며 페이지가 만들어집니다. HTTP(HyperText Transfer Protocol)는 웹의 기반이 되는 통신 규약입니다. 이를 제대로 이해하면 API 설계, 성능 최적화, 보안까지 폭넓게 활용할 수 있습니다.

HTTP 요청 구조

GET /api/posts/1 HTTP/1.1
Host: api.devlog.kr
Accept: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)

요청은 메서드 + 경로 + 버전으로 시작하고, 그 아래 헤더들이 이어집니다. POST/PUT은 헤더 아래 빈 줄 다음에 본문(body)이 옵니다.

HTTP 메서드

// GET — 리소스 조회 (멱등, 캐시 가능)
fetch('/api/posts')

// POST — 리소스 생성 (멱등 아님)
fetch('/api/posts', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ title: '새 글', content: '...' })
})

// PUT — 리소스 전체 교체 (멱등)
fetch('/api/posts/1', {
  method: 'PUT',
  body: JSON.stringify({ title: '수정된 글', content: '...' })
})

// PATCH — 리소스 일부 수정 (멱등)
fetch('/api/posts/1', {
  method: 'PATCH',
  body: JSON.stringify({ title: '제목만 수정' })
})

// DELETE — 리소스 삭제 (멱등)
fetch('/api/posts/1', { method: 'DELETE' })

HTTP 상태 코드

// 2xx — 성공
200 OK           // 요청 성공
201 Created      // 리소스 생성 성공 (POST 응답)
204 No Content   // 성공, 응답 본문 없음 (DELETE 응답)

// 3xx — 리다이렉트
301 Moved Permanently  // 영구 이동 (SEO에 영향)
302 Found              // 임시 이동
304 Not Modified       // 캐시 사용 가능

// 4xx — 클라이언트 오류
400 Bad Request        // 잘못된 요청 (유효성 오류)
401 Unauthorized       // 인증 필요 (로그인 안 함)
403 Forbidden          // 권한 없음 (로그인은 했으나 접근 불가)
404 Not Found          // 리소스 없음
409 Conflict           // 리소스 충돌 (중복 이메일 등)
422 Unprocessable      // 유효성 검사 실패
429 Too Many Requests  // 요청 횟수 초과 (Rate Limit)

// 5xx — 서버 오류
500 Internal Server Error  // 서버 내부 오류
502 Bad Gateway            // 게이트웨이 오류
503 Service Unavailable    // 서버 과부하 / 점검 중

주요 HTTP 헤더

--- 요청 헤더 ---
Content-Type: application/json        # 요청 본문 형식
Accept: application/json, text/html   # 받고 싶은 형식
Authorization: Bearer          # 인증 토큰
Cache-Control: no-cache               # 캐시 정책
Cookie: session=abc123                # 쿠키 전송

--- 응답 헤더 ---
Content-Type: application/json; charset=utf-8
Cache-Control: max-age=3600, public   # 1시간 캐시
ETag: "abc123"                        # 리소스 버전 식별자
Set-Cookie: session=xyz; HttpOnly; Secure; SameSite=Strict
Access-Control-Allow-Origin: *        # CORS 허용
X-RateLimit-Remaining: 95            # 남은 요청 횟수

HTTPS와 TLS 암호화

HTTPS는 HTTP에 TLS(Transport Layer Security) 암호화를 더한 것입니다. 데이터가 암호화되어 전송되므로 중간자 공격(MITM)을 방어합니다.

TLS 핸드셰이크 과정:
1. 클라이언트 → 서버: "TLS 1.3 지원해요, 이런 암호화 방식 씁니다"
2. 서버 → 클라이언트: "인증서 여기요, 이 방식 쓰죠"
3. 클라이언트: 인증서 검증 (CA 서명 확인)
4. 키 교환 (Diffie-Hellman) → 세션 키 생성
5. 이후 모든 통신은 세션 키로 대칭 암호화

CORS — 교차 출처 리소스 공유

// 브라우저는 다른 출처의 API 요청을 기본 차단
// Origin: http://localhost:3000 → API: http://api.devlog.kr
// → CORS 오류 발생

// 서버에서 허용 헤더 추가 (Express 예시)
const cors = require('cors');
app.use(cors({
  origin: ['https://devlog.kr', 'http://localhost:3000'],
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  credentials: true  // 쿠키 포함 요청 허용
}));

// Preflight 요청 (OPTIONS)
// 브라우저가 실제 요청 전에 서버에 "이 요청 해도 돼?" 확인
// 서버가 Access-Control-Allow-* 헤더로 응답해야 함

HTTP/2와 HTTP/3

HTTP/1.1 (1997)
- 요청당 하나의 TCP 연결
- 헤더 압축 없음
- 텍스트 기반 프로토콜

HTTP/2 (2015)
- 하나의 연결로 여러 요청 병렬 처리 (멀티플렉싱)
- 헤더 압축 (HPACK)
- 서버 푸시 가능
- 바이너리 프로토콜

HTTP/3 (2022)
- UDP 기반 QUIC 프로토콜 사용
- 연결 설정 시간 단축 (0-RTT)
- 패킷 손실 시 다른 스트림 영향 없음

캐싱 전략

--- 자주 바뀌지 않는 정적 파일 ---
Cache-Control: public, max-age=31536000, immutable
# 1년 캐시, 내용 변경 없음 → 파일명에 해시 포함 (app.abc123.js)

--- HTML 파일 ---
Cache-Control: no-cache
# 매번 서버에 확인, 변경 없으면 304 반환

--- API 응답 ---
Cache-Control: private, max-age=300
# 브라우저만 5분 캐시

--- ETag 활용 ---
# 서버: ETag: "version-123"
# 클라이언트 재요청: If-None-Match: "version-123"
# 변경 없으면: 304 Not Modified (본문 없이)
핵심 정리
HTTP는 요청-응답 구조의 무상태 프로토콜입니다. 올바른 메서드(GET/POST/PUT/PATCH/DELETE)와 상태 코드(2xx/4xx/5xx)를 사용하고, HTTPS로 통신을 암호화하세요. 캐싱 전략으로 불필요한 요청을 줄이면 성능이 크게 향상됩니다.