[Python] requests에서 대량 배열 전송 시 403 오류 해결

2025. 9. 7. 03:00·개발 (Development)/Python

문제 상황

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])

해결 원인

  1. 바디 크기 최적화
    • ensure_ascii=False로 유니코드 탈출을 막아 불필요한 길이 증가 방지
    • separators=(",", ":")로 공백 제거 → 바디 크기 최소화
  2. 고정 Content-Length 전송
    • PreparedRequest 단계에서 바디와 헤더를 확정 → Content-Length를 명확히 지정
    • WAF/서버가 허용하는 “고정 길이 JSON” 요청 형식으로 맞아떨어짐
  3. UI 요청과 동일한 형식 재현
    • 브라우저·Postman에서 보내는 방식(원시 JSON 바이트 + application/json + Content-Length)을 그대로 구현

정리

문제의 본질은 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
'개발 (Development)/Python' 카테고리의 다른 글
  • [Python] `__pycache__`란 무엇인가?
  • [Python] RuntimeError: module compiled against API version 0xf but this version of numpy is 0xe 에러 해결
  • [Python] MAU(Monthly Active Users) 계산 방법
  • [Python/Docker] Python Docker 이미지에서 취약점 제거하기 (python:3.9.12-slim 기반)
LoopThinker
LoopThinker
모르는 것을 알아가고, 아는 것을 더 깊게 파고드는 공간
  • LoopThinker
    CodeMemoir
    LoopThinker
  • 전체
    오늘
    어제
    • 분류 전체보기 (231)
      • 개발 (Development) (165)
        • Algorithm (1)
        • Angular (1)
        • AWS (6)
        • DeepSeek (2)
        • Docker (7)
        • Git (3)
        • Java (34)
        • JavaScript (4)
        • Kafka (5)
        • Kubernetes (4)
        • Linux (7)
        • PostgreSQL (38)
        • Python (31)
        • React (3)
        • TypeScript (3)
        • Vue.js (5)
        • General (11)
      • 데이터 분석 (Data Analysis) (1)
      • 알고리즘 문제 풀이 (Problem Solving.. (27)
      • 자격증 (Certifications) (24)
        • ADsP (14)
        • 정보처리기사 (4)
        • Linux Master (5)
        • SQLD (1)
      • 기술 동향 (Tech Trends) (11)
      • 기타 (Others) (3)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    python
    springboot
    java
    Kafka
    백준알고리즘
    Linux master
    자바
    javascript
    리눅스 마스터 2급
    백준자바
    deepseek
    PostgreSQL
    Spring boot
    Linux
    DevOps
    ADsP
    MyBatis
    오답노트
    timescaledb
    데이터분석
    pandas
    JPA
    Kubernetes
    백준온라인저지
    리눅스 마스터 2급 2차
    JSON
    AWS
    docker
    Vue.js
    백준
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
LoopThinker
[Python] requests에서 대량 배열 전송 시 403 오류 해결
상단으로

티스토리툴바