Python Requests로 POST JSON 요청을 보내는 방법 (2026)
Python requests 라이브러리로 JSON 데이터를 전송하는 방법 완벽 가이드
Hypereal로 구축 시작하기
단일 API를 통해 Kling, Flux, Sora, Veo 등에 액세스하세요. 무료 크레딧으로 시작하고 수백만으로 확장하세요.
신용카드 불필요 • 10만 명 이상의 개발자 • 엔터프라이즈 지원
Python Requests로 POST JSON 보내는 방법 (2026)
Python requests 라이브러리는 Python 생태계에서 가장 인기 있는 HTTP 라이브러리로, JSON 데이터와 함께 POST 요청을 보내는 것은 가장 흔한 사용 사례 중 하나입니다. REST API를 호출하거나, webhook으로 데이터를 보내거나, 서드파티 서비스와 통합할 때 요청 구조를 올바르게 잡는 방법을 알아야 합니다.
이 가이드는 기초부터 인증, 에러 핸들링, 재시도(retries), 그리고 비동기 요청을 포함한 고급 패턴까지 모든 내용을 다룹니다.
기초: JSON POST 요청 보내기
방법 1: `json` 파라미터 사용 (권장)
JSON 데이터를 보내는 가장 간단하고 정확한 방법입니다.
import requests
url = "https://api.example.com/v1/users"
data = {
"name": "Jane Doe",
"email": "jane@example.com",
"role": "developer"
}
response = requests.post(url, json=data)
print(response.status_code) # 200
print(response.json()) # JSON 응답 파싱
json 파라미터를 사용하면 requests가 자동으로 다음을 수행합니다:
- 딕셔너리를 JSON 문자열로 직렬화(Serialization)
Content-Type헤더를application/json으로 설정- 데이터를 UTF-8로 인코딩
방법 2: 수동 직렬화와 함께 `data` 사용
직렬화 과정을 직접 제어해야 하는 경우 사용합니다.
import requests
import json
url = "https://api.example.com/v1/users"
data = {
"name": "Jane Doe",
"email": "jane@example.com"
}
response = requests.post(
url,
data=json.dumps(data),
headers={"Content-Type": "application/json"}
)
이 방법을 사용하는 경우: 커스텀 JSON 직렬화(예: 특정 날짜 형식 지정이나 Decimal 처리)가 필요한 경우에만 사용하세요. 그 외 모든 경우에는 json=data를 사용하십시오.
핵심 차이점: `json` vs `data`
| 파라미터 | 직렬화 | Content-Type | 사용 사례 |
|---|---|---|---|
json=data |
자동 (json.dumps) |
자동 설정됨 | 표준 JSON API |
data=json.dumps(data) |
수동 | 수동으로 설정해야 함 | 커스텀 직렬화 필요 시 |
data=data |
Form-encoded | application/x-www-form-urlencoded |
HTML 폼, 레거시 API |
흔히 하는 실수는 JSON API에 json.dumps 없이 data=data를 사용하는 것입니다. 이는 JSON이 아닌 폼 인코딩 데이터를 전송하므로 서버에서 요청을 거부할 가능성이 높습니다.
헤더 추가하기
표준 헤더
headers = {
"Content-Type": "application/json", # json= 파라미터 사용 시 자동 설정됨
"Accept": "application/json",
"User-Agent": "MyApp/1.0"
}
response = requests.post(url, json=data, headers=headers)
json=data를 사용할 때는 Content-Type을 직접 설정할 필요가 없으며 requests 라이브러리가 알아서 처리합니다. 하지만 Accept와 User-Agent를 설정하는 것은 좋은 관습입니다.
API Key 인증
headers = {
"X-API-Key": "your-api-key-here",
"Accept": "application/json"
}
response = requests.post(
"https://api.example.com/v1/generate",
json={"prompt": "A sunset over mountains", "model": "flux"},
headers=headers
)
Bearer Token 인증
headers = {
"Authorization": "Bearer eyJhbGciOiJIUzI1NiIs...",
"Accept": "application/json"
}
response = requests.post(url, json=data, headers=headers)
Basic Authentication
from requests.auth import HTTPBasicAuth
response = requests.post(
url,
json=data,
auth=HTTPBasicAuth("username", "password")
)
# 축약형
response = requests.post(url, json=data, auth=("username", "password"))
응답 처리하기
JSON 응답 파싱
response = requests.post(url, json=data)
# 요청 성공 여부 확인
if response.ok: # 상태 코드 200-299인 경우 True
result = response.json()
print(result)
else:
print(f"Error: {response.status_code}")
print(response.text)
전체 응답 검사
response = requests.post(url, json=data)
print(f"Status: {response.status_code}")
print(f"Headers: {dict(response.headers)}")
print(f"Content-Type: {response.headers.get('Content-Type')}")
print(f"Response time: {response.elapsed.total_seconds()}s")
print(f"Body: {response.text}")
# Content-Type이 JSON인 경우에만 파싱
if "application/json" in response.headers.get("Content-Type", ""):
print(f"JSON: {response.json()}")
상태 코드별 처리
response = requests.post(url, json=data)
match response.status_code:
case 200 | 201:
result = response.json()
print(f"Success: {result}")
case 400:
print(f"Bad request: {response.json().get('error', 'Unknown')}")
case 401:
print("Unauthorized: check your API key")
case 403:
print("Forbidden: insufficient permissions")
case 404:
print("Endpoint not found: check your URL")
case 429:
retry_after = response.headers.get("Retry-After", "60")
print(f"Rate limited: retry after {retry_after} seconds")
case 500:
print("Server error: try again later")
case _:
print(f"Unexpected status: {response.status_code}")
에러 핸들링 및 재시도
기본 에러 핸들링
import requests
from requests.exceptions import (
ConnectionError,
Timeout,
HTTPError,
RequestException
)
try:
response = requests.post(
url,
json=data,
timeout=30 # 항상 타임아웃을 설정하세요
)
response.raise_for_status() # 4xx/5xx 응답 시 HTTPError 발생
result = response.json()
except ConnectionError:
print("Failed to connect to server")
except Timeout:
print("Request timed out")
except HTTPError as e:
print(f"HTTP error: {e.response.status_code}")
except RequestException as e:
print(f"Request failed: {e}")
지수 백오프(Exponential Backoff)를 이용한 재시도
import requests
import time
def post_with_retry(url, json_data, headers=None, max_retries=3, base_delay=1):
"""지수 백오프를 사용하여 POST 요청을 재시도합니다."""
for attempt in range(max_retries):
try:
response = requests.post(
url,
json=json_data,
headers=headers,
timeout=30
)
# 클라이언트 에러는 재시도하지 않음 (429 제외)
if 400 <= response.status_code < 500 and response.status_code != 429:
response.raise_for_status()
# 속도 제한(Rate limit) 발생 시 재시도
if response.status_code == 429:
retry_after = int(response.headers.get("Retry-After", base_delay * (2 ** attempt)))
print(f"Rate limited. Waiting {retry_after}s...")
time.sleep(retry_after)
continue
# 서버 에러 발생 시 재시도
if response.status_code >= 500:
raise requests.exceptions.HTTPError(response=response)
return response
except (requests.exceptions.ConnectionError, requests.exceptions.Timeout) as e:
if attempt == max_retries - 1:
raise
delay = base_delay * (2 ** attempt)
print(f"Attempt {attempt + 1} failed: {e}. Retrying in {delay}s...")
time.sleep(delay)
raise Exception(f"All {max_retries} attempts failed")
# 사용 예시
response = post_with_retry(
"https://api.example.com/v1/data",
json_data={"key": "value"},
headers={"X-API-Key": "your-key"}
)
커넥션 재사용을 위한 `requests.Session` 사용
동일한 서버에 여러 번 요청을 보낼 때는 세션을 사용하여 TCP 커넥션을 재사용하세요.
import requests
session = requests.Session()
session.headers.update({
"X-API-Key": "your-api-key",
"Accept": "application/json"
})
# 이 세션을 통한 모든 요청은 동일한 헤더와 커넥션 풀을 공유합니다
response1 = session.post("https://api.example.com/v1/users", json={"name": "Alice"})
response2 = session.post("https://api.example.com/v1/users", json={"name": "Bob"})
response3 = session.post("https://api.example.com/v1/users", json={"name": "Charlie"})
session.close()
세션은 쿠키 관리, 커넥션 풀링, Keep-alive를 자동으로 처리합니다.
고급 패턴
중첩된 JSON 보내기
data = {
"user": {
"name": "Jane Doe",
"address": {
"street": "123 Main St",
"city": "San Francisco",
"state": "CA"
}
},
"preferences": {
"notifications": True,
"theme": "dark",
"languages": ["en", "es", "fr"]
}
}
response = requests.post(url, json=data)
파일 업로드와 JSON 함께 보내기
한 요청에서 json=과 files=를 동시에 사용할 수 없습니다. 두 가지를 모두 요구하는 API의 경우, JSON을 폼 필드로 인코딩하여 보내야 합니다.
import json
files = {
"image": ("photo.jpg", open("photo.jpg", "rb"), "image/jpeg"),
"metadata": (None, json.dumps({"title": "My Photo", "tags": ["nature"]}), "application/json")
}
response = requests.post(url, files=files, headers={"X-API-Key": "key"})
대용량 JSON 응답 스트리밍
대용량 응답을 반환하는 API의 경우:
response = requests.post(url, json=data, stream=True)
# 응답을 청크 단위로 처리
for chunk in response.iter_content(chunk_size=8192):
process_chunk(chunk)
httpx를 이용한 비동기 요청
비동기 지원이 필요한 애플리케이션에서는 httpx를 사용하세요 (requests 호환 API를 제공합니다).
import httpx
import asyncio
async def post_json_async():
async with httpx.AsyncClient() as client:
response = await client.post(
"https://api.example.com/v1/data",
json={"key": "value"},
headers={"X-API-Key": "your-key"},
timeout=30.0
)
return response.json()
# 실행
result = asyncio.run(post_json_async())
asyncio를 이용한 병렬 요청
import httpx
import asyncio
async def send_batch(items):
async with httpx.AsyncClient() as client:
tasks = [
client.post(
"https://api.example.com/v1/process",
json={"item": item},
headers={"X-API-Key": "your-key"}
)
for item in items
]
responses = await asyncio.gather(*tasks)
return [r.json() for r in responses]
items = ["item1", "item2", "item3", "item4", "item5"]
results = asyncio.run(send_batch(items))
흔한 실수와 해결 방법
| 실수 | 문제점 | 해결 방법 |
|---|---|---|
json=data 대신 data=data 사용 |
JSON이 아닌 폼 인코딩으로 전송됨 | json=data 사용 |
timeout 파라미터 누락 |
요청이 무한정 대기 상태에 빠질 수 있음 | 항상 timeout=30 설정 |
response.raise_for_status() 미호출 |
4xx/5xx 에러에도 실패를 감지 못함 | 에러 체크 로직 추가 |
| API 키 하드코딩 | 보안 위험 | 환경 변수 사용 |
| 세션(Session) 미사용 | 다수 요청 시 성능 저하 | requests.Session() 사용 |
| 응답의 Content-Type 무시 | JSON이 아닌 응답에서 .json() 호출 시 크래시 |
호출 전 헤더 확인 |
빠른 참조 (Quick Reference)
import requests
# 단순 POST
response = requests.post("https://api.example.com/data", json={"key": "value"})
# 헤더 포함
response = requests.post(url, json=data, headers={"X-API-Key": "key"})
# 타임아웃 포함
response = requests.post(url, json=data, timeout=30)
# 인증 포함
response = requests.post(url, json=data, auth=("user", "pass"))
# 응답 확인
response.status_code # 200
response.json() # 파싱된 JSON
response.text # 원문 텍스트
response.headers # 응답 헤더
response.ok # 200-299인 경우 True
response.raise_for_status() # 4xx/5xx 시 예외 발생
결론
패턴만 알면 Python requests로 JSON POST 요청을 보내는 것은 매우 간단합니다. 자동 직렬화를 위해 json=data를 사용하고, 항상 타임아웃을 설정하며, 운영 환경 코드에서는 재시도 로직을 구현하고, 동일 서버에 반복 요청 시 세션을 활용하세요.
Python HTTP 실력을 테스트해 볼 실제 API를 찾고 있다면, Hypereal AI는 AI 기반 이미지 생성, 비디오 제작 및 음성 합성을 위한 REST API를 제공합니다. 해당 엔드포인트들은 API 키 인증을 통한 표준 JSON POST 요청을 사용하므로, 이 가이드에서 배운 패턴을 실습해 보기에 완벽한 사례입니다.
