🌐

requests 내부 구조 — requests.get()이 실제로 하는 일

Session, PreparedRequest, HTTPAdapter, urllib3까지의 호출 체인

import requests
response = requests.get('https://api.example.com/data')

이 한 줄 뒤에서 벌어지는 일:

호출 체인

requests.get(url)
  → requests.request('GET', url)
    → Session().request('GET', url)   # 임시 Session 생성
      → Request(method, url, ...)     # Request 객체 생성
      → session.prepare_request(req)  # PreparedRequest 조립 (헤더, 쿠키, 인코딩)
      → session.send(prepared)        # 전송
        → adapter.send(prepared)      # HTTPAdapter 선택 (https:// → HTTPSAdapter)
          → urllib3.PoolManager       # 커넥션 풀에서 연결 가져오기
            → socket.connect()        # 실제 TCP 연결

왜 이걸 알아야 하는가

requests.get()을 루프에서 100번 호출하면, 매번 TCP 연결을 새로 만든다. Session을 직접 만들면 커넥션 풀을 재사용한다:

# ❌ 매번 새 연결
for url in urls:
    requests.get(url)

# ✅ 커넥션 풀 재사용
with requests.Session() as s:
    for url in urls:
        s.get(url)

urllib3 — requests의 엔진

requests는 HTTP 프로토콜 처리를 전부 urllib3에 위임한다. 커넥션 풀링, Keep-Alive, 재시도, SSL 검증 — 전부 urllib3 레벨. requests는 그 위에 "사용하기 편한 API"를 얹은 것.

HTTPAdapter로 세밀한 제어

from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

retry = Retry(total=3, backoff_factor=0.5)
adapter = HTTPAdapter(max_retries=retry, pool_connections=10, pool_maxsize=20)

session = requests.Session()
session.mount('https://', adapter)

커넥션 풀 크기, 재시도 횟수/간격, 최대 동시 연결 수를 제어할 수 있다.

핵심 포인트

1

requests.get()은 내부적으로 임시 Session을 만든다

2

PreparedRequest에서 헤더/쿠키/인코딩을 조립

3

HTTPAdapter → urllib3.PoolManager → socket.connect()로 실제 TCP 연결

4

Session을 직접 만들면 커넥션 풀 재사용으로 성능 향상

사용 사례

API 클라이언트 — Session + HTTPAdapter로 재시도/타임아웃/풀 설정 크롤러 — Session으로 쿠키/헤더를 유지하면서 여러 페이지 요청