HTTP 기본 구조
HTTP 요청·응답 메시지와 메서드의 의미·안전성·멱등성을 익히고 HTTP 버전별 전송 방식의 차이를 이해합니다.
DNS가 도메인 이름을 IP 주소로 변환해 주었으니, 이제 실제로 서버와 데이터를 주고받을 차례입니다.
웹에서 이 통신을 담당하는 프로토콜이 HTTP(Hypertext Transfer Protocol)입니다.
웹 개발자가 매일 사용하는 프로토콜이지만, GET과 POST의 차이가 뭔가요?를 넘어서 HTTP의 구조를 정확히 이해하면, API 설계, 성능 최적화, 보안 설정에서 훨씬 나은 판단을 내릴 수 있습니다.
요청과 응답 포맷
HTTP는 요청-응답(Request-Response) 패턴으로 동작합니다.
클라이언트가 요청을 보내면 서버가 응답을 돌려보냅니다.
POST /api/users HTTP/1.1
Host: example.com
Content-Type: application/json
Content-Length: 2
{}HTTP/1.1 201 Created
Location: /api/users/1
Content-Type: application/json
Content-Length: 2
{}위 메시지는 빈 JSON 객체 2바이트를 사용하는 형식 예시입니다. 실행 응답이 아니며, 줄 끝은 실제 HTTP/1.1 전송에서 CRLF를 사용합니다.
HTTP/1.1의 시작 줄과 헤더는 텍스트 형식이고, 본문에는 이미지 같은 바이너리도 올 수 있습니다. 시작 줄 뒤의 헤더 목록은 빈 줄로 끝납니다.
다만 HTTPS라면 TLS로 암호화되므로 패킷 캡처에서 본문을 그대로 읽을 수 없습니다.
HTTP/2와 HTTP/3는 바이너리 프레이밍을 사용하지만, 메서드·URI·상태 코드·헤더·본문이라는 논리적 의미는 HTTP Semantics로 유지됩니다.
HTTP 메서드
HTTP 메서드는 클라이언트가 서버에 어떤 동작을 원하는지 표현합니다.
메서드의 의도와 안전성·멱등성의 비교 기준과 해석 범위입니다.
| 메서드 | 요청 의미와 본문 | 안전 / 멱등 |
|---|---|---|
| GET | 조회; 페이지 로드, API 데이터 조회. 보통 없음 | 예 / 예 |
| POST | 처리/생성; 회원가입, 게시글 작성, 파일 업로드. 있음 | 아니오 / 아니오 |
| PUT | 전체 대체; 프로필 전체 수정. 있음 | 아니오 / 예 |
| PATCH | 부분 수정; 이메일만 변경. 있음 | 아니오 / 설계에 따라 |
| DELETE | 삭제; 계정 삭제, 게시글 삭제. 보통 없음 | 아니오 / 예 |
| OPTIONS | 메서드 확인; CORS 프리플라이트. 보통 없음 | 예 / 예 |
| HEAD | 헤더만 조회; 리소스 존재 확인, 크기 확인. 응답 본문 없음 | 예 / 예 |
- GET
- 요청 의미와 본문: 조회; 페이지 로드, API 데이터 조회. 보통 없음안전 / 멱등: 예 / 예
- POST
- 요청 의미와 본문: 처리/생성; 회원가입, 게시글 작성, 파일 업로드. 있음안전 / 멱등: 아니오 / 아니오
- PUT
- 요청 의미와 본문: 전체 대체; 프로필 전체 수정. 있음안전 / 멱등: 아니오 / 예
- PATCH
- 요청 의미와 본문: 부분 수정; 이메일만 변경. 있음안전 / 멱등: 아니오 / 설계에 따라
- DELETE
- 요청 의미와 본문: 삭제; 계정 삭제, 게시글 삭제. 보통 없음안전 / 멱등: 아니오 / 예
- OPTIONS
- 요청 의미와 본문: 메서드 확인; CORS 프리플라이트. 보통 없음안전 / 멱등: 예 / 예
- HEAD
- 요청 의미와 본문: 헤더만 조회; 리소스 존재 확인, 크기 확인. 응답 본문 없음안전 / 멱등: 예 / 예
예/아니오는 메서드가 정의한 의도에 대한 분류입니다. GET의 부수적 로깅이나 반복 요청의 응답 코드 차이까지 없다는 뜻은 아닙니다.
GET /search?q=network&page=1 HTTP/1.1
* 데이터를 URL 쿼리스트링에 포함
* 브라우저 히스토리에 남음
* 북마크 가능
* 실무상 브라우저/서버/프록시별 URL 길이 제한에 영향
* 캐시 가능
* 뒤로 가기 시 캐시·이력 복원 정책에 따라 재요청할 수 있음
POST /api/users HTTP/1.1
Content-Type: application/json
{"name": "홍길동"}
* 데이터를 본문(Body)에 포함
* URL과 달리 POST 본문은 주소 표시줄에 나타나지 않음
* 북마크 불가
* 본문 크기는 서버·프록시·애플리케이션의 제한을 받음
* 기본적으로 캐시 활용이 어렵지만, 명시적 캐시 조건을 둘 수 있음
* 뒤로 가기에서 재전송이 필요하면 재제출 확인이 나타날 수 있음GET 요청에도 메시지 본문 자체를 붙이는 것이 문법적으로 완전히 불가능한 것은 아니지만, 표준 의미가 일반적으로 정의되어 있지 않아 실무에서는 쓰지 않는 편이 안전합니다.
멱등성과 안전성
HTTP 메서드를 분류하는 두 가지 중요한 속성이 있습니다.
안전성(Safety): 클라이언트가 요청한 의미가 서버 상태 변경을 요구하지 않는 메서드입니다.
GET, HEAD, OPTIONS가 안전합니다.
서버가 로그를 남기거나 통계를 갱신하는 부수 효과까지 모두 금지한다는 뜻은 아닙니다.
멱등성(Idempotency): 같은 요청을 한 번 보내든 여러 번 보내든 서버에 남는 의도한 효과가 동일한 메서드입니다.
응답 코드나 응답 본문이 매번 완전히 같아야 한다는 뜻은 아닙니다.
import json
from http.client import HTTPSConnection
def http_request(method, host, path, body=None):
"""다양한 HTTP 메서드로 요청 전송"""
conn = HTTPSConnection(host)
headers = {
"Content-Type": "application/json",
"Accept": "application/json",
}
json_body = json.dumps(body) if body else None
conn.request(method, path, body=json_body, headers=headers)
response = conn.getresponse()
data = response.read().decode()
print(f"{method} {path}")
print(f" Status: {response.status} {response.reason}")
print(f" Content-Type: {response.getheader('Content-Type')}")
if data and len(data) < 200:
print(f" Body: {data}")
print()
conn.close()
return response.status, data
# 사용 예시 (httpbin.org는 HTTP 테스트 서비스)
# http_request("GET", "httpbin.org", "/get")
# http_request("POST", "httpbin.org", "/post", {"name": "test"})
# http_request("PUT", "httpbin.org", "/put", {"name": "updated"})
# http_request("DELETE", "httpbin.org", "/delete")HTTP 버전별 차이
| 버전 | 연도와 전송 특성 |
|---|---|
| HTTP/1.0 | 연도: 1996; 연결 방식: 기본적으로 요청마다 새 연결; 특징: 연결 재사용 확장도 사용됨 |
| HTTP/1.1 | 연도: 1997; 연결 방식: Keep-Alive (연결 재사용); 특징: 파이프라이닝 지원, 실무 채택 제한 |
| HTTP/2 | 연도: 2015; 연결 방식: 멀티플렉싱 (1연결, 병렬 전송); 특징: 바이너리, 헤더 압축, 서버 푸시(효용 제한) |
| HTTP/3 | 연도: 2022; 연결 방식: QUIC (UDP 기반); 특징: 스트림 독립성, TLS 1.3, 0-RTT |
HTTP/3는 TCP 연결 단위의 HOL Blocking을 QUIC 스트림 구조로 크게 완화합니다.
다만 같은 스트림 내부에서는 순서 있는 전달이 필요하므로, 손실의 영향이 완전히 사라지는 것은 아닙니다.
다음 절에서는 서버가 응답할 때 사용하는 상태 코드와 요청/응답에 포함되는 헤더를 살펴보겠습니다.