Skip to main content

HTTP 기초

HTTP(HyperText Transfer Protocol)는 웹에서 클라이언트와 서버가 데이터를 주고받기 위한 애플리케이션 계층 프로토콜입니다. 브라우저가 웹페이지를 요청하는 것부터, AI 모델이 API를 호출하는 것까지 — 거의 모든 웹 통신은 HTTP 위에서 이루어집니다. LLM API를 호출하든, ML 모델을 서빙하든, 데이터 파이프라인을 구축하든, HTTP의 동작 원리를 이해하는 것은 필수입니다.

학습 목표

  • HTTP 메서드의 의미와 멱등성/안전성 개념을 이해한다
  • 상태코드 그룹별 의미를 파악하고 적절한 응답을 설계할 수 있다
  • 요청-응답 구조와 주요 헤더의 역할을 설명할 수 있다
  • HTTPS, 쿠키, 캐싱, 타임아웃 등 실무에서 마주치는 HTTP 개념을 활용할 수 있다

왜 중요한가

AI/ML 워크플로에서 HTTP는 모든 서비스 간 통신의 기반입니다. OpenAI API를 호출할 때, HuggingFace에서 모델을 다운로드할 때, MLflow에 실험 결과를 기록할 때 — 이 모든 것이 HTTP 요청과 응답으로 이루어집니다. 상태코드를 이해하지 못하면 429 Too Many Requests를 받았을 때 왜 API 호출이 실패했는지 알 수 없고, 헤더를 모르면 Authorization: Bearer가 왜 필요한지 이해할 수 없습니다. HTTP는 웹 세계의 공통 언어이며, 이 언어를 읽고 쓸 수 있어야 LLMOps까지 나아갈 수 있습니다.

HTTP 요청-응답 구조

HTTP 메시지는 시작줄(Start Line), 헤더(Headers), 빈 줄, 본문(Body) 네 부분으로 구성됩니다.

요청 메시지 예시

POST /v1/chat/completions HTTP/1.1
Host: api.openai.com
Content-Type: application/json
Authorization: Bearer sk-proj-abc123...

{
  "model": "gpt-4o",
  "messages": [{"role": "user", "content": "Hello!"}]
}

응답 메시지 예시

HTTP/1.1 200 OK
Content-Type: application/json
X-Request-Id: req_abc123
X-RateLimit-Remaining: 59

{
  "id": "chatcmpl-abc",
  "object": "chat.completion",
  "choices": [{"message": {"role": "assistant", "content": "안녕하세요!"}}]
}
시작줄은 요청에서는 메서드 경로 프로토콜, 응답에서는 프로토콜 상태코드 상태메시지 형태입니다. 헤더와 본문 사이의 빈 줄은 반드시 존재해야 합니다.

HTTP 메서드 상세

메서드용도안전성멱등성요청 본문응답 본문
GET리소스 조회OOXO
POST리소스 생성 / 처리 요청XXOO
PUT리소스 전체 교체XOOO
PATCH리소스 부분 수정X△ (구현에 따라 다름)OO
DELETE리소스 삭제XO선택선택
HEAD헤더만 조회 (GET과 동일, 본문 없음)OOXX
OPTIONS지원 메서드 확인 (CORS preflight)OOXO
멱등성(Idempotency): 같은 요청을 여러 번 보내도 결과가 동일한 성질입니다. GET, PUT, DELETE는 멱등이고, POST는 멱등이 아닙니다 — 같은 POST를 두 번 보내면 리소스가 두 개 생길 수 있습니다. 안전성(Safety): 서버 상태를 변경하지 않는 성질입니다. GETHEAD만 안전합니다.

상태코드 그룹별 상세

1xx — 정보(Informational)

서버가 요청을 수신했고 처리 중임을 알립니다. 실무에서 직접 마주칠 일은 드뭅니다.
코드의미설명
100Continue클라이언트가 본문을 보내도 된다는 신호
101Switching ProtocolsWebSocket 업그레이드 시 사용

2xx — 성공(Success)

