메뉴 닫기

파이썬 requests 타임아웃 설정 가이드 ConnectTimeout ReadTimeout 구분과 서버 지연 및 느린 로리스 방지

파이썬 requests 타임아웃 설정 가이드 ConnectTimeout ReadTimeout 구분과 서버 지연 및 느린 로리스 방지

🧭 한 번에 정리하는 타임아웃 튜닝과 장애 회피 전략으로 API 응답 지연과 연결 고착을 깔끔히 끝내세요

네트워크 요청이 멈춘 듯 버티는 경험을 해본 적이 있다면, 문제의 시작은 대개 타임아웃 설정에서 출발합니다.
파이썬에서 가장 많이 쓰는 HTTP 클라이언트인 requests는 단일 값뿐 아니라 연결과 읽기 단계를 따로 제어할 수 있어, 의도치 않은 대기나 스레드 고갈을 예방하는 핵심 도구가 됩니다.
특히 외부 API를 호출하는 백엔드 서비스나 배치 작업에서는 몇 초 차이로 장애의 규모가 달라지기 때문에, 정확한 기준과 용어를 이해하는 것이 비용을 절감하는 지름길이죠.
이 글은 현장에서 바로 적용할 수 있도록 개념과 실무 팁을 친근하게 풀어 설명합니다.

핵심은 간단합니다.
requests의 타임아웃을 올바르게 설정하고, ConnectTimeout과 ReadTimeout을 분리해 장애 원인을 빠르게 특정하며, 서버 지연과 느린 로리스(Slowloris)처럼 연결을 붙잡는 패턴으로부터 애플리케이션을 방어하는 것입니다.
이를 위해 기본 동작과 값의 의미를 정리하고, 각 타임아웃이 발생하는 상황을 재현 예시와 함께 살펴봅니다.
또한 운영환경에서 안전한 권장값을 잡는 방법, 재시도와 회로차단기 같은 보호 패턴, 로깅 기준까지 한 번에 정리해 드립니다.



🔗 파이썬 requests 타임아웃 기본 개념과 설정값

requests의 timeout은 “얼마나 오래 기다릴 것인가”를 정의하는 필수 안전장치입니다.
값을 지정하지 않으면 기본적으로 무한 대기와 같아, 외부 서비스 지연이나 연결 고착 상황에서 프로세스가 묶일 수 있습니다.
타임아웃은 단일 실수값 또는 (connect, read) 튜플로 지정합니다.
단일값은 연결 수립과 응답 본문 읽기 단계 모두에 동일하게 적용되고, 튜플은 각 단계를 분리해 보다 정밀하게 제어합니다.
운영 환경에서는 서비스 특성에 맞춘 명시적 값 설정이 필수입니다.

여기서 ConnectTimeout은 서버와의 TCP 연결을 마련하는 동안 제한 시간을 의미하며, 서버가 포트를 열지 않았거나 네트워크 경로가 불안정할 때 주로 발생합니다.
반면 ReadTimeout은 연결은 되었지만 응답 헤더 또는 바디를 충분히 빠르게 전달받지 못할 때 발생합니다.
즉, connect는 “붙는 시간”, read는 “데이터가 흘러오는 시간”을 관리하는 셈입니다.
이 두 단계를 구분하면, 장애 원인을 더 빨리 특정하고 대응 정책(재시도, 차단, 격리)을 선택하기 쉬워집니다.

CODE BLOCK
import requests

# 1) 단일 타임아웃: 연결과 읽기에 동일하게 5초
r = requests.get("https://api.example.com/data", timeout=5)

# 2) 튜플 타임아웃: (connect=2초, read=5초)
r = requests.get("https://api.example.com/data", timeout=(2, 5))

# 3) 세션에 공통 파라미터 패턴으로 적용
with requests.Session() as s:
    def get(url, **kw):
        return s.get(url, timeout=(2, 5), **kw)

    resp = get("https://api.example.com/slow")

