파서를 작성하거나 데이터 수집을 자동화하거나 IP 제한을 우회하려면 PHP에서 프록시가 필요합니다. 이 가이드에서는 내장된 cURL과 인기 있는 라이브러리 Guzzle HTTP Client의 두 가지 주요 도구를 다루며, 즉시 프로젝트에서 사용할 수 있는 코드 예제를 제공합니다.
PHP 프로젝트에서 프록시가 필요한 이유
PHP는 서버 자동화에 가장 널리 사용되는 언어 중 하나이며, 프록시가 필요한 작업이 자주 발생합니다. 다음은 프록시가 필요한 주요 시나리오입니다:
- 웹사이트 및 마켓플레이스 파싱 — Wildberries, Ozon, Avito, AliExpress는 몇십 개의 요청 후 IP를 차단합니다. 프록시는 다양한 주소 간에 부하를 분산시켜 403/429 오류를 피할 수 있게 해줍니다.
- 지리적 의존성이 있는 리소스에서 데이터 수집 — 가격, 검색 엔진 결과, 콘텐츠는 국가에 따라 다를 수 있습니다. 필요한 지리적 위치의 프록시가 이 문제를 해결합니다.
- 요금 제한 우회 — 많은 API는 하나의 IP에서 요청 수를 제한합니다. 프록시 회전은 이러한 제한을 우회할 수 있게 해줍니다.
- 지리적 의존성이 있는 콘텐츠 테스트 — 사무실을 나가지 않고도 다른 국가의 사용자에게 사이트가 어떻게 보이는지 확인합니다.
- 자동화 시 익명성 유지 — 외부 리소스에 대한 대량 요청 시 서버의 실제 IP를 숨깁니다.
- 경쟁사 모니터링 — 경쟁사의 사이트에서 가격, 품목, 프로모션을 정기적으로 수집하여 차단 위험을 줄입니다.
PHP에서 HTTP 요청을 수행할 때 주로 두 가지 도구를 사용합니다: 내장 확장 cURL과 라이브러리 Guzzle HTTP Client. 두 도구 모두 HTTP, HTTPS, SOCKS4 및 SOCKS5 프록시를 지원합니다 — 각 도구를 자세히 살펴보겠습니다.
PHP에 적합한 프록시 유형 선택하기
코드를 작성하기 전에 어떤 유형의 프록시가 귀하의 작업에 적합한지 이해하는 것이 중요합니다. 다양한 유형은 속도, 신뢰성 및 익명성 수준에 따라 다릅니다.
| 프록시 유형 | 속도 | 익명성 | 최적의 용도 |
|---|---|---|---|
| 데이터 센터 프록시 | 매우 높음 | 중간 | 엄격한 안티봇 시스템이 없는 파싱, API 요청 |
| 거주지 프록시 | 중간 | 높음 | 보호된 사이트 파싱, 마켓플레이스, 지리 데이터 |
| 모바일 프록시 | 중간 | 최고 | 엄격한 안티봇 보호가 있는 사이트, 소셜 미디어 |
대부분의 마켓플레이스에서 데이터를 파싱하는 작업에는 거주지 프록시가 최적의 선택이 될 것입니다 — 이들의 IP는 실제 가정 사용자에게 속해 있어 요청이 일반 브라우저 트래픽과 거의 구별되지 않습니다. 데이터 센터 프록시는 속도가 더 중요하고 보호가 약한 곳에 적합합니다.
세 가지 유형의 프록시 모두 HTTP/HTTPS 및 SOCKS5 프로토콜을 지원하므로 코드 측면에서 설정은 동일합니다. 차이점은 연결 문자열에만 있습니다.
cURL에서의 프록시: 기본 설정
cURL 확장은 PHP에서 기본적으로 제공되며 서버에서 HTTP 요청을 수행하는 표준 방법입니다. 프록시를 연결하기 위해서는 두 가지 주요 옵션인 CURLOPT_PROXY와 CURLOPT_PROXYTYPE를 사용합니다.
인증 없는 HTTP 프록시
<?php
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => 'https://httpbin.org/ip',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_TIMEOUT => 30,
// 프록시 주소 지정
CURLOPT_PROXY => '185.199.100.1:8080',
// 프록시 유형: HTTP (기본값)
CURLOPT_PROXYTYPE => CURLPROXY_HTTP,
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
echo "HTTP 코드: $httpCode\n";
echo $response;
여기서 CURLOPT_PROXY는 host:port 형식의 문자열을 받습니다. HTTPS 트래픽의 경우 cURL은 자동으로 CONNECT 방법을 사용하여 요청 내용을 암호화된 상태로 유지합니다.
프록시 작업을 위한 유용한 cURL 옵션
| 옵션 | 설명 |
|---|---|
CURLOPT_PROXY |
프록시 서버 주소 (host:port) |
CURLOPT_PROXYTYPE |
유형: CURLPROXY_HTTP, CURLPROXY_SOCKS4, CURLPROXY_SOCKS5 |
CURLOPT_PROXYUSERPWD |
로그인 및 비밀번호 (user:password 형식) |
CURLOPT_HTTPPROXYTUNNEL |
HTTP CONNECT를 통한 터널링 활성화 |
CURLOPT_SSL_VERIFYPEER |
SSL 인증서 검증 (false — 비활성화) |
CURLOPT_TIMEOUT |
요청 타임아웃 (초 단위) |
CURLOPT_CONNECTTIMEOUT |
프록시 연결 타임아웃 |
cURL에서의 인증 및 SOCKS5
대부분의 상업용 프록시는 로그인 및 비밀번호 인증을 요구합니다. 또한 SOCKS5는 완전한 익명성이 필요할 때 선호되는 프로토콜입니다. 왜냐하면 SOCKS5는 프록시 사용을 드러낼 수 있는 X-Forwarded-For와 같은 헤더를 추가하지 않기 때문입니다.
로그인 및 비밀번호가 있는 HTTP 프록시
<?php
$proxyHost = '185.199.100.1';
$proxyPort = '8080';
$proxyUser = 'your_login';
$proxyPass = 'your_password';
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => 'https://httpbin.org/ip',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_CONNECTTIMEOUT => 10,
// 인증이 있는 프록시
CURLOPT_PROXY => "$proxyHost:$proxyPort",
CURLOPT_PROXYTYPE => CURLPROXY_HTTP,
CURLOPT_PROXYUSERPWD => "$proxyUser:$proxyPass",
// HTTPS를 위한 터널링
CURLOPT_HTTPPROXYTUNNEL => true,
// 브라우저를 가장하기 위한 헤더
CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
CURLOPT_HTTPHEADER => [
'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language: ru-RU,ru;q=0.9,en;q=0.8',
],
]);
$response = curl_exec($ch);
if (curl_errno($ch)) {
echo 'cURL 오류: ' . curl_error($ch);
} else {
echo $response;
}
curl_close($ch);
cURL에서의 SOCKS5 프록시
<?php
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => 'https://httpbin.org/ip',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_PROXY => '185.199.100.1:1080',
// DNS를 프록시를 통해 해결하는 SOCKS5 (권장!)
CURLOPT_PROXYTYPE => CURLPROXY_SOCKS5_HOSTNAME,
// 인증 (필요한 경우)
CURLOPT_PROXYUSERPWD => 'login:password',
]);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
💡 중요: SOCKS5 vs SOCKS5_HOSTNAME
CURLPROXY_SOCKS5_HOSTNAME를 사용하세요. CURLPROXY_SOCKS5 대신에. SOCKS5_HOSTNAME을 사용할 경우 DNS 요청도 프록시를 통해 전달되므로 DNS 유출을 방지하고 익명성을 높일 수 있습니다. 일반 SOCKS5는 로컬에서 DNS를 해결하므로 실제 IP가 드러날 수 있습니다.
cURL에서의 프록시 회전
하나의 프록시는 대량 요청 시 빠르게 차단될 수 있습니다. 올바른 전략은 프록시 풀을 가지고 이를 교대로 사용하는 것입니다. 다음은 간단하지만 효과적인 구현입니다:
<?php
class ProxyRotator
{
private array $proxies;
private int $currentIndex = 0;
public function __construct(array $proxies)
{
$this->proxies = $proxies;
shuffle($this->proxies); // 무작위 순서를 위해 섞기
}
/**
* 풀에서 다음 프록시 가져오기
*/
public function getNext(): string
{
$proxy = $this->proxies[$this->currentIndex];
$this->currentIndex = ($this->currentIndex + 1) % count($this->proxies);
return $proxy;
}
/**
* 무작위 프록시 가져오기
*/
public function getRandom(): string
{
return $this->proxies[array_rand($this->proxies)];
}
}
// login:password@host:port 형식의 프록시 목록
$proxyList = [
'user1:[email protected]:8080',
'user2:[email protected]:8080',
'user3:[email protected]:8080',
'user4:[email protected]:8080',
];
$rotator = new ProxyRotator($proxyList);
/**
* 자동으로 프록시를 선택하여 요청하는 함수
*/
function fetchWithProxy(string $url, ProxyRotator $rotator): ?string
{
$proxyStr = $rotator->getNext();
// 프록시 문자열 파싱
preg_match('/^(.+):(.+)@(.+):(\d+)$/', $proxyStr, $m);
[, $user, $pass, $host, $port] = $m;
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 20,
CURLOPT_CONNECTTIMEOUT => 8,
CURLOPT_PROXY => "$host:$port",
CURLOPT_PROXYTYPE => CURLPROXY_HTTP,
CURLOPT_PROXYUSERPWD => "$user:$pass",
CURLOPT_USERAGENT => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)',
CURLOPT_SSL_VERIFYPEER => false,
]);
$response = curl_exec($ch);
$error = curl_error($ch);
curl_close($ch);
if ($error) {
error_log("프록시 $host:$port에 접근할 수 없습니다: $error");
return null;
}
return $response;
}
// 예시: 프록시 회전을 통해 10페이지 파싱
$urls = [
'https://example.com/page/1',
'https://example.com/page/2',
'https://example.com/page/3',
// ...
];
foreach ($urls as $url) {
$html = fetchWithProxy($url, $rotator);
if ($html) {
echo "받은 데이터: " . strlen($html) . " 바이트\n";
}
usleep(500000); // 요청 간 0.5초 대기
}
usleep(500000)에 주목하세요 — 요청 간 대기는 매우 중요합니다. 프록시 회전이 있더라도 너무 잦은 요청은 행동 패턴으로 인해 차단될 수 있습니다. 권장 간격은 사이트에 따라 500ms에서 2초입니다.
Guzzle HTTP Client에서의 프록시: 기본 설정
Guzzle는 HTTP 요청을 위한 강력한 PHP 라이브러리로, 대부분의 현대 PHP 프레임워크(예: Laravel, Symfony)에서 사용됩니다. Guzzle은 기본 cURL에 비해 더 편리하고 읽기 쉬운 API를 제공하며, 비동기 요청, 미들웨어 및 오류 처리를 쉽게 할 수 있습니다.
Composer를 통한 설치
composer require guzzlehttp/guzzle
인증 없는 HTTP 프록시
<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
$client = new Client([
// 기본 URL (선택 사항)
'base_uri' => 'https://httpbin.org',
// 모든 요청에 대한 기본 설정
'timeout' => 30,
'connect_timeout' => 10,
// 클라이언트의 모든 요청에 대한 프록시
'proxy' => 'http://185.199.100.1:8080',
]);
$response = $client->get('/ip');
echo $response->getStatusCode() . "\n"; // 200
echo $response->getBody() . "\n"; // {"origin": "185.199.100.1"}
Guzzle에서 프록시는 URL 형식의 proxy 옵션을 통해 설정됩니다. 이는 cURL보다 더 직관적입니다. 프록시는 클라이언트 수준(모든 요청에 대해) 또는 각 요청별로 개별적으로 설정할 수 있습니다.
인증이 있는 HTTP 프록시
<?php
use GuzzleHttp\Client;
$login = 'your_login';
$password = 'your_password';
$host = '185.199.100.1';
$port = '8080';
$client = new Client([
'timeout' => 30,
]);
// 인증이 있는 프록시는 URL 형식으로 전달됩니다.
$response = $client->get('https://httpbin.org/ip', [
'proxy' => "http://$login:$password@$host:$port",
'headers' => [
'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9',
],
]);
echo $response->getBody();
Guzzle에서 프록시 URL 형식: scheme://user:password@host:port. HTTP 프록시에는 http://를 사용하고, SOCKS5에는 socks5://를 사용합니다.
Guzzle의 고급 작업: 타임아웃, 헤더, SOCKS5
더 복잡한 시나리오를 살펴보겠습니다: HTTP 및 HTTPS에 대한 다양한 프록시, SOCKS5, SSL 검증 비활성화 및 브라우저 모방을 위한 헤더 설정.
HTTP 및 HTTPS에 대한 다양한 프록시
<?php
use GuzzleHttp\Client;
$client = new Client([
'timeout' => 30,
'proxy' => [
// HTTP 요청에 대한 프록시
'http' => 'http://login:[email protected]:8080',
// HTTPS 요청에 대한 프록시
'https' => 'http://login:[email protected]:8080',
// 예외 — 이 호스트들은 프록시 없이 접근
'no' => ['localhost', '127.0.0.1', '.internal.corp'],
],
]);
Guzzle에서의 SOCKS5
<?php
use GuzzleHttp\Client;
// Guzzle에서 SOCKS5를 사용하려면 추가 패키지가 필요합니다.
// composer require clue/socks-react (또는 curl 핸들러 사용)
$client = new Client([
'timeout' => 30,
'proxy' => 'socks5://login:[email protected]:1080',
'curl' => [
CURLOPT_PROXYTYPE => CURLPROXY_SOCKS5_HOSTNAME,
],
]);
$response = $client->get('https://httpbin.org/ip');
echo $response->getBody();
브라우저 헤더가 포함된 전체 구성
<?php
use GuzzleHttp\Client;
use GuzzleHttp\RequestOptions;
$client = new Client([
'timeout' => 30,
'connect_timeout' => 10,
'verify' => false, // SSL 검증 비활성화 (디버깅 용도)
'allow_redirects' => [
'max' => 5,
'strict' => false,
'referer' => true,
'protocols' => ['http', 'https'],
],
'headers' => [
'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language' => 'ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7',
'Accept-Encoding' => 'gzip, deflate, br',
'Cache-Control' => 'no-cache',
'Connection' => 'keep-alive',
],
]);
$response = $client->get('https://example.com/catalog', [
'proxy' => 'http://login:[email protected]:8080',
]);
$statusCode = $response->getStatusCode();
$body = (string) $response->getBody();
echo "상태: $statusCode, 크기: " . strlen($body) . " 바이트\n";
⚠️ verify => false에 대한 주의
SSL 검증 비활성화 ('verify' => false)는 디버깅 또는 내부 서비스 작업 시에만 허용됩니다. 프로덕션 코드에서는 항상 SSL 검증을 활성화하여 중간자 공격에 취약하지 않도록 해야 합니다.
Guzzle에서의 프록시 회전
Guzzle에서 프록시 회전은 미들웨어를 통해 쉽게 구현할 수 있습니다. 미들웨어는 각 요청을 가로채고 프록시를 추가하는 중간 계층입니다. 이는 각 호출에 프록시를 수동으로 전달하는 것보다 더 우아합니다.
<?php
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use Psr\Http\Message\RequestInterface;
/**
* 프록시 자동 회전을 위한 미들웨어
*/
function proxyRotationMiddleware(array $proxies): callable
{
$index = 0;
$total = count($proxies);
return Middleware::mapRequest(
function (RequestInterface $request) use ($proxies, &$index, $total) {
$proxy = $proxies[$index % $total];
$index++;
// 요청의 속성에 프록시 추가
// (핸들러의 옵션을 통해 전달)
return $request->withHeader('X-Selected-Proxy', $proxy);
}
);
}
// 프록시 목록
$proxies = [
'http://user1:[email protected]:8080',
'http://user2:[email protected]:8080',
'http://user3:[email protected]:8080',
];
// Guzzle을 감싸는 간단한 클래스
class GuzzleWithRotation
{
private Client $client;
private array $proxies;
private int $index = 0;
public function __construct(array $proxies, array $clientConfig = [])
{
$this->proxies = $proxies;
$this->client = new Client($clientConfig);
}
public function get(string $url, array $options = []): \GuzzleHttp\Psr7\Response
{
$options['proxy'] = $this->proxies[$this->index % count($this->proxies)];
$this->index++;
return $this->client->get($url, $options);
}
public function post(string $url, array $options = []): \GuzzleHttp\Psr7\Response
{
$options['proxy'] = $this->proxies[$this->index % count($this->proxies)];
$this->index++;
return $this->client->post($url, $options);
}
}
// 사용 예시
$guzzle = new GuzzleWithRotation($proxies, [
'timeout' => 30,
'headers' => [
'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)',
],
]);
$urls = [
'https://example.com/product/1',
'https://example.com/product/2',
'https://example.com/product/3',
];
foreach ($urls as $url) {
try {
$response = $guzzle->get($url);
echo "OK [{$response->getStatusCode()}]: $url\n";
sleep(1);
} catch (\Exception $e) {
echo "오류: " . $e->getMessage() . "\n";
}
}
오류 처리 및 디버깅
프록시를 사용할 때 오류는 불가피합니다: 프록시가 사용할 수 없거나 응답이 느리거나 대상 사이트가 오류를 반환할 수 있습니다. 이러한 모든 상황을 적절하게 처리하는 것이 중요합니다.
cURL에서의 오류 처리
<?php
function fetchWithErrorHandling(string $url, string $proxy): array
{
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 20,
CURLOPT_CONNECTTIMEOUT => 8,
CURLOPT_PROXY => $proxy,
CURLOPT_PROXYTYPE => CURLPROXY_HTTP,
]);
$body = curl_exec($ch);
$errno = curl_errno($ch);
$error = curl_error($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$totalTime = curl_getinfo($ch, CURLINFO_TOTAL_TIME);
curl_close($ch);
// 프록시에 대한 cURL 오류 코드
$proxyErrors = [
CURLE_COULDNT_CONNECT => '프록시에 연결할 수 없습니다',
CURLE_OPERATION_TIMEDOUT => '프록시 연결 타임아웃',
CURLE_RECV_ERROR => '데이터 수신 오류',
CURLE_SSL_CONNECT_ERROR => '프록시를 통한 SSL 오류',
];
if ($errno) {
$message = $proxyErrors[$errno] ?? "cURL 오류 #$errno: $error";
return ['success' => false, 'error' => $message, 'code' => $errno];
}
if ($httpCode >= 400) {
return ['success' => false, 'error' => "HTTP $httpCode", 'code' => $httpCode];
}
return [
'success' => true,
'body' => $body,
'code' => $httpCode,
'time' => round($totalTime, 3),
];
}
// 재시도를 포함한 사용 예시
function fetchWithRetry(string $url, array $proxies, int $maxRetries = 3): ?string
{
foreach ($proxies as $proxy) {
for ($attempt = 1; $attempt <= $maxRetries; $attempt++) {
$result = fetchWithErrorHandling($url, $proxy);
if ($result['success']) {
echo "$proxy를 통해 성공 (시도 $attempt, {$result['time']}초)\n";
return $result['body'];
}
echo "$proxy를 통해 오류 발생: {$result['error']}\n";
if ($attempt < $maxRetries) {
sleep(2); // 재시도 전 대기
}
}
}
return null; // 모든 프록시가 실패함
}
Guzzle에서의 오류 처리
<?php
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Exception\ServerException;
use GuzzleHttp\Exception\ClientException;
$client = new Client(['timeout' => 30]);
$proxies = [
'http://user1:[email protected]:8080',
'http://user2:[email protected]:8080',
];
function fetchGuzzleWithFallback(Client $client, string $url, array $proxies): ?string
{
foreach ($proxies as $proxy) {
try {
$response = $client->get($url, [
'proxy' => $proxy,
'timeout' => 20,
]);
return (string) $response->getBody();
} catch (ConnectException $e) {
// 프록시가 사용할 수 없거나 연결 타임아웃
echo "프록시 사용 불가 ($proxy): " . $e->getMessage() . "\n";
} catch (ClientException $e) {
// HTTP 4xx — 클라이언트 오류 (403, 404, 429)
$code = $e->getResponse()->getStatusCode();
echo "$url에 대한 HTTP $code ($proxy)\n";
if ($code === 429) {
echo "요금 제한 — 5초 대기\n";
sleep(5);
}
} catch (ServerException $e) {
// HTTP 5xx — 서버 오류
echo "서버 오류: " . $e->getResponse()->getStatusCode() . "\n";
} catch (RequestException $e) {
// 기타 요청 오류
echo "요청 오류: " . $e->getMessage() . "\n";
}
}
return null;
}
cURL vs Guzzle: 무엇을 선택할까
두 도구 모두 프록시와 잘 작동하지만 서로 다른 강점을 가지고 있습니다. 다음은 주요 기준에 따른 비교입니다:
| 기준 | cURL | Guzzle |
|---|---|---|
| 설치 | ✅ PHP에 내장됨 | ⚠️ Composer 필요 |
| 코드 가독성 | ⚠️ 많은 옵션, 장황함 | ✅ 깔끔하고 편리한 API |
| 성능 | ✅ 약간 더 빠름 (포장 없음) | ✅ 유사, 비동기 지원 |
| 비동기 요청 | ⚠️ curl_multi (어렵다) | ✅ 내장 지원 |
| 미들웨어 / 훅 | ❌ 없음 | ✅ HandlerStack, Middleware |
| SOCKS5 지원 | ✅ 네이티브 지원 | ✅ curl 핸들러를 통한 지원 |
| 오류 처리 | ⚠️ 오류 코드를 수동으로 처리 | ✅ 유형별 예외 처리 |
| Laravel/Symfony 통합 | ⚠️ 수동으로 통합 | ✅ 네이티브 지원 |
| 적합한 용도 | 간단한 스크립트, 의존성 없음 | 프레임워크 기반 프로젝트, 복잡한 로직 |
추천: 간단한 스크립트를 작성하거나 Composer가 없는 호스팅에서 작업하는 경우 cURL을 사용하세요. Laravel, Symfony 또는 기타 현대 PHP 애플리케이션에서 작업하는 경우 Guzzle이 훨씬 더 편리하고 확장 가능성을 제공합니다.
🚀 실행 전 빠른 체크리스트
- ✅ 프록시가 작동하는지 확인:
curl -x http://login:pass@host:port https://httpbin.org/ip - ✅ 타임아웃 설정:
timeout및connect_timeout - ✅ 현실적인 User-Agent 추가
- ✅ 요청 간 대기 시간 구현 (최소 500ms)
- ✅ 오류 처리 및 다른 프록시로의 대체 구현
- ✅ SOCKS5의 경우 CURLPROXY_SOCKS5_HOSTNAME 사용 (SOCKS5 사용 금지)
- ✅ 프로덕션에서는 SSL 검증 비활성화 금지
결론
PHP에서 프록시 설정은 올바른 옵션과 형식을 알면 간단한 작업입니다. 기본 작업에는 CURLOPT_PROXY 및 CURLOPT_PROXYUSERPWD가 있는 cURL이면 충분합니다. 프레임워크 기반 프로젝트에서는 Guzzle이 더 편리한 API, 유형화된 예외 및 프록시 회전을 위한 미들웨어 지원을 제공합니다.
신뢰할 수 있는 작업을 위한 핵심 원칙: DNS를 프록시를 통해 해결하는 SOCKS5 사용 (CURLPROXY_SOCKS5_HOSTNAME), 항상 타임아웃 설정, 요청 간 대기 시간 추가 및 대규모 작업을 위한 프록시 회전 구현. 오류 처리 및 다른 프록시로의 대체는 모든 프로덕션 코드의 필수 요소입니다.
```