코드의미사용 상황
200OKGET 성공, 일반적인 성공 응답
201CreatedPOST로 리소스 생성 완료
202Accepted비동기 작업 접수 완료 (아직 처리 중)
204No Content삭제 성공 등 본문 없는 성공

3xx — 리다이렉션(Redirection)

코드의미사용 상황
301Moved PermanentlyURL이 영구적으로 변경됨
302Found임시 리다이렉션
304Not Modified캐시된 리소스가 변경되지 않음

4xx — 클라이언트 오류(Client Error)

코드의미사용 상황
400Bad Request잘못된 요청 형식, 유효성 검사 실패
401Unauthorized인증 필요 (토큰 없음/만료)
403Forbidden인증은 됐지만 권한 없음
404Not Found리소스가 존재하지 않음
405Method Not Allowed해당 경로에서 지원하지 않는 메서드
409Conflict리소스 충돌 (동시 수정 등)
422Unprocessable Entity형식은 맞지만 의미적으로 처리 불가
429Too Many RequestsRate Limit 초과

5xx — 서버 오류(Server Error)

코드의미사용 상황
500Internal Server Error서버 내부 예외 발생
502Bad Gateway프록시/게이트웨이가 업스트림에서 잘못된 응답 수신
503Service Unavailable서비스 일시 중단 (배포, 과부하)
504Gateway Timeout업스트림 서버 응답 시간 초과
LLM API에서 429는 매우 자주 만나는 상태코드입니다. Retry-After 헤더를 확인하고 지수 백오프를 적용해야 합니다. 503은 모델 서빙 서버가 아직 로딩 중일 때 흔히 발생합니다.

주요 HTTP 헤더

요청 헤더

헤더용도예시
Accept원하는 응답 형식application/json
Content-Type요청 본문 형식application/json
Authorization인증 정보Bearer sk-proj-abc123...
User-Agent클라이언트 식별python-requests/2.31.0

응답 헤더

헤더용도예시
Content-Type응답 본문 형식application/json; charset=utf-8
Cache-Control캐시 정책max-age=3600, public
X-Request-Id요청 추적 IDreq_abc123def456

CORS 헤더

헤더용도
Access-Control-Allow-Origin허용 도메인 (* 또는 특정 도메인)
Access-Control-Allow-Methods허용 HTTP 메서드
Access-Control-Allow-Headers허용 요청 헤더
Access-Control-Max-Agepreflight 결과 캐시 시간(초)

Rate Limit 헤더

헤더용도
X-RateLimit-Limit허용된 총 요청 수
X-RateLimit-Remaining남은 요청 수
X-RateLimit-Reset제한이 초기화되는 시각(Unix timestamp)
Retry-After재시도까지 대기 시간(초)

HTTPS와 TLS

HTTPS는 HTTP에 TLS(Transport Layer Security) 암호화를 추가한 것입니다. 평문으로 전송되는 HTTP와 달리, HTTPS는 데이터를 암호화하여 도청, 변조, 위장을 방지합니다.
1

클라이언트 Hello

클라이언트가 지원하는 TLS 버전, 암호 스위트 목록, 랜덤 값을 서버에 전송합니다.
2

서버 Hello + 인증서 전송

서버가 선택한 암호 스위트와 자신의 TLS 인증서(공개키 포함)를 클라이언트에 전송합니다.
3

인증서 검증

클라이언트가 인증서의 유효성을 확인합니다 — 발급 CA(Certificate Authority)의 서명, 만료일, 도메인 일치 여부를 검증합니다.
4

키 교환

클라이언트와 서버가 공유 비밀(session key)을 안전하게 교환합니다. 이후 이 대칭키로 데이터를 암호화합니다.
5

암호화 통신 시작

TLS 핸드셰이크가 완료되면, 이후 모든 HTTP 메시지는 협상된 대칭키로 암호화되어 전송됩니다.
인증서 체인: 서버 인증서 → 중간 CA 인증서 → 루트 CA 인증서 순으로 신뢰를 검증합니다. Let’s Encrypt: 무료 TLS 인증서를 자동 발급/갱신해주는 비영리 CA로, 대부분의 웹 서비스에서 사용합니다.