💡 TIP: 라이브러리 전역 기본값은 없습니다.
반드시 각 호출에 timeout을 명시하거나, 래퍼 함수를 만들어 일관되게 적용하세요.

타임아웃 유형 주요 단계·징후
ConnectTimeout 연결 수립 지연.
포트 미오픈, 방화벽, 경로 장애, DNS 지연 등에서 발생.
ReadTimeout 응답 대기 지연.
서버 부하·슬로우 쿼리·느린 전송(느린 로리스 유사 패턴 포함)에서 발생.

💬 timeout은 최대 대기 시간을 강제할 뿐, 요청의 총 실행 시간을 보장하는 SLA가 아닙니다.
재시도, 백오프, 회로차단기 같은 보호 장치와 함께 사용해야 서비스 안정성이 높아집니다.

  • 🛠️timeout은 모든 외부 호출에 명시한다.
  • ⚙️가능하면 (connect, read) 튜플로 분리한다.
  • 🔌지속 지연 시 예외 로그에 ConnectTimeoutReadTimeout을 구분 기록한다.

⚠️ 주의: 긴 스트리밍 응답을 다룰 때 read 타임아웃을 과도하게 짧게 잡으면 정상적인 대용량 다운로드도 잦은 예외로 중단될 수 있습니다.
스트리밍이나 서버-센트 이벤트처럼 장시간 연결을 기대하는 경우에는 단계별 타임아웃을 신중히 조정하세요.

핵심 정보 요약입니다.
1) timeout=None은 사실상 무제한 대기입니다.
2) 단일 값은 연결·읽기 모두에 적용되고, 튜플은 연결과 읽기를 명확히 분리합니다.
3) ConnectTimeout은 붙는 시간, ReadTimeout은 데이터가 오는 시간을 관리합니다.
4) 운영 환경에서는 요청 유형과 평균 응답 지연 분포를 바탕으로 합리적 기준을 수립해야 서버 지연과 느린 로리스류의 고착 패턴을 효과적으로 방지할 수 있습니다.

🛠️ ConnectTimeout과 ReadTimeout의 차이와 재현 예시

파이썬 requests를 사용할 때 가장 흔한 오해 중 하나는 ConnectTimeoutReadTimeout이 단순히 “시간 초과”라는 동일한 예외라고 생각하는 것입니다.
하지만 두 타임아웃은 완전히 다른 단계에서 발생하며, 각각의 대응 전략도 달라야 합니다.
두 가지를 구분하지 못하면 불필요한 재시도나 네트워크 리소스 낭비, 그리고 실제 원인 파악의 지연으로 이어질 수 있습니다.

🔎 ConnectTimeout 재현과 분석

이 예외는 서버로 연결을 맺지 못했을 때 발생합니다.
즉, TCP 3-way handshake가 일정 시간 내에 완료되지 못하면 ConnectTimeout이 트리거됩니다.
보통 원인은 서버 포트 미열림, 방화벽, 라우팅 장애, DNS 응답 지연입니다.

CODE BLOCK
import requests
from requests.exceptions import ConnectTimeout

try:
    # 존재하지 않는 IP 또는 포트로 연결 시도
    requests.get("http://10.255.255.1:8080", timeout=(2, 5))
except ConnectTimeout:
    print("⚠️ ConnectTimeout 발생: 서버와 연결할 수 없습니다.")

💡 TIP: ConnectTimeout이 자주 발생한다면, DNS 캐시 또는 네트워크 경로를 모니터링하고, 장애 구간을 로그에 남겨야 합니다.

⏳ ReadTimeout 재현과 분석

서버와 연결은 되었지만, 응답 데이터가 지정된 시간 안에 오지 않을 때 발생합니다.
이 경우 서버의 부하, 느린 쿼리, 또는 고의적으로 데이터를 천천히 보내는 느린 로리스(Slowloris) 공격 패턴일 수 있습니다.

CODE BLOCK
import requests
from requests.exceptions import ReadTimeout

