블로그로 돌아가기

구글 클라우드 함수와 프록시 통합: 파싱 및 자동화를 위한 설정

Google Cloud Functions와 프록시 통합에 대한 완벽한 가이드: HTTP/SOCKS5 설정, Python 및 Node.js 예제, 오류 처리 및 파싱과 자동화를 위한 IP 회전.

📅2026년 2월 18일
```html

Google Cloud Functions는 서버를 관리하지 않고 코드를 실행할 수 있는 서버리스 플랫폼입니다. 파싱, API 요청 자동화 또는 데이터 수집 작업을 할 때는 차단 우회, IP 회전 및 지리적 타겟팅을 위해 프록시를 통해 트래픽을 라우팅해야 하는 경우가 많습니다. 이 가이드에서는 Python 및 Node.js에서 Cloud Functions의 프록시 설정을 실용적인 예제와 함께 살펴보겠습니다.

Cloud Functions에서 프록시를 사용하는 이유

Google Cloud Functions는 Google 데이터 센터의 공용 IP 주소로 격리된 환경에서 작동합니다. 외부 API 또는 웹사이트에 대한 빈번한 요청 시 다음과 같은 문제가 발생합니다:

  • IP 차단 — 많은 서비스(구글, 페이스북, 마켓플레이스)는 데이터 센터에서 발생하는 트래픽을 인식하고 속도 제한 또는 완전 차단을 적용합니다.
  • 지리적 제한 — 특정 국가에서만 접근할 수 있는 콘텐츠에 접근하기 위해 필요합니다(예: Wildberries 또는 Ozon의 지역 가격 파싱).
  • 속도 제한 — 하나의 IP 주소는 분당 제한된 수의 요청을 할 수 있습니다. 프록시는 부하를 분산시킬 수 있습니다.
  • 익명성 — 민감한 데이터 또는 경쟁 정보 작업 시 요청의 실제 출처를 숨깁니다.

Cloud Functions에서 프록시를 사용하는 일반적인 시나리오는 다음과 같습니다:

  • 경쟁업체 가격 모니터링을 위한 마켓플레이스 파싱(Wildberries, Ozon, Amazon)
  • API 또는 웹 스크래핑을 통한 소셜 미디어 데이터 수집(인스타그램, 틱톡)
  • 다양한 지역에서 광고 검토 자동화
  • SEO 분석을 위한 검색 엔진에 대한 대량 요청(구글, 야ндекс)
  • 애플리케이션의 지리적 기능 테스트

Cloud Functions에 적합한 프록시 유형

프록시 유형 선택은 작업, 예산 및 익명성 요구 사항에 따라 다릅니다. 다음은 주요 옵션에 대한 비교입니다:

프록시 유형 속도 익명성 가장 적합한 용도
데이터 센터 프록시 높음 (50-200 ms) 중간 간단한 웹사이트 파싱, API 요청, SEO 모니터링
주거용 프록시 중간 (200-800 ms) 높음 소셜 미디어, 마켓플레이스 파싱, 안티봇 시스템 우회
모바일 프록시 중간 (300-1000 ms) 매우 높음 인스타그램, 틱톡, 모바일 애플리케이션, 페이스북 API

선택 추천:

  • 마켓플레이스 파싱 (Wildberries, Ozon, Amazon) — 요청 시 회전하는 주거용 프록시, 각 요청이 새로운 IP에서 발생하도록 합니다.
  • API 요청 (Google Maps API, OpenWeatherMap) — IP에 대한 엄격한 제한이 없다면 높은 속도의 데이터 센터 프록시를 사용합니다.
  • 소셜 미디어 (인스타그램, 틱톡) — 모바일 프록시를 사용합니다. 모바일 운영자의 IP를 가지고 있으며 차단되는 일이 드뭅니다.
  • SEO 파싱 (구글, 야ндекс) — 필요한 지역에 지리적 연관성이 있는 주거용 프록시를 사용합니다.

Python에서 프록시 설정하기 (requests, aiohttp)

Python은 파싱 및 자동화 작업을 위한 Cloud Functions에서 가장 인기 있는 언어입니다. requests (동기 요청) 및 aiohttp (비동기 요청) 라이브러리와의 프록시 통합을 살펴보겠습니다.

requests 라이브러리 예제 (HTTP 프록시)

import requests
import os

def parse_with_proxy(request):
    # 환경 변수에서 프록시 데이터 가져오기
    proxy_host = os.environ.get('PROXY_HOST', 'proxy.example.com')
    proxy_port = os.environ.get('PROXY_PORT', '8080')
    proxy_user = os.environ.get('PROXY_USER', 'username')
    proxy_pass = os.environ.get('PROXY_PASS', 'password')
    
    # 인증이 포함된 프록시 URL 형성
    proxy_url = f"http://{proxy_user}:{proxy_pass}@{proxy_host}:{proxy_port}"
    
    proxies = {
        'http': proxy_url,
        'https': proxy_url
    }
    
    try:
        # 프록시를 통해 타임아웃과 함께 요청하기
        response = requests.get(
            'https://api.example.com/data',
            proxies=proxies,
            timeout=10,
            headers={'User-Agent': 'Mozilla/5.0'}
        )
        
        # 응답 상태 확인
        response.raise_for_status()
        
        return {
            'statusCode': 200,
            'body': response.json(),
            'ip_used': response.headers.get('X-Forwarded-For', 'unknown')
        }
        
    except requests.exceptions.ProxyError as e:
        return {'statusCode': 502, 'error': f'프록시 오류: {str(e)}'}
    except requests.exceptions.Timeout:
        return {'statusCode': 504, 'error': '요청 타임아웃'}
    except requests.exceptions.RequestException as e:
        return {'statusCode': 500, 'error': f'요청 실패: {str(e)}'}

중요한 점:

  • 환경 변수 — 프록시 데이터(호스트, 포트, 로그인, 비밀번호)를 코드에 저장하지 말고 Secret Manager 또는 Cloud Functions의 환경 변수에 저장하세요.
  • 타임아웃 — 프록시 문제로 함수가 멈추지 않도록 timeout을 반드시 설정하세요.
  • User-Agent — 요청이 실제 브라우저에서 발생한 것처럼 보이도록 User-Agent 헤더를 추가하세요.
  • 오류 처리 — ProxyError(프록시 문제)와 Timeout(느린 프록시)를 별도로 처리하세요.

aiohttp 예제 (비동기 요청)

고부하 작업(예: 1000페이지 이상 파싱)에는 aiohttp를 사용하여 비동기 요청을 사용하세요:

import aiohttp
import asyncio
import os

async def fetch_with_proxy(url, proxy_url):
    async with aiohttp.ClientSession() as session:
        try:
            async with session.get(
                url,
                proxy=proxy_url,
                timeout=aiohttp.ClientTimeout(total=10),
                headers={'User-Agent': 'Mozilla/5.0'}
            ) as response:
                return await response.text()
        except aiohttp.ClientProxyConnectionError:
            return {'error': '프록시 연결 실패'}
        except asyncio.TimeoutError:
            return {'error': '요청 타임아웃'}

def parse_multiple_urls(request):
    proxy_url = f"http://{os.environ['PROXY_USER']}:{os.environ['PROXY_PASS']}@{os.environ['PROXY_HOST']}:{os.environ['PROXY_PORT']}"
    
    urls = [
        'https://example.com/page1',
        'https://example.com/page2',
        'https://example.com/page3'
    ]
    
    # 비동기 요청을 병렬로 실행
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    
    tasks = [fetch_with_proxy(url, proxy_url) for url in urls]
    results = loop.run_until_complete(asyncio.gather(*tasks))
    
    return {'statusCode': 200, 'results': results}

비동기 접근 방식은 프록시를 통해 10-100개의 병렬 요청을 가능하게 하여 Cloud Functions의 제한된 실행 시간(최대 9분) 내에서 대량의 데이터를 파싱하는 데 필수적입니다.

SOCKS5 프록시 작업하기

일부 프록시 제공업체는 UDP 트래픽의 더 안정적인 작업이나 차단 우회를 위해 SOCKS5 프록시를 제공합니다. Python에서 SOCKS5를 사용하려면 requests[socks] 라이브러리를 사용하세요:

# requirements.txt에 추가:
# requests[socks]

import requests

def use_socks5_proxy(request):
    proxy_url = f"socks5://{os.environ['PROXY_USER']}:{os.environ['PROXY_PASS']}@{os.environ['PROXY_HOST']}:{os.environ['PROXY_PORT']}"
    
    proxies = {
        'http': proxy_url,
        'https': proxy_url
    }
    
    response = requests.get(
        'https://api.ipify.org?format=json',
        proxies=proxies,
        timeout=10
    )
    
    return {'statusCode': 200, 'ip': response.json()}

Node.js에서 프록시 설정하기 (axios, node-fetch)

Node.js는 Cloud Functions에서 두 번째로 인기 있는 언어입니다. axiosnode-fetch 라이브러리와의 프록시 통합을 살펴보겠습니다.

axios 예제

const axios = require('axios');
const HttpsProxyAgent = require('https-proxy-agent');

exports.parseWithProxy = async (req, res) => {
  const proxyUrl = `http://${process.env.PROXY_USER}:${process.env.PROXY_PASS}@${process.env.PROXY_HOST}:${process.env.PROXY_PORT}`;
  
  const agent = new HttpsProxyAgent(proxyUrl);
  
  try {
    const response = await axios.get('https://api.example.com/data', {
      httpsAgent: agent,
      timeout: 10000,
      headers: {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
      }
    });
    
    res.status(200).json({
      success: true,
      data: response.data,
      proxyUsed: proxyUrl.split('@')[1] // 비밀번호 없이 호스트:포트 반환
    });
    
  } catch (error) {
    if (error.code === 'ECONNREFUSED') {
      res.status(502).json({ error: '프록시 연결 거부됨' });
    } else if (error.code === 'ETIMEDOUT') {
      res.status(504).json({ error: '프록시 타임아웃' });
    } else {
      res.status(500).json({ error: error.message });
    }
  }
};

