크롤러 / 스크래퍼

주거용 / 모바일 프록시 연동 가이드

데이터센터 IP 차단 우회, Bright Data·Webshare 주거용 회전 프록시 연동, 환경변수 설정 및 비용 최적화 전략.

업데이트: 2026-10-02

대부분의 일반 웹사이트는 올바른 헤더와 playwright-stealth 설정만으로도 DashCloud의 기본 데이터센터 IP를 통해 문제없이 수집할 수 있습니다.

하지만 Cloudflare Turnstile, Akamai Bot Manager, 네이버/쿠팡 등 보안 수준이 극도로 높은 타깃 사이트는 요청이 들어온 IP가 **데이터센터 호스팅 대역(AWS, GCP, Vultr 등)**에 속해 있는 것 자체를 감지하여 캡차 화면을 띄우거나 차단합니다.

이러한 고난도 타깃 사이트를 수집할 때 주거용 및 모바일 회전 프록시를 DashCloud에 연동하고 비용을 최소화하는 실전 전략을 다룹니다.


데이터센터 IP vs 주거용 프록시 비교

[ 일반 데이터센터 IP 요청 ]
DashCloud 크롤러  ─────── 1. "상품 가격 요청" ───────►  타깃 쇼핑몰 WAF
                                                         └─► "IP 조회: 호스팅 데이터센터(AWS/GCP) 대역 감지!"
                                                         └─► ❌ 즉시 403 Forbidden 또는 캡차 차단
 
[ 주거용 프록시 경유 요청 ]
DashCloud 크롤러  ───────►  주거용 프록시 게이트웨이  ───────►  타깃 쇼핑몰 WAF
                              (실제 가정용 인터넷 IP로 변환)       └─► "IP 조회: 일반 가정집 초고속 인터넷(KT/SKT/Comcast) 확인"
                                                                  └─► ✅ 100% 정상 페이지 응답
  • 데이터센터 IP: 가격이 매우 저렴하거나 무료(서버 기본 IP)이지만, 보안 솔루션의 IP 평판 점수가 낮아 봇 탐지에 쉽게 걸립니다.
  • 주거용 IP: 실제 일반 가정집 초고속 인터넷 회선을 경유하므로 WAF를 완벽히 통과하지만, 트래픽(GB당 $2~$5 수준)에 따라 과금되므로 리소스를 아껴 써야 합니다.

연동 방식 1: 대시보드 환경변수로 전역 프록시 주입

가장 간편한 방법은 DashCloud 대시보드의 환경 변수 메뉴에 표준 프록시 환경변수를 등록하는 것입니다. requests, httpx, aiohttp 등 표준 파이썬 라이브러리가 자동으로 이 설정을 감지해 모든 트래픽을 프록시로 라우팅합니다.

Node.js 런타임(NODE_20 / NODE_22)에는 undici 프리로드가 이미지에 내장되어 있어, 같은 환경변수를 등록하면 fetch() 요청이 자동으로 프록시를 경유합니다. 코드 수정이 필요 없습니다.

대시보드 환경변수 등록 (프로젝트 ➔ 환경변수 탭)
HTTP_PROXY=http://username:[email protected]:8000
HTTPS_PROXY=http://username:[email protected]:8000
ALL_PROXY=socks5://username:[email protected]:1080
적용 시점

환경변수를 저장하면 컨테이너가 재생성되며 새 프록시 설정이 주입됩니다. 적용 여부는 콘솔에서 curl -s ifconfig.me 같은 확인 스크립트로 검증하세요.


연동 방식 2: Playwright 컨텍스트별 프록시 동적 주입

수집 루프마다 서로 다른 프록시 IP를 순환시키거나, 특정 페이지에서만 프록시를 켜고 싶을 때는 코드 레벨에서 브라우저 컨텍스트 단위로 주입합니다.

playwright_proxy.py
import os
from playwright.sync_api import sync_playwright
 
PROXY_SERVER = os.environ.get("PROXY_SERVER", "http://proxy.example.com:8000")
PROXY_USERNAME = os.environ.get("PROXY_USERNAME")
PROXY_PASSWORD = os.environ.get("PROXY_PASSWORD")
 
def crawl_with_proxy(target_url: str):
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=True)
 
        # [핵심] 컨텍스트 생성 시 프록시 설정 주입
        proxy_config = {
            "server": PROXY_SERVER,
        }
        if PROXY_USERNAME and PROXY_PASSWORD:
            proxy_config["username"] = PROXY_USERNAME
            proxy_config["password"] = PROXY_PASSWORD
 
        context = browser.new_context(
            proxy=proxy_config,
            viewport={"width": 1920, "height": 1080}
        )
 
        page = context.new_page()
        try:
            # 내 실제 아웃바운드 IP 확인 페이지 접속
            page.goto("https://api.ipify.org?format=json", timeout=20000)
            print("현재 프록시 IP:", page.content())
 
            # 실제 타깃 사이트 접속
            page.goto(target_url, timeout=30000)
            print("타깃 수집 성공:", page.title())
 
        finally:
            context.close()
            browser.close()
 
if __name__ == "__main__":
    crawl_with_proxy("https://example.com")

프록시 비용 80% 절감 전략: "스마트 라우팅"

주거용 프록시는 GB 단위로 요금이 청구됩니다. 이미지나 자바스크립트 번들 파일까지 비싼 주거용 프록시로 다운로드하면 수집 몇 번 만에 수만 원의 요금이 날아갈 수 있습니다.

1. 이미지 및 미디어 요청 네트워크 레벨 차단

Playwright의 route.abort()를 적용하면 HTML 텍스트 데이터만 프록시를 통과하므로, 페이지당 트래픽 소모량이 3MB에서 150KB 이하로 95% 급감합니다.

# 이미지/폰트/미디어는 프록시 대역폭을 타지 않도록 즉시 거절
page.route(
    "**/*", 
    lambda route: route.abort() 
    if route.request.resource_type in ["image", "media", "font"] 
    else route.continue_()
)

2. 하이브리드 라우팅 (차단 시에만 프록시 투입)

평상시에는 무료인 DashCloud 서버 기본 IP로 수집을 시도하다가, 403 Forbidden이나 캡차가 감지되었을 때만 주거용 프록시 풀로 전환하는 페일오버 로직을 구축하세요.


프록시 연결 장애 자동 복구 페일오버 풀 템플릿

프록시 노드가 일시적으로 응답하지 않거나 타임아웃이 발생했을 때, 멈추지 않고 다음 프록시로 자동 전환하는 견고한 클래스입니다.

proxy_pool.py
import random
import logging
from typing import List, Dict
 
logger = logging.getLogger("ProxyPool")
 
class ResilientProxyManager:
    def __init__(self, proxy_list: List[Dict[str, str]]):
        self.proxy_list = proxy_list
        self.failed_proxies = set()
 
    def get_random_proxy(self) -> Dict[str, str]:
        """정상 작동하는 프록시 중 랜덤 선택"""
        available = [p for p in self.proxy_list if p["server"] not in self.failed_proxies]
        if not available:
            logger.warning("모든 프록시가 실패 목록에 있습니다. 실패 목록을 초기화합니다.")
            self.failed_proxies.clear()
            available = self.proxy_list
        return random.choice(available)
 
    def mark_proxy_failed(self, server_url: str):
        """장애 발생 프록시 임시 격리"""
        logger.error(f"프록시 노드 장애 감지, 격리 조치: {server_url}")
        self.failed_proxies.add(server_url)

다음 추천 문서