try:
    # 응답이 일부러 느린 서버 호출
    requests.get("https://httpstat.us/200?sleep=6000", timeout=(2, 3))
except ReadTimeout:
    print("⏰ ReadTimeout 발생: 응답이 너무 느립니다.")

이처럼 서버는 이미 연결을 받아들였지만 데이터를 일정한 속도로만 흘려보낼 수 있습니다.
이를 방치하면 클라이언트 리소스가 묶여 전체 워커가 소진될 수 있죠.
따라서 ReadTimeout은 단순히 느린 응답을 잘라내는 용도뿐 아니라, 애플리케이션의 생존성을 지키는 차단벽 역할을 합니다.

💬 ConnectTimeout은 “붙지 못한 경우”, ReadTimeout은 “붙었지만 안 오는 경우”입니다.
이 차이를 명확히 구분해 로깅하면 장애 원인 분석 속도가 두 배는 빨라집니다.

  • 📡ConnectTimeout은 TCP 단계에서 실패하므로 재시도 시 DNS·네트워크 경로를 우선 점검한다.
  • ⚙️ReadTimeout은 서버 성능 저하 또는 응답 처리 지연을 뜻하므로, API의 응답 시간 SLA를 점검한다.
  • 🧩두 예외를 별도로 캡처해 로깅하고, 장애 리포트에 명시하면 분석 효율이 높아진다.

이 두 타임아웃의 구분은 단순히 개발자의 편의를 위한 개념이 아니라, 서비스 복구 속도와 시스템 안정성을 좌우하는 요소입니다.
실제 운영 환경에서는 둘을 구분해 로깅하고, 알람 규칙이나 자동화된 복구 로직에도 별도로 반영해야 합니다.



⚙️ 서버 지연과 느린 로리스 패턴이 왜 문제인가

서버가 응답을 지연하거나 일부러 천천히 데이터를 전송할 때, 클라이언트는 연결을 오래 유지하며 리소스를 소비하게 됩니다.
이 현상을 Slowloris(느린 로리스)라고 부르며, 과거에는 주로 웹 서버를 대상으로 한 공격 패턴이었지만 최근에는 API 요청이나 스트리밍에서도 발견됩니다.
이 공격의 본질은 “끊지 않고 붙잡기”입니다.
조금씩 패킷을 보내며 서버 소켓을 계속 점유하고, 클라이언트도 타임아웃이 설정되어 있지 않다면 무한히 기다리는 구조로 빠집니다.

예를 들어, 요청 수가 많지 않아도 서버가 지속적으로 열린 연결을 다수 유지하면 워커 수가 고갈되고, 새로운 요청을 처리하지 못해 전체 지연으로 확산됩니다.
반대로 클라이언트 측에서는 ReadTimeout이 설정되어 있지 않다면, 느리게 오는 데이터 스트림 하나가 워커를 영구적으로 묶어둘 수 있습니다.
이런 상황이 누적되면 시스템은 장애가 아닌 것처럼 보이지만, 실상은 리소스 고갈 상태가 되어버립니다.

🔍 느린 로리스의 작동 원리

느린 로리스 공격은 간단한 원리로 작동합니다.
클라이언트가 HTTP 요청 헤더를 완성하지 않은 상태에서, 주기적으로 소량의 데이터를 전송해 서버가 요청을 “아직 진행 중”이라 인식하게 만듭니다.
서버는 연결을 유지하며 응답을 기다리게 되고, 결국 연결 수 한도에 도달하면 정상적인 요청까지 처리하지 못하게 되죠.
이 방식은 서버뿐 아니라, 요청을 기다리는 클라이언트 쪽에서도 문제를 유발할 수 있습니다.

💡 TIP: 느린 로리스는 단순한 트래픽 폭주가 아닙니다.
요청을 “끊지 않고 유지”하는 방식이므로, 일반적인 DDoS 탐지보다 훨씬 식별이 어렵습니다.

🧩 클라이언트 측 방어 전략

