🌐
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으로 쿠키/헤더를 유지하면서 여러 페이지 요청