쿠키와 캐싱

서버가 Set-Cookie 헤더로 클라이언트에 데이터를 저장하고, 이후 요청마다 자동으로 전송됩니다.
Set-Cookie: session_id=abc123; HttpOnly; Secure; SameSite=Strict; Max-Age=3600
속성의미
HttpOnlyJavaScript에서 접근 불가 (XSS 방어)
SecureHTTPS에서만 전송
SameSite크로스사이트 요청 시 전송 제한 (CSRF 방어)
Max-Age쿠키 유효 시간(초)

HTTP 캐싱

# 응답 헤더 - 캐시 제어
Cache-Control: max-age=3600, public
ETag: "abc123hash"

# 조건부 요청 헤더
If-None-Match: "abc123hash"
캐시된 리소스가 변경되지 않았으면 서버는 304 Not Modified를 본문 없이 반환합니다. 이를 통해 대역폭을 절약하고 응답 속도를 높일 수 있습니다.

타임아웃, 재시도, 백오프

타임아웃 종류

종류설명권장값
연결 타임아웃(Connection Timeout)TCP 연결 수립까지 대기 시간5~10초
읽기 타임아웃(Read Timeout)응답 데이터 수신까지 대기 시간30~120초 (LLM API는 더 길게)

지수 백오프(Exponential Backoff)

재시도 간격을 지수적으로 늘려 서버 부하를 완화합니다.
import time
import random
import requests

def request_with_backoff(url, max_retries=5):
    for attempt in range(max_retries):
        response = requests.get(url, timeout=(5, 30))

        if response.status_code == 200:
            return response.json()

        if response.status_code == 429:  # Rate Limited
            retry_after = int(response.headers.get("Retry-After", 0))
            wait = max(retry_after, (2 ** attempt) + random.uniform(0, 1))
            print(f"Rate limited. Retry in {wait:.1f}s (attempt {attempt + 1})")
            time.sleep(wait)
        elif response.status_code >= 500:  # 서버 오류
            wait = (2 ** attempt) + random.uniform(0, 1)
            print(f"Server error {response.status_code}. Retry in {wait:.1f}s")
            time.sleep(wait)
        else:
            response.raise_for_status()  # 4xx는 즉시 실패

    raise Exception(f"Max retries ({max_retries}) exceeded")
지수 백오프 공식: wait = (2^attempt) + random_jitter 지터(jitter)를 추가하는 이유는 다수의 클라이언트가 동시에 재시도하는 thundering herd 문제를 방지하기 위함입니다.

curl 기초

curl은 터미널에서 HTTP 요청을 보내는 가장 기본적인 도구입니다.
# GET 요청
curl https://api.example.com/users

# 헤더만 확인 (-I)
curl -I https://api.example.com/users

# POST - JSON 전송
curl -X POST https://api.example.com/users \
  -H "Content-Type: application/json" \
  -d '{"name": "홍길동", "email": "hong@example.com"}'

# 인증 헤더 포함
curl https://api.openai.com/v1/models \
  -H "Authorization: Bearer $OPENAI_API_KEY"

# PUT으로 리소스 교체
curl -X PUT https://api.example.com/users/1 \
  -H "Content-Type: application/json" \
  -d '{"name": "김철수", "email": "kim@example.com"}'

# DELETE
curl -X DELETE https://api.example.com/users/1

# 응답을 jq로 가독성 있게 출력
curl -s https://api.example.com/users | jq '.data[] | {name, email}'

# 상세 통신 과정 확인 (-v)
curl -v https://api.example.com/health
-s(silent)와 jq를 조합하면 JSON 응답을 깔끔하게 파싱할 수 있습니다. -v(verbose)는 요청/응답 헤더, TLS 핸드셰이크까지 모두 보여주어 디버깅에 매우 유용합니다.

AI/ML에서 HTTP가 중요한 이유