requests 라이브러리에서 타임아웃을 올바르게 설정하면, 이런 “붙잡기” 패턴으로부터 애플리케이션을 보호할 수 있습니다.
특히 ReadTimeout은 서버가 느리게 데이터를 보내더라도 지정된 시간 이후에는 연결을 강제로 끊습니다.
즉, 공격 여부와 관계없이 일정 시간 내 응답이 없으면 종료시켜 시스템의 안정성을 확보합니다.
또한 ConnectTimeout은 연결 시도를 무한히 재시도하는 문제를 방지하여, 불안정한 네트워크 환경에서 대기 시간 폭주를 막는 데도 효과적입니다.

💬 서버 성능만 높인다고 느린 로리스 공격을 막을 수는 없습니다.
정확한 타임아웃 정책과 연결 관리 로직이 병행되어야 시스템 전체가 안전해집니다.

  • 🧠ReadTimeout을 반드시 지정해, 서버의 느린 응답이 애플리케이션 전체를 묶지 않도록 한다.
  • ⚙️서버나 클라이언트 모두 keep-alive 연결의 최대 수명을 제한한다.
  • 🚦지속적인 응답 지연이 탐지되면 회로차단기(circuit breaker)를 트리거하여 연결을 차단한다.

⚠️ 주의: 단순히 ReadTimeout을 크게 늘리는 것은 해결책이 아닙니다.
오히려 느린 로리스와 같은 공격을 더욱 효과적으로 만들어버릴 수 있습니다.
적절한 시간 설정과 로깅, 모니터링이 함께 이루어져야 합니다.

즉, 타임아웃은 단순한 편의 기능이 아니라 네트워크 보안과 시스템 안정성의 첫 번째 방어선입니다.
ConnectTimeout과 ReadTimeout을 적절히 설정하는 것은 서비스 품질을 보장하기 위한 최소한의 안전장치이며, 서버 지연과 느린 로리스 같은 장기 점유형 문제를 예방하는 가장 확실한 방법입니다.

🔌 안전한 타임아웃 튜닝 가이드와 권장값

타임아웃 설정은 모든 환경에서 동일하게 적용할 수 있는 절대값이 아닙니다.
네트워크의 품질, API의 성격, 응답 크기, 서버의 위치 등 여러 요소를 종합적으로 고려해야 합니다.
다만 requests의 타임아웃은 합리적인 범위 내에서 “짧게 설정할수록 안전하다”는 원칙이 있습니다.
너무 길게 설정하면 불필요한 대기가 늘어나고, 너무 짧으면 정상 요청이 중단될 수 있기 때문이죠.

📊 권장 타임아웃 값 기준

실무에서 널리 사용되는 기본 권장값은 다음과 같습니다.
이는 서버와의 연결 지연, 평균 응답 시간, 사용자 체감 속도를 모두 고려한 수치로, 일반적인 API 통신 환경에서 안정적입니다.

환경 ConnectTimeout ReadTimeout
로컬 테스트 / 내부망 0.5~1초 2~3초
공공 API / 일반 HTTP 요청 1~3초 5~10초
대용량 다운로드 / 스트리밍 2~5초 20초 이상 (스트림 길이에 따라 조정)

requests는 단일값으로도 타임아웃을 지정할 수 있지만, 운영 환경에서는 반드시 (connect, read) 튜플을 권장합니다.
이를 통해 연결 지연과 응답 지연을 독립적으로 조정할 수 있고, 장애 원인 분석도 명확해집니다.

🧠 튜닝 시 고려할 핵심 포인트

  • 🧩API의 평균 응답 시간의 2~3배 정도로 ReadTimeout을 설정한다.
  • ⏱️연결 타임아웃은 최대 5초 이내로 유지해 네트워크 지연에 빠르게 대응한다.
  • 📉장기 연결이나 스트리밍은 세션 단위로 별도 타임아웃을 지정한다.
  • 🚨타임아웃 예외 발생 시 단순 재시도보다는 회로차단기(Circuit Breaker)로 보호한다.

