API 주소로 요청했는데 JSONDecodeError가 발생했다면 JSON 문법부터 고치기 전에 응답을 확인해야 합니다. 로그인 화면으로 이동했거나, 본문 없는 204를 받았거나, 프록시 오류 HTML을 받았을 수 있습니다. 이 글은 JSON 객체를 반환하기로 한 조회 API를 읽는 작은 함수를 만들고 실패 조건을 나눠 봅니다.

요청을 보내기 전 준비

Requests는 Python 표준 라이브러리가 아니므로 사용하는 가상환경에서 python -m pip install requests로 설치합니다. URL·인증·쿼리·성공 응답 구조는 호출할 서비스의 문서로 확인합니다. 아래 함수는 인증 설정을 가진 Session과 URL을 인자로 받으며, 글 저장이나 자동 재시도는 하지 않습니다.

import requests

def fetch_json_object(session, url, params=None):
    with session.get(
        url,
        params=params,
        headers={"Accept": "application/json"},
        timeout=(3.05, 10),
        allow_redirects=False,
    ) as response:
        if 300 <= response.status_code < 400:
            raise ValueError("이동 응답: 로그인 또는 URL을 확인하세요.")
        response.raise_for_status()
        if response.status_code == 204:
            return None
        if not 200 <= response.status_code < 300:
            raise ValueError("예상하지 않은 HTTP 상태입니다.")
        media_type = response.headers.get("Content-Type", "")
        media_type = media_type.split(";", 1)[0].strip().lower()
        if media_type != "application/json" and not (
            media_type.startswith("application/")
            and media_type.endswith("+json")
        ):
            raise ValueError("JSON으로 선언된 응답이 아닙니다.")
        data = response.json()
        if not isinstance(data, dict):
            raise ValueError("이 호출은 JSON 객체 응답을 기대합니다.")
        return data

raise_for_status()는 4xx·5xx를 HTTP 오류로 올립니다. JSON으로 해석할 수 있다는 사실과 HTTP 성공 여부는 별개입니다. 204는 이 함수에서 None으로 구분하며, 빈 결과 객체 {}와 혼동하지 않습니다. 배열을 반환하는 API에는 마지막 자료형 검사를 그 계약에 맞게 바꿉니다. Requests Response·예외 인터페이스

정상 JSON 안에서도 필요한 필드 확인

관리자 도구 검색처럼 results 배열을 약속한 응답이라면 다음 검사를 더할 수 있습니다. 아래 데이터는 서버 호출 없이 실행하는 예제입니다.

data = {"results": [{"name": "글자수 계산기"}]}
results = data.get("results")
if not isinstance(results, list):
    raise ValueError("검색 응답에 results 배열이 없습니다.")
for item in results:
    if not isinstance(item, dict) or not isinstance(item.get("name"), str):
        raise ValueError("검색 항목에 문자열 name이 필요합니다.")
    print(item["name"])

현재 win-j 관리자 검색은 staff 세션이 필요합니다. 이 글의 함수만으로 로그인이 생기지 않습니다. 인증을 생략하고 운영 주소를 호출한 뒤 로그인 HTML이 왔다면 인증 절차를 먼저 확인해야 합니다. 실제 쿠키 값을 예제 파일에 복사하거나 저장소에 올리지 않습니다.

오류에 따라 다른 조치하기

상황 함수에서 나타나는 결과 다음 조치
200 + JSON 객체 객체 반환 필수 필드와 자료형 검사
200 + 로그인 HTML 형식 오류 이동 이력·인증 방식 확인
302 이동 오류 새 URL 또는 로그인 필요 여부 확인
204 None 빈 본문을 허용하는 계약인지 확인
400·401·429·500 등 HTTPError 상태와 제공자 오류 규칙 확인
JSON 헤더 + 깨진 본문 JSONDecodeError 서버 직렬화·잘린 응답 확인
접속 또는 수신 지연 Timeout 계열 네트워크·서버 상태 점검

연결 실패는 ConnectionError, 인증서 문제는 SSLError 같은 예외로 구분할 수 있습니다. TLS 오류를 없애려고 verify=False를 기본으로 쓰면 서버 인증을 포기하게 됩니다. 인증서 체인·호스트 이름·신뢰 저장소를 확인합니다.

timeout과 재시도의 범위

timeout=(3.05, 10)은 연결 대기와 읽기 대기를 나눈 값입니다. 전체 다운로드가 반드시 13.05초 안에 끝난다는 뜻은 아닙니다. 큰 응답을 다루는 작업에는 다운로드 크기 제한·전체 작업 시간 제한을 별도로 설계해야 합니다. 위 함수는 작은 JSON 응답을 위한 입문 예제입니다. Requests timeout 설명

429를 받았다고 매초 무한 재시도하지 않습니다. 서버의 Retry-After, 제한량, 최대 시도 횟수를 반영합니다. POST는 응답이 사라져도 서버에서 저장됐을 수 있으므로 이 조회 함수를 POST 재시도 코드로 단순 변환하지 않습니다.

params·data·json을 선택하는 기준

쿼리 값은 params={"q": "글자수"}, 폼 본문은 data={...}, JSON 본문은 json={...}으로 전달합니다. json=은 직렬화와 JSON Content-Type 설정을 맡습니다. 현재 이 사이트의 관리자 글 저장은 폼 계약이므로 JSON으로 바꾸어 보내는 것만으로 호환되지 않습니다.

서버로 보내기 전에 형식을 비교하는 방법은 HTTP 헤더 글에 있습니다. 이 예제는 자동 로그인·대용량 처리·재시도·모든 JSON 스키마 검증까지 제공하는 범용 클라이언트는 아닙니다.