Node.js에서 프록시를 쓰려면 무엇이 필요한가요?
Node.js에 들어 있는 fetch는 undici라는 라이브러리로 동작하고, 기본으로는 프록시 설정을 읽지 않아요. 그래서 HTTP 프록시는 undici의 ProxyAgent를 dispatcher로 넘겨서 쓰고, SOCKS5 프록시는 socks-proxy-agent를 Node의 https 모듈에 agent로 넘겨서 써요.
예시는 IP 198.51.100.23, HTTP 포트 8000, SOCKS5 포트 1080, 아이디 spx_user, 비밀번호 spx_pass를 써요. 모든 코드는 ES 모듈 형식(.mjs)이에요.
npm install undici socks-proxy-agentfetch에 HTTP 프록시를 연결하려면 어떻게 하나요?
ProxyAgent에 프록시 주소를 넣고, 아이디와 비밀번호는 token 옵션에 Basic 인증 값으로 넣어요. undici에서 가져온 fetch에 dispatcher로 넘기면 그 요청이 프록시를 거쳐요. HTTPS 주소는 CONNECT 방식으로 암호화된 채 전달돼요.
같은 ProxyAgent를 여러 요청에 다시 쓰면 연결이 재사용되어 빨라요. 프록시마다 에이전트를 하나씩 만들어 두고 돌아가며 쓰면 돼요.
import { fetch, ProxyAgent } from "undici";
const agent = new ProxyAgent({
uri: "http://198.51.100.23:8000",
token: "Basic " + Buffer.from("spx_user:spx_pass").toString("base64"),
});
const res = await fetch("https://api.ipify.org?format=json", { dispatcher: agent });
console.log(res.status, await res.json()); // "ip" should be the proxy IPSOCKS5 프록시는 어떻게 연결하나요?
socks-proxy-agent는 Node의 http, https 모듈과 함께 쓰는 에이전트예요. 주소를 socks5h로 쓰면 도메인 이름 확인을 프록시 쪽에서 해요. 아래 예시는 https.get에 에이전트를 넘겨 응답 본문을 모아 출력해요.
axios처럼 http 모듈 위에서 동작하는 라이브러리도 httpAgent와 httpsAgent에 같은 에이전트를 넣으면 SOCKS5로 요청이 나가요.
import https from "node:https";
import { SocksProxyAgent } from "socks-proxy-agent";
const agent = new SocksProxyAgent("socks5h://spx_user:spx_pass@198.51.100.23:1080");
https.get("https://api.ipify.org?format=json", { agent }, (res) => {
let body = "";
res.on("data", (chunk) => (body += chunk));
res.on("end", () => console.log(res.statusCode, body));
}).on("error", (err) => console.error("request failed:", err.message));API로 목록을 받아 순환하려면 어떻게 하나요?
시작할 때 API로 목록을 받아 프록시마다 에이전트를 만들고, 요청마다 다음 에이전트를 쓰는 방식이에요. 아래 코드는 HTTP 목록을 ip:port:user:pass 형식으로 받아 ProxyAgent 배열을 만들고, 실패하면 다음 프록시로 다시 시도해요.
요청에는 AbortSignal.timeout으로 시간 제한을 꼭 넣어 주세요. 응답이 없는 요청이 쌓이면 전체 작업이 느려져요.
import { fetch, ProxyAgent } from "undici";
const API = "https://socks5proxy.io/api/v1";
const KEY = "YOUR_API_KEY";
const text = await (await fetch(API + "/proxies/download?format=ip:port:user:pass&protocol=http", {
headers: { Authorization: "Bearer " + KEY },
})).text();
const agents = text.trim().split("\n").map((line) => {
const [ip, port, user, pass] = line.trim().split(":");
return new ProxyAgent({
uri: "http://" + ip + ":" + port,
token: "Basic " + Buffer.from(user + ":" + pass).toString("base64"),
});
});
let i = 0;
async function get(url, tries = 3) {
for (let t = 0; t < tries; t++) {
const dispatcher = agents[i++ % agents.length];
try {
const res = await fetch(url, { dispatcher, signal: AbortSignal.timeout(20000) });
if (res.ok) return await res.text();
} catch {
// try the next proxy
}
}
return null;
}
console.log((await get("https://example.com/"))?.length);IP 인증을 쓰면 코드가 어떻게 바뀌나요?
대시보드에 Node.js가 실행되는 서버의 IPv4를 등록하면 ProxyAgent에서 token 옵션을 빼고 주소만 넣으면 돼요. SOCKS5도 socks5h://198.51.100.23:1080처럼 아이디와 비밀번호 없이 쓸 수 있어요. 비밀번호를 환경 변수나 코드에 두지 않아도 되어 서버 여러 대에 배포하기 쉬워요.
axios를 쓰고 있다면 어떻게 연결하나요?
axios는 Node의 http, https 모듈 위에서 동작해서 에이전트를 넘겨 프록시를 쓸 수 있어요. SOCKS5는 socks-proxy-agent를 httpAgent와 httpsAgent에 넣고, axios 자체의 proxy 옵션은 false로 두어 두 설정이 겹치지 않게 해요.
HTTP 프록시는 axios의 proxy 옵션에 host, port, auth를 넣는 방법도 있지만, HTTPS 주소에서는 에이전트를 쓰는 방식이 더 안정적이에요. https-proxy-agent 패키지를 쓰면 HTTP 프록시도 같은 구조로 연결할 수 있어요.
import axios from "axios";
import { SocksProxyAgent } from "socks-proxy-agent";
const agent = new SocksProxyAgent("socks5h://spx_user:spx_pass@198.51.100.23:1080");
const client = axios.create({ httpAgent: agent, httpsAgent: agent, proxy: false, timeout: 20000 });
const { data } = await client.get("https://api.ipify.org?format=json");
console.log(data);동시에 여러 요청을 보낼 때 주의할 점은 무엇인가요?
Promise.all로 수천 개 요청을 한꺼번에 시작하면 연결이 너무 많이 열려 프록시와 대상 사이트 모두에 부담이 가요. 동시에 처리할 개수를 정하고 그만큼만 돌리는 작업 큐를 두세요. 작은 함수로 직접 만들어도 되고, p-limit 같은 패키지를 써도 돼요.
프록시마다 동시 연결 수를 고르게 나누면 IP 하나에 요청이 몰리지 않아요. 공유 플랜은 동시 연결 500이 포함되어 있으니 전체 동시 요청 수를 그 안으로 맞춰 주세요.
환경 변수로 전체 요청에 프록시를 적용할 수 있나요?
undici의 EnvHttpProxyAgent는 HTTP_PROXY, HTTPS_PROXY, NO_PROXY 환경 변수를 읽어 프록시를 정해요. setGlobalDispatcher로 등록하면 그 프로세스의 모든 fetch가 프록시를 거쳐요. 여러 라이브러리가 내부에서 fetch를 쓰는 경우 한 번에 적용할 수 있어 편해요.
전역 설정은 모든 요청에 같은 IP를 쓰게 되니, IP를 나눠 써야 하는 수집 작업보다는 한 나라에서 접속해야 하는 도구나 시험에 맞아요. 순환이 필요하면 앞에서 본 것처럼 요청마다 dispatcher를 넘기세요.
// HTTPS_PROXY=http://spx_user:spx_pass@198.51.100.23:8000 node app.mjs
import { EnvHttpProxyAgent, setGlobalDispatcher, fetch } from "undici";
setGlobalDispatcher(new EnvHttpProxyAgent());
const res = await fetch("https://api.ipify.org");
console.log(await res.text());자주 나는 문제는 어떻게 해결하나요?
Node.js 내장 fetch에 dispatcher를 넘겼는데 무시되는 것처럼 보이면, undici 패키지에서 fetch를 함께 가져왔는지 확인하세요. 내장 fetch와 설치한 undici의 버전이 다르면 에이전트가 맞지 않을 수 있어서, 같은 패키지에서 fetch와 ProxyAgent를 가져오는 방법이 가장 확실해요.
407 응답은 인증 정보가 틀렸다는 뜻이에요. token 값의 Basic 뒤에 아이디:비밀번호를 base64로 바꾼 값이 들어갔는지 확인하세요. ECONNREFUSED는 포트가 틀린 경우가 많아요. HTTP 에이전트에는 HTTP 포트를, SOCKS 에이전트에는 SOCKS5 포트를 넣어 주세요.
- fetch와 ProxyAgent는 같은 undici 패키지에서
- 407: token 값과 등록한 IP 확인
- ECONNREFUSED: HTTP 포트와 SOCKS5 포트 구분
- 모든 요청에 시간 제한