💡 TIP: 운영 로그에 “요청 시작 시각, 연결 완료 시각, 응답 완료 시각”을 모두 남기면 ConnectTimeout과 ReadTimeout 튜닝을 객관적으로 검증할 수 있습니다.

적절한 타임아웃 값은 단순히 에러를 줄이는 것이 아니라, 전체 시스템의 응답성을 개선하고 리소스 효율을 극대화합니다.
API 요청이 수천 건 이상 발생하는 환경이라면, 이 미세한 조정이 CPU·메모리 사용량을 20% 이상 줄일 수도 있습니다.
결국 타임아웃 튜닝은 안정성과 성능을 동시에 잡는 필수적인 운영 스킬입니다.



💡 프로덕션에서의 예외 처리 재시도 회로차단기

프로덕션 환경에서는 단순히 타임아웃을 설정하는 것만으로는 충분하지 않습니다.
네트워크는 언제나 불안정하고, 일시적인 지연이나 서버의 응답 정체는 피할 수 없는 변수입니다.
따라서 타임아웃과 함께 예외 처리, 재시도 로직, 회로차단기(Circuit Breaker)를 결합해야 서비스 연속성을 보장할 수 있습니다.

예외를 구체적으로 구분하면 대응도 효율적입니다.
예를 들어 ConnectTimeout은 네트워크 또는 DNS 문제일 가능성이 높으므로, 즉시 재시도보다 백오프(backoff)를 두고 일정 횟수만 반복하는 것이 안전합니다.
반면 ReadTimeout은 서버 부하나 느린 쿼리 탓일 수 있으므로, 즉시 재시도는 오히려 부하를 악화시킬 수 있습니다.

🔁 requests + retry 조합 예시

파이썬 requests는 urllib3.util.retry.Retry 클래스를 통해 손쉽게 재시도 정책을 추가할 수 있습니다.
아래 예시는 ConnectTimeout 시에만 재시도를 수행하고, ReadTimeout 시에는 회로차단기를 통해 중단하는 안전한 구조입니다.

CODE BLOCK
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry
import requests

retry_strategy = Retry(
    total=3,
    backoff_factor=0.5,
    status_forcelist=[500, 502, 503, 504],
    allowed_methods=["GET", "POST"]
)

adapter = HTTPAdapter(max_retries=retry_strategy)
session = requests.Session()
session.mount("http://", adapter)
session.mount("https://", adapter)

try:
    response = session.get("https://api.example.com/data", timeout=(2, 5))
    response.raise_for_status()
except requests.exceptions.ConnectTimeout:
    print("⚠️ ConnectTimeout - 네트워크 연결 실패, 재시도 정책 수행")
except requests.exceptions.ReadTimeout:
    print("⏰ ReadTimeout - 서버 응답 지연, 회로차단기 트리거")

🧩 회로차단기(Circuit Breaker) 패턴

회로차단기는 일정 횟수 이상 오류가 발생하면 외부 호출을 일시적으로 차단하여 시스템을 보호하는 패턴입니다.
대표적인 구현 라이브러리로는 pybreaker가 있으며, requests와 함께 사용해 네트워크 장애가 확산되는 것을 막을 수 있습니다.

CODE BLOCK
from pybreaker import CircuitBreaker
import requests

breaker = CircuitBreaker(fail_max=5, reset_timeout=30)

@breaker
def safe_request():
    return requests.get("https://api.example.com/data", timeout=(2, 5))

try:
    resp = safe_request()
except Exception as e:
    print("🚫 회로차단기 작동:", e)

💎 핵심 포인트:
ConnectTimeout은 네트워크 단절일 때 재시도하고, ReadTimeout은 서버 문제일 때 회로를 차단하라.
이를 구분해야 트래픽 폭주나 장애 확산을 막을 수 있습니다.