package.json의 의존성:

{
  "dependencies": {
    "axios": "^1.6.0",
    "https-proxy-agent": "^7.0.2"
  }
}

node-fetch 및 SOCKS5 예제

const fetch = require('node-fetch');
const { SocksProxyAgent } = require('socks-proxy-agent');

exports.fetchWithSocks5 = async (req, res) => {
  const proxyUrl = `socks5://${process.env.PROXY_USER}:${process.env.PROXY_PASS}@${process.env.PROXY_HOST}:${process.env.PROXY_PORT}`;
  
  const agent = new SocksProxyAgent(proxyUrl);
  
  try {
    const response = await fetch('https://api.ipify.org?format=json', {
      agent,
      timeout: 10000
    });
    
    const data = await response.json();
    
    res.status(200).json({
      success: true,
      yourIP: data.ip
    });
    
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
};

SOCKS5 의존성:

{
  "dependencies": {
    "node-fetch": "^2.7.0",
    "socks-proxy-agent": "^8.0.2"
  }
}

프록시 인증: 로그인/비밀번호 및 IP 화이트리스트

프록시 작업 시 두 가지 주요 인증 방법이 있습니다:

1. 로그인 및 비밀번호 인증

가장 일반적인 방법은 프록시 URL에 자격 증명을 전달하는 것입니다:

http://username:password@proxy.example.com:8080

장점: 설정이 간단하고 고정 IP 출처가 필요하지 않습니다.

단점: 자격 증명이 각 요청에 전달되므로 약간의 오버헤드가 발생합니다.

2. IP 화이트리스트 인증

일부 제공업체는 Cloud Functions의 IP 주소를 화이트리스트에 추가할 수 있게 해줍니다. 문제는 Cloud Functions가 Google Cloud의 동적 IP를 사용한다는 것입니다.

해결책: Cloud NAT를 사용하여 정적 외부 IP를 통해 아웃바운드 트래픽을 라우팅합니다:

  1. Google Cloud에서 VPC 네트워크 및 서브넷을 생성합니다.
  2. 정적 IP 예약으로 Cloud NAT를 설정합니다.
  3. Cloud Functions를 VPC 커넥터에 연결합니다.
  4. 프록시 제공업체의 화이트리스트에 정적 IP를 추가합니다.

프록시 설정 후 로그인 및 비밀번호가 필요하지 않습니다:

proxies = {
    'http': 'http://proxy.example.com:8080',
    'https': 'http://proxy.example.com:8080'
}

추천: 대부분의 경우 로그인/비밀번호 인증을 사용하는 것이 더 간단하며 Cloud NAT에 대한 추가 비용(시간당 $0.044 + 트래픽)이 발생하지 않습니다.

IP 회전 및 프록시 풀 관리

대량의 데이터를 파싱할 때는 차단을 피하기 위해 IP 회전을 사용하는 것이 중요합니다. 여러 가지 접근 방식이 있습니다:

1. 제공업체 측 회전 (Rotating Proxies)

많은 제공업체가 회전 프록시를 제공합니다 — 요청 시 또는 타이머에 따라 IP를 자동으로 변경하는 단일 엔드포인트:

# 하나의 엔드포인트, IP가 자동으로 변경됨
proxy_url = "http://username:password@rotating.proxy.com:8080"

# 각 요청이 새로운 IP에서 발생함
for i in range(100):
    response = requests.get('https://api.ipify.org', proxies={'http': proxy_url})
    print(f"요청 {i}: IP = {response.text}")

장점: 프록시 풀을 수동으로 관리할 필요가 없으며, 간단한 통합이 가능합니다.

단점: 특정 IP에 대한 제어가 없으며, 비용이 더 비쌀 수 있습니다.

2. 수동으로 프록시 풀 관리

정적 프록시 목록이 있는 경우 코드 수준에서 회전을 구현하세요:

import random
import requests

# 프록시 풀 (Secret Manager에서 로드할 수 있음)
PROXY_POOL = [
    "http://user:pass@proxy1.example.com:8080",
    "http://user:pass@proxy2.example.com:8080",
    "http://user:pass@proxy3.example.com:8080",
]

def get_random_proxy():
    return random.choice(PROXY_POOL)

def parse_with_rotation(urls):
    results = []
    
    for url in urls:
        proxy = get_random_proxy()
        
        try:
            response = requests.get(
                url,
                proxies={'http': proxy, 'https': proxy},
                timeout=10
            )
            results.append({
                'url': url,
                'status': response.status_code,
                'proxy': proxy.split('@')[1]
            })
        except Exception as e:
            # 프록시가 작동하지 않으면 다른 프록시로 시도
            proxy = get_random_proxy()
            response = requests.get(url, proxies={'http': proxy, 'https': proxy})
            results.append({'url': url, 'status': response.status_code})
    
    return results

3. 세션 기반 프록시 (sticky sessions)

세션 내에서 하나의 IP를 유지해야 하는 작업(예: 웹사이트 로그인)에는 프록시 URL에 세션 ID를 사용하세요:

# 로그인에 세션 ID 추가
import uuid

session_id = str(uuid.uuid4())
proxy_url = f"http://username-session-{session_id}:password@proxy.example.com:8080"

# 이 session_id로 모든 요청이 하나의 IP를 통해 진행됨
session = requests.Session()
session.proxies = {'http': proxy_url, 'https': proxy_url}

# 로그인
session.post('https://example.com/login', data={'user': 'test', 'pass': '123'})

# 같은 세션 내에서의 후속 요청
session.get('https://example.com/dashboard')

오류 처리 및 타임아웃

Cloud Functions에서 프록시 작업 시 오류를 올바르게 처리하는 것이 중요하여 데이터를 잃지 않고 실행 시간 제한을 초과하지 않도록 합니다.

오류 유형 및 처리 방법

오류 원인 해결책
ProxyError 프록시가 사용할 수 없거나 잘못된 자격 증명 프록시 풀에서 다른 프록시로 전환
Timeout 느린 프록시 또는 과부하된 서버 5-10초의 타임아웃 설정, 다른 IP로 재시도
407 Proxy Authentication Required 잘못된 로그인/비밀번호 환경 변수에서 자격 증명 확인
429 Too Many Requests 대상 웹사이트에서 속도 제한 요청 간 지연 추가, 더 많은 IP 사용
403 Forbidden 프록시 IP가 웹사이트에 의해 차단됨 IP 변경, 데이터 센터 대신 주거용 사용

복합 오류 처리 예제

import requests
import time
from requests.exceptions import ProxyError, Timeout, RequestException

def fetch_with_retry(url, proxy_pool, max_retries=3):
    """
    자동 재시도 및 오류 발생 시 프록시 변경 요청
    """
    for attempt in range(max_retries):
        proxy = random.choice(proxy_pool)
        
        try:
            response = requests.get(
                url,
                proxies={'http': proxy, 'https': proxy},
                timeout=10,
                headers={'User-Agent': 'Mozilla/5.0'}
            )
            
            # 상태 코드 확인
            if response.status_code == 200:
                return {'success': True, 'data': response.text, 'proxy': proxy}
            elif response.status_code == 429:
                # 속도 제한 — 대기 후 재시도
                time.sleep(2 ** attempt)  # 지수 백오프
                continue
            elif response.status_code == 403:
                # IP 차단 — 프록시 변경
                continue
            else:
                return {'success': False, 'status': response.status_code}
                
        except ProxyError:
            # 프록시 작동하지 않음 — 다음으로 시도
            print(f"프록시 {proxy} 실패, 다른 것으로 시도...")
            continue
        except Timeout:
            # 타임아웃 — 다른 프록시로 시도
            print(f"{proxy}에서 타임아웃, 재시도...")
            continue
        except RequestException as e:
            # 기타 오류
            print(f"요청 실패: {e}")
            if attempt == max_retries - 1:
                return {'success': False, 'error': str(e)}
            continue
    
    return {'success': False, 'error': '최대 재시도 초과'}

Cloud Functions에서 타임아웃 설정

Cloud Functions는 실행 시간 제한이 있습니다(기본 60초, 최대 540초). 프록시 타임아웃 설정 시 이를 고려하세요:

  • Connection timeout — 프록시와의 연결을 설정하는 데 걸리는 시간(5초 권장)
  • Read timeout — 프록시를 통해 대상 서버의 응답을 받는 데 걸리는 시간(10-15초 권장)
  • Total timeout — 전체 요청에 대한 총 시간(함수의 타임아웃보다 작아야 함)
# Python: 개별 타임아웃
response = requests.get(
    url,
    proxies=proxies,
    timeout=(5, 15)  # (연결 타임아웃, 읽기 타임아웃)
)

# Node.js에서 axios
const response = await axios.get(url, {
  httpsAgent: agent,
  timeout: 10000  // 밀리초 단위의 총 타임아웃
});

모범 사례 및 성능 최적화

Cloud Functions에서 프록시를 효과적으로 사용하기 위한 권장 사항:

1. 자격 증명에 환경 변수 사용

프록시의 로그인 및 비밀번호를 코드에 저장하지 마세요. Secret Manager 또는 환경 변수를 사용하세요:

# Google Cloud에서 비밀 생성
gcloud secrets create proxy-credentials \
    --data-file=proxy-config.json

# Cloud Functions에 접근 권한 부여
gcloud secrets add-iam-policy-binding proxy-credentials \
    --member=serviceAccount:PROJECT_ID@appspot.gserviceaccount.com \
    --role=roles/secretmanager.secretAccessor
# 코드에서 비밀 읽기
from google.cloud import secretmanager
import json

def get_proxy_config():
    client = secretmanager.SecretManagerServiceClient()
    name = f"projects/{PROJECT_ID}/secrets/proxy-credentials/versions/latest"
    response = client.access_secret_version(request={"name": name})
    return json.loads(response.payload.data.decode('UTF-8'))

2. 파싱 결과 캐시하기

Cloud Storage 또는 Firestore를 사용하여 데이터를 캐시하여 프록시를 통해 반복 요청을 하지 않도록 합니다:

import hashlib
from google.cloud import storage

def fetch_with_cache(url, proxy):
    # URL을 기반으로 캐시 키 생성
    cache_key = hashlib.md5(url.encode()).hexdigest()
    
    # Cloud Storage에서 캐시 확인
    bucket = storage.Client().bucket('my-cache-bucket')
    blob = bucket.blob(f"cache/{cache_key}.json")
    
    if blob.exists():
        # 캐시된 데이터 반환
        return json.loads(blob.download_as_text())
    
    # 프록시를 통해 요청
    response = requests.get(url, proxies={'http': proxy})
    data = response.json()
    
    # 캐시에 저장
    blob.upload_from_string(json.dumps(data))
    
    return data

3. 모니터링 및 로깅

Cloud Logging을 통해 프록시 성능 및 오류 빈도를 모니터링하세요:

import logging
import time

def fetch_with_logging(url, proxy):
    start_time = time.time()
    
    try:
        response = requests.get(url, proxies={'http': proxy}, timeout=10)
        duration = time.time() - start_time
        
        logging.info({
            'url': url,
            'proxy': proxy.split('@')[1],
            'status': response.status_code,
            'duration': duration,
            'success': True
        })
        
        return response
        
    except Exception as e:
        duration = time.time() - start_time
        
        logging.error({
            'url': url,
            'proxy': proxy.split('@')[1],
            'error': str(e),
            'duration': duration,
            'success': False
        })
        
        raise

4. 콜드 스타트 최적화

Cloud Functions는 콜드 스타트 지연이 있습니다. 의존성을 최소화하고 라이브러리의 최소 버전을 사용하세요:

# requirements.txt — 필요한 라이브러리만
requests==2.31.0
# 중요하지 않은 경우 pandas와 같은 무거운 라이브러리는 피하세요

연결 재사용을 위해 전역 변수를 사용하세요:

# 콜드 스타트 시 한 번 세션 생성
session = requests.Session()
session.proxies = {'http': PROXY_URL, 'https': PROXY_URL}

def parse_data(request):
    # 호출 간 세션 재사용
    response = session.get('https://api.example.com/data')
    return response.json()

5. 지리적 타겟팅 프록시

지리적 타겟팅 작업(예: 지역 가격 파싱)에는 특정 국가 또는 도시와 연결된 프록시를 사용하세요:

# 국가를 로그인에 지정할 수 있는 주거용 프록시 예제
proxy_url = f"http://username-country-ru:password@proxy.example.com:8080"

# 또는 국가별로 다른 엔드포인트 사용
PROXIES_BY_COUNTRY = {
    'RU': 'http://user:pass@ru.proxy.example.com:8080',
    'US': 'http://user:pass@us.proxy.example.com:8080',
    'DE': 'http://user:pass@de.proxy.example.com:8080'
}

def parse_by_country(country_code):
    proxy = PROXIES_BY_COUNTRY.get(country_code)
    response = requests.get('https://example.com', proxies={'http': proxy})
    return response.text

결론

Google Cloud Functions와 프록시 통합은 IP 제한 없이 파싱, 자동화 및 API 작업을 위한 광범위한 가능성을 열어줍니다. 고려해야 할 주요 사항은 오류를 올바르게 처리하고 재시도 로직을 구현하며, 멈춤을 방지하기 위해 타임아웃을 설정하고, 차단을 피하기 위해 IP를 회전시키며, Secret Manager에 자격 증명을 안전하게 저장하는 것입니다.

대부분의 파싱 및 자동화 작업에 대해 주거용 프록시가 최적의 선택이 될 것입니다 — 실제 사용자의 IP를 사용하여 높은 익명성과 낮은 차단 비율을 보장합니다. 소셜 미디어 및 모바일 애플리케이션 작업에는 인스타그램 및 틱톡과 같은 플랫폼에서 거의 차단되지 않는 모바일 운영자의 IP를 가진 모바일 프록시를 추천합니다.

프록시와 함께 Cloud Functions를 올바르게 설정하면 인프라를 관리할 필요 없이 대량의 데이터를 처리하기 위한 확장 가능하고 경제적인 솔루션을 얻을 수 있습니다.

```