낮은 성공률의 원인을 진단하는 방법: 단계별 가이드
성공률(Success Rate)은 전체 시도 횟수 중 성공한 요청의 백분율입니다. 이 지표가 정상 범위 아래로 떨어지면 돈, 시간, 데이터가 낭비됩니다. 원인은 수십 가지가 될 수 있습니다: 잘못된 설정부터 대상 서버의 차단까지. 이 글에서는 체계적인 진단 방법을 살펴보고 해결책을 찾아보겠습니다.
성공률이란 무엇이고 정상 범위는 얼마인가
성공률(SR) = (성공한 요청 / 전체 요청 수) × 100%
정상 값은 작업 유형에 따라 다릅니다:
| 작업 | 정상 SR | 위험 수준 |
|---|---|---|
| 공개 데이터 파싱 | 95–99% | 85% 미만 |
| SNS 자동화 | 90–97% | 80% 미만 |
| 광고 검증 | 98–99.5% | 95% 미만 |
| API 통합 | 99–99.9% | 98% 미만 |
기본 수준에서 SR이 5–10% 떨어지면 진단이 필요합니다. 20% 이상 떨어지면 긴급 조치가 필요합니다.
진단의 첫 단계
1단계: 로그 및 메트릭 확인
지난 24–72시간의 데이터를 수집하세요:
- 정확히 언제 SR이 떨어졌나요? (정확한 시간)
- 407 오류(프록시 인증 필요)를 반환하는 요청의 비율은?
- 429 오류(너무 많은 요청)의 비율은?
- 타임아웃(연결 타임아웃)의 비율은?
- 부하(RPS - 초당 요청 수)가 변경되었나요?
2단계: 격리된 상태에서 테스트
애플리케이션 없이 프록시를 확인하는 간단한 스크립트를 사용하세요:
import requests
import time
proxy = "http://proxy_ip:port"
proxies = {"http": proxy, "https": proxy}
target_url = "https://httpbin.org/ip"
success = 0
failed = 0
for i in range(100):
try:
response = requests.get(
target_url,
proxies=proxies,
timeout=10,
verify=False
)
if response.status_code == 200:
success += 1
print(f"✓ 시도 {i+1}: 성공")
else:
failed += 1
print(f"✗ 시도 {i+1}: 상태 {response.status_code}")
except Exception as e:
failed += 1
print(f"✗ 시도 {i+1}: {str(e)}")
time.sleep(0.5)
sr = (success / (success + failed)) * 100
print(f"\n성공률: {sr:.1f}%")
print(f"성공: {success}, 오류: {failed}")
이 테스트가 정상 SR을 보이면 문제는 코드나 설정에 있습니다. SR이 낮으면 프록시나 대상 서버에 문제가 있습니다.
프록시 측의 문제
오류 407: 프록시 인증 필요
원인:
- 잘못된 자격증명(로그인/비밀번호)
- 계정 만료
- IP 주소가 화이트리스트에 없음(필요한 경우)
- IP 로테이션이 작동하지 않거나 비활성화됨
해결책:
import requests
# 레지던셜 프록시의 올바른 형식
proxy = "http://login:password@proxy-host:port"
proxies = {"http": proxy, "https": proxy}
# 테스트
response = requests.get("https://httpbin.org/ip", proxies=proxies, timeout=10)
print(response.text)
프록시 서버 과부하
모든 서비스 사용자가 동시에 많은 요청을 보내면 RPS(초당 요청 수) 제한이 발생할 수 있습니다. 이는 드물지만 발생합니다.
확인하세요:
- 현재 피크 RPS
- 요금제의 제한
- 로그에 429 오류가 있는지 여부
해결책: 요청 간 지연을 추가하거나 요금제를 업그레이드하세요.
IP 주소 품질
레지던셜 프록시의 경우 낮은 SR은 차단된 주소로 로테이션하고 있다는 의미일 수 있습니다. 확인하세요:
- 403 Forbidden을 반환하는 IP 주소의 비율은?
- 동일한 주소가 반복되나요?
- 패턴이 있나요 - 한 국가/지역은 작동하고 다른 지역은 작동하지 않나요?
대상 서버의 차단 및 필터
오류 429: 너무 많은 요청
대상 서버가 하나의 IP에서 또는 전체적으로 너무 많은 요청을 감지했습니다. 해결책:
- 지연 추가: `time.sleep(random.uniform(1, 3))`
- IP 로테이션 사용: 각 요청마다 새 IP
- RPS 감소: 병렬이 아닌 순차적으로 요청 전송
- 현실적인 헤더 추가: User-Agent, Referer, Accept-Language
오류 403 Forbidden
서버가 IP(또는 프록시 IP)를 차단했습니다. 다음과 같을 수 있습니다:
- 지리적 위치 필터
- 프록시 서비스 블랙리스트
- 봇 감지기(JavaScript, CAPTCHA)
해결책: 모바일 프록시 또는 로테이션이 있는 레지던셜 프록시를 사용하세요. 차단하기가 더 어렵습니다.
오류 403: User-Agent 확인
일부 서비스는 의심스러운 User-Agent를 거부합니다:
import requests
import random
user_agents = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36",
"Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15"
]
headers = {
"User-Agent": random.choice(user_agents),
"Accept-Language": "en-US,en;q=0.9",
"Accept": "text/html,application/xhtml+xml",
"Referer": "https://google.com"
}
response = requests.get(
"https://target-site.com",
headers=headers,
proxies={"http": proxy, "https": proxy},
timeout=10
)
print(response.status_code)
클라이언트 코드의 오류
잘못된 예외 처리
일반적인 오류: 코드가 연결 오류를 실패한 요청으로 계산하지만 재연결을 시도하지 않습니다:
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
# 재시도 로직이 있는 올바른 방법
session = requests.Session()
retry_strategy = Retry(
total=3,
backoff_factor=1,
status_forcelist=[429, 500, 502, 503, 504],
allowed_methods=["GET", "POST"]
)
adapter = HTTPAdapter(max_retries=retry_strategy)
session.mount("http://", adapter)
session.mount("https://", adapter)
try:
response = session.get(url, proxies=proxies, timeout=10)
except requests.exceptions.RequestException as e:
print(f"오류: {e}")
# 로그하고 다음 요청으로 이동
잘못된 타임아웃
타임아웃이 너무 짧으면(1–2초) 느린 프록시가 거부됩니다:
- 일반 파싱: 10–30초
- 모바일 프록시: 15–45초
- API: 5–10초
SSL/TLS 오류
`verify=False`를 사용하면 실제 문제를 숨길 수 있습니다. 인증서를 업데이트하는 것이 좋습니다:
import requests
import certifi
# 올바른 방법
response = requests.get(
url,
proxies=proxies,
verify=certifi.where(), # verify=False 대신
timeout=15
)
네트워크 문제 및 타임아웃
연결 타임아웃 vs 읽기 타임아웃
차이가 중요합니다:
- 연결 타임아웃: 프록시가 응답하지 않음(프록시 또는 네트워크 문제)
- 읽기 타임아웃: 대상 서버가 데이터를 천천히 전송(대상 서버 문제)
import requests
# timeout = (connection_timeout, read_timeout)
try:
response = requests.get(
url,
proxies=proxies,
timeout=(5, 15) # 연결 5초, 읽기 15초
)
except requests.exceptions.ConnectTimeout:
print("프록시가 응답하지 않음")
except requests.exceptions.ReadTimeout:
print("대상 서버가 느림")
DNS 문제
대상 서버가 확인되지 않으면 프록시 오류가 아닙니다:
import socket
# 프록시 없이 DNS 확인
try:
ip = socket.gethostbyname("target-site.com")
print(f"확인됨: {ip}")
except socket.gaierror:
print("DNS 오류 — 사이트를 찾을 수 없음")
낮은 SR 진단 체크리스트
- 기본 수준 설정: 이전에 정상이었던 SR은 얼마였나요?
- 격리된 테스트 실행 (위의 스크립트) 100개 요청
- 로그 확인: 어떤 HTTP 코드가 지배적인가요? (407, 429, 403, 타임아웃?)
- 407인 경우: 로그인/비밀번호 및 IP 화이트리스트 확인
- 429인 경우: 요청 간 지연 추가, IP 로테이션 사용
- 403인 경우: User-Agent, Referer 확인, 현실적인 헤더 추가
- 타임아웃인 경우: 타임아웃 증가, RPS 확인, 재시도 로직 사용
- 코드 확인: 올바른 예외 처리, 올바른 타임아웃
- 대상 서버 확인: 프록시 없이 접근 가능한가요?
- 다른 모든 것이 실패하면: 다른 프록시 유형이나 다른 제공자를 시도하세요
빠른 진단 테이블
| HTTP 코드 | 가능한 원인 | 해결책 |
|---|---|---|
| 407 | 잘못된 프록시 자격증명 | 로그인/비밀번호, IP 화이트리스트 확인 |
| 429 | 너무 많은 요청 | 지연 추가, IP 로테이션 사용 |
| 403 | IP 차단 또는 봇 감지 | 현실적인 헤더 추가, 모바일 프록시 사용 |
| 타임아웃 | 프록시가 느리거나 대상 서버 과부하 | 타임아웃 증가, RPS 확인 |
| 연결 거부 | 프록시 서버 접근 불가 | IP:port 확인, 프록시 상태 확인 |
결론
낮은 성공률은 증상이지 질병이 아닙니다. 원인은 많을 수 있습니다: 코드의 오타부터 대상 서버의 차단까지. 체계적인 진단이 해결의 열쇠입니다:
- 메트릭 및 로그 확인
- 문제 격리(프록시 vs 대상 서버 vs 코드)
- 오류 유형 결정(407, 429, 403, 타임아웃)
- 해당 솔루션 적용
안정성과 높은 SR이 필요한 작업의 경우 IP 로테이션이 있는 레지던셜 프록시를 권장합니다. 감지하기가 더 어렵고 더 안정적입니다. proxycove.com에서 무료 테스트를 시도하고 작업에서 테스트해보세요.