이러한 전략은 단순한 안정성 보완이 아니라, 서비스 품질 관리의 핵심 요소입니다.
재시도와 타임아웃, 회로차단기를 유기적으로 결합하면 예측 불가능한 외부 환경에서도 API 호출이 안전하게 수행됩니다.
즉, 타임아웃 설정은 네트워크의 일시적 문제를 회피하고, 회로차단기는 시스템의 연쇄 장애를 차단하는 이중 방어선이 되는 것입니다.

자주 묻는 질문 (FAQ)

requests의 timeout 기본값은 얼마인가요?
기본값은 없습니다. timeout을 명시하지 않으면 무제한 대기 상태가 되어, 서버가 응답하지 않을 경우 프로그램이 멈출 수 있습니다. 반드시 명시적으로 지정해야 합니다.
ConnectTimeout과 ReadTimeout은 각각 언제 발생하나요?
ConnectTimeout은 서버에 연결이 이루어지지 못할 때, ReadTimeout은 연결은 되었지만 응답 데이터가 지정된 시간 내에 오지 않을 때 발생합니다.
느린 로리스(Slowloris) 공격은 어떤 문제를 일으키나요?
클라이언트가 의도적으로 데이터를 천천히 전송하여 서버가 연결을 계속 유지하게 만듭니다. 결과적으로 서버 리소스가 고갈되고 정상 요청이 차단됩니다.
timeout 튜플 (connect, read) 형태로 지정하는 이유는 뭔가요?
두 단계를 분리하면 연결 문제와 응답 지연 문제를 명확히 구분할 수 있고, 각각 다른 정책(재시도·차단)을 적용할 수 있습니다.
타임아웃 값을 너무 크게 잡으면 어떻게 되나요?
서버 지연이나 느린 로리스 공격에 쉽게 노출됩니다. 대기 시간이 늘어나 리소스가 점유되고, 전체 응답성이 저하될 수 있습니다.
백오프(backoff)는 무엇인가요?
재시도 간격을 점진적으로 늘리는 기법으로, 네트워크 과부하나 서버의 순간적 부하를 완화할 수 있습니다. 예를 들어 1초 → 2초 → 4초 간격으로 재시도합니다.
회로차단기(Circuit Breaker)는 어떻게 동작하나요?
일정 횟수 이상 실패하면 외부 호출을 잠시 중단하고, 일정 시간이 지나면 다시 시도합니다. 연쇄 장애를 방지하는 보호 메커니즘입니다.
requests 외에 타임아웃 제어를 지원하는 다른 방법이 있나요?
asyncio의 aiohttp, httpx 등 비동기 HTTP 클라이언트도 개별 타임아웃 옵션을 제공합니다. 비동기 환경에서는 httpx의 timeout 설정이 더 세밀하게 제어됩니다.

🧭 파이썬 requests 타임아웃으로 서버 지연을 완벽히 제어하기

파이썬의 requests는 간결하면서도 강력한 HTTP 클라이언트이지만, 타임아웃을 설정하지 않으면 잠재적인 서비스 장애의 원인이 됩니다.
ConnectTimeout과 ReadTimeout을 구분해 지정하면 네트워크 연결 문제와 서버 응답 지연을 효과적으로 분리할 수 있고, 로그 분석과 자동화된 대응이 훨씬 쉬워집니다.
또한 느린 로리스(Slowloris)와 같은 지연형 공격에도 타임아웃 설정이 가장 실질적인 방어 수단이 됩니다.
특히 프로덕션 환경에서는 재시도, 회로차단기, 백오프 로직을 결합해 운영 안정성을 강화해야 합니다.

핵심은 적절한 시간 설정명확한 예외 구분입니다.
timeout을 튜플 형태로 지정하고, 각 예외를 세분화해 처리하면 불필요한 리소스 낭비 없이 장애 원인을 정확히 추적할 수 있습니다.
결국 타임아웃은 코드 한 줄로 시스템의 회복력을 높이는 가장 단순하면서도 강력한 전략입니다.


🏷️ 관련 태그 : 파이썬, requests, 타임아웃, ConnectTimeout, ReadTimeout, 서버지연, 느린로리스, 네트워크지연, 회로차단기, 백오프