브라우저에 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로 통신을 암호화하세요. 캐싱 전략으로 불필요한 요청을 줄이면 성능이 크게 향상됩니다.

보안 헤더 — 필수 설정 목록

HTTPS만으로는 부족합니다. 다양한 웹 공격을 방어하기 위해 보안 헤더를 반드시 설정해야 합니다.

Strict-Transport-Security: max-age=31536000; includeSubDomains
# HSTS: 브라우저가 항상 HTTPS를 사용하도록 강제. 1년간 유지

Content-Security-Policy: default-src 'self'; script-src 'self' https://cdnjs.com
# CSP: XSS 방어. 허가된 출처에서만 리소스 로드 허용

X-Frame-Options: DENY
# Clickjacking 방어. iframe 삽입 차단

X-Content-Type-Options: nosniff
# MIME 타입 스니핑 방어

Referrer-Policy: strict-origin-when-cross-origin
# 외부 사이트로 Referrer 헤더 전송 제한

Permissions-Policy: camera=(), microphone=(), geolocation=()
# 브라우저 API 사용 권한 제한

Node.js/Express에서는 helmet 라이브러리 하나로 주요 보안 헤더를 한꺼번에 설정할 수 있습니다. app.use(helmet())를 추가하면 됩니다.

WebSocket — 실시간 양방향 통신

HTTP는 클라이언트가 요청해야만 서버가 응답할 수 있습니다. 실시간 채팅, 주식 시세, 게임처럼 서버가 먼저 데이터를 밀어줘야 하는 경우 WebSocket을 사용합니다.

// 클라이언트
const ws = new WebSocket('wss://api.example.com/chat');

ws.onopen = () => ws.send(JSON.stringify({ type: 'join', room: 'general' }));

ws.onmessage = (event) => {
  const data = JSON.parse(event.data);
  appendMessage(data.user, data.text);
};

ws.onclose = () => console.log('연결 종료');

// Node.js 서버 (ws 라이브러리)
import { WebSocketServer } from 'ws';

const wss = new WebSocketServer({ port: 8080 });

wss.on('connection', (ws) => {
  ws.on('message', (data) => {
    // 모든 클라이언트에게 브로드캐스트
    wss.clients.forEach((client) => {
      if (client.readyState === WebSocket.OPEN) {
        client.send(data.toString());
      }
    });
  });
});

HTTP/3와 QUIC — 차세대 프로토콜

HTTP/3는 TCP 대신 QUIC(UDP 기반) 프로토콜을 사용합니다. 패킷 손실 시 다른 스트림에 영향을 주지 않아 불안정한 모바일 환경에서 특히 유리합니다.

  • 연결 설정 — 0-RTT 또는 1-RTT로 기존 HTTP/2의 1~2 RTT보다 빠름
  • HOL Blocking 해소 — TCP의 Head-of-Line blocking 문제가 없어 패킷 손실 시 다른 요청에 영향 없음
  • Connection Migration — Wi-Fi에서 LTE로 전환되어도 연결이 유지됨

2026년 기준 Google, Cloudflare, Nginx, Caddy 등 주요 서버·CDN에서 HTTP/3를 지원합니다. 모바일 사용자가 많은 서비스라면 HTTP/3 활성화를 검토해보세요.

HTTPS 인증서 관리와 자동화

HTTPS를 운영하려면 SSL/TLS 인증서가 필요하며, 인증서 관리는 보안 운영의 중요한 부분입니다. Let's Encrypt는 무료로 인증서를 발급해주는 CA(인증 기관)로, Certbot 도구를 사용하면 발급과 갱신을 완전히 자동화할 수 있습니다. 인증서는 보통 90일 유효기간을 가지며, Certbot은 만료 30일 전부터 자동 갱신을 시도합니다. cron 작업으로 certbot renew를 주기적으로 실행하도록 설정해두면 인증서 만료로 인한 사이트 접속 오류를 예방할 수 있습니다. 클라우드 환경에서는 AWS Certificate Manager(ACM)이나 Google-managed SSL 인증서를 사용하면 갱신까지 완전 자동화가 가능합니다.

인증서 종류 선택도 서비스 성격에 따라 다릅니다. 도메인 유효성만 검증하는 DV(Domain Validation) 인증서는 개인 블로그나 소규모 서비스에 적합하고, Let's Encrypt가 제공하는 것이 바로 이 유형입니다. 기업이나 이커머스 사이트라면 조직 실체를 검증하는 OV(Organization Validation) 또는 EV(Extended Validation) 인증서가 신뢰도 측면에서 더 유리할 수 있습니다. 와일드카드 인증서(*.yourdomain.com)는 여러 서브도메인을 한 인증서로 커버할 수 있어 서브도메인이 많은 서비스에서 관리 편의성이 높습니다. 인증서 교체는 서비스 중단 없이 진행할 수 있도록 미리 새 인증서를 발급해두고 만료 직전에 교체하는 방식이 안전합니다.