문제 상황
Python requests
라이브러리로 API에 PUT/POST 요청을 보낼 때, 배열 길이가 100개를 초과하면 403 Forbidden
오류가 발생했습니다.
같은 API를 브라우저 UI나 Postman에서 호출하면 정상 동작했기 때문에, Python에서의 요청 형식에 문제가 있다고 판단했습니다.
원인 분석
requests
자체 제한 없음: 라이브러리 차원에서 배열 크기나 본문 길이를 제한하지는 않습니다.- 직렬화 방식 차이:
json=
옵션은 기본적으로ensure_ascii=True
, 직렬화 시 공백 포함 → 바디가 불필요하게 길어짐. - 전송 형식 차이:
Content-Length
가 명확히 지정되지 않거나 청크드 전송이 되면, 보안 장비(WAF)가 차단할 수 있음. - UI와 차이: 브라우저/포스트맨은 항상
application/json
+ 고정Content-Length
로 원시 JSON을 보냄.
해결 방법
PreparedRequest
를 활용해 보낼 데이터와 헤더를 직접 직렬화하고 고정 길이로 전송하도록 수정했습니다.
import json, requests
payload = {...}
url = "https://api.example.com/v1/resource"
headers = {"Content-Type": "application/json"}
# 1) JSON 직렬화 (UTF-8, 공백 제거)
body_bytes = json.dumps(
payload,
ensure_ascii=False,
separators=(",", ":")
).encode("utf-8")
# 2) PreparedRequest 생성
req = requests.Request("POST", url, headers=headers, data=body_bytes)
prepped = req.prepare()
# 3) 최종 Content-Length 확인
print("Content-Length:", prepped.headers.get("Content-Length"))
# 4) 전송
with requests.Session() as s:
resp = s.send(prepped, timeout=60)
print(resp.status_code, resp.text[:200])
해결 원인
- 바디 크기 최적화
ensure_ascii=False
로 유니코드 탈출을 막아 불필요한 길이 증가 방지separators=(",", ":")
로 공백 제거 → 바디 크기 최소화
- 고정 Content-Length 전송
PreparedRequest
단계에서 바디와 헤더를 확정 →Content-Length
를 명확히 지정- WAF/서버가 허용하는 “고정 길이 JSON” 요청 형식으로 맞아떨어짐
- UI 요청과 동일한 형식 재현
- 브라우저·Postman에서 보내는 방식(원시 JSON 바이트 +
application/json
+ Content-Length)을 그대로 구현
- 브라우저·Postman에서 보내는 방식(원시 JSON 바이트 +
정리
문제의 본질은 Python이 데이터를 잘못 보내는 것이 아니라, 서버/보안 정책이 특정 형식의 요청만 허용한 것이었습니다.PreparedRequest
를 활용해 최종 전송되는 헤더와 바디를 직접 확인하고 통제하면서 문제를 해결할 수 있었습니다.
앞으로 대량 데이터를 보낼 때는
- 직렬화 크기를 최소화하고,
Content-Length
를 명확히 지정하며,- 필요하다면 데이터를 배치 분할 전송하는 전략이 안전합니다.
반응형
'개발 (Development) > Python' 카테고리의 다른 글
[Python] `__pycache__`란 무엇인가? (0) | 2025.09.19 |
---|---|
[Python] RuntimeError: module compiled against API version 0xf but this version of numpy is 0xe 에러 해결 (0) | 2025.09.19 |
[Python] MAU(Monthly Active Users) 계산 방법 (2) | 2025.08.10 |
[Python/Docker] Python Docker 이미지에서 취약점 제거하기 (python:3.9.12-slim 기반) (0) | 2025.08.03 |
[Python] 문자열 각 단어의 첫 글자를 대문자로 만드는 방법 (0) | 2025.08.03 |