상황HTTP 개념
OpenAI API 호출POST, Authorization 헤더, JSON 본문, 429 처리
모델 서빙 (FastAPI/vLLM)GET/POST 엔드포인트, 상태코드 설계, 타임아웃
모델 다운로드 (HuggingFace)GET, Content-Length, 304 캐싱, Range 요청
학습 파이프라인 (MLflow)REST API, POST로 메트릭 기록, 인증
스트리밍 응답 (SSE)Transfer-Encoding: chunked, text/event-stream
CI/CD 웹훅POST 콜백, 서명 검증, 상태코드 응답
HTTP/1.1: 텍스트 기반. 파이프라이닝으로 여러 요청을 보낼 수 있지만, 응답은 순서대로 처리되어야 하므로 앞선 응답이 지연되면 뒤의 응답도 대기합니다(Head-of-Line Blocking). HTTP/2: 바이너리 프레이밍, 하나의 TCP 연결에서 여러 스트림을 멀티플렉싱. 헤더 압축(HPACK). HTTP/3: TCP 대신 QUIC(UDP 기반) 사용. 연결 설정이 빠르고, 패킷 손실 시에도 다른 스트림에 영향 없음. 대부분의 API 서버는 HTTP/1.1 또는 HTTP/2를 사용하며, HTTP/3은 CDN과 브라우저에서 점진적으로 도입 중입니다.
네트워크가 불안정하면 클라이언트는 요청이 성공했는지 확인할 수 없어 재시도를 하게 됩니다. PUTDELETE는 멱등이므로 재시도해도 안전합니다. 하지만 POST는 멱등이 아니라 중복 생성이 발생할 수 있습니다. 이를 방지하기 위해 Idempotency-Key 헤더를 사용하는 패턴이 있습니다 (Stripe API가 대표적).
브라우저는 보안을 위해 **동일 출처 정책(Same-Origin Policy)**을 적용합니다. https://frontend.com에서 https://api.backend.com으로 요청하면 출처가 다르므로 CORS 오류가 발생합니다. 서버가 Access-Control-Allow-Origin 헤더를 올바르게 설정해야 합니다. 단, curl이나 Python requests에서는 CORS가 적용되지 않습니다 — 이는 브라우저 전용 보안 메커니즘입니다.
Content-Type보내는 데이터의 형식을 명시합니다 (예: 요청 본문이 JSON임을 알림). Accept받고 싶은 데이터의 형식을 서버에 요청합니다 (예: JSON으로 응답해달라는 요청). LLM API에서는 대부분 Content-Type: application/jsonAccept: application/json을 함께 사용합니다. 스트리밍 응답을 원할 때는 Accept: text/event-stream을 사용하기도 합니다.
LLM은 토큰을 하나씩 생성하므로 응답 시간이 수십 초에 달할 수 있습니다. 일반적인 REST API의 읽기 타임아웃(5~10초)으로는 부족합니다. OpenAI SDK는 기본 읽기 타임아웃이 600초(10분)이며, 스트리밍 모드에서는 첫 토큰까지의 시간(TTFT)과 전체 응답 완료 시간을 별도로 관리해야 합니다.

체크리스트

  • HTTP 요청 메시지의 4개 구성요소(시작줄, 헤더, 빈 줄, 본문)를 설명할 수 있다
  • GET, POST, PUT, PATCH, DELETE의 용도와 멱등성/안전성 차이를 이해한다
  • 200, 201, 204, 301, 400, 401, 403, 404, 429, 500, 503 상태코드의 의미를 안다
  • Authorization, Content-Type, Cache-Control 헤더의 역할을 설명할 수 있다
  • HTTPS와 TLS 핸드셰이크의 기본 과정을 이해한다
  • 지수 백오프와 Retry-After를 활용한 재시도 전략을 구현할 수 있다
  • curl로 GET/POST 요청을 보내고 응답 헤더를 확인할 수 있다
  • Rate Limit 헤더(X-RateLimit-*)를 읽고 429 상황에 대응할 수 있다

다음 문서