# Node.js 프록시 설정: Axios, Fetch, Undici 및 회전

axios 프록시 옵션은 HTTPS 대상에서 함정이며, 네이티브 fetch는 프록시 환경 변수를 완전히 무시합니다. 여기 axios, fetch 및 undici에서 작동하는 설정이 있습니다 — 에이전트, 인증, SOCKS5, 회전 및 스트리밍.

Node.js 요청을 프록시를 통해 라우팅하는 것은 간단해 보이지만 디버깅에 오후를 소비하게 됩니다. 그 이유는 대부분의 프로젝트에서 사용하는 세 가지 HTTP 클라이언트 — axios, Node 18에 글로벌로 제공된 네이티브 `fetch`, 그리고 기본 undici 라이브러리 — 각각 프록시를 다르게 처리하며, 기대한 대로 작동하지 않기 때문입니다. Axios는 문서화된 내장 `proxy` 구성을 가지고 있으며, Node 전용이며, HTTP 프록시 뒤의 HTTPS 대상에 대해 조용히 깨져 있습니다: 2020년부터 axios GitHub 추적기에 열려 있는 버그이며, 많은 추천을 받은 Stack Overflow 스레드를 유발했습니다. 한편 네이티브 fetch는 `HTTP_PROXY` 환경 변수를 무시하며 프록시 옵션이 전혀 없습니다. 이 가이드는 각 클라이언트에서 실제로 작동하는 설정, 인증, SOCKS5, 회전 및 스트리밍을 제공합니다.

## Axios 프록시 구성 대 프록시 에이전트

Axios는 `proxy` 객체 — `{ host, port, auth }` — 를 제공하며, 일반 HTTP 대상에 대해서는 잘 작동합니다. 함정은 HTTPS입니다: 대상이 `https://`이고 프록시가 HTTP를 사용할 때, axios는 CONNECT 터널을 열지 못하고 요청이 멈추거나 실제 IP로 돌아옵니다. 커뮤니티가 정한 해결책은 내장 구성을 완전히 우회하고 대신 axios에 프록시 에이전트를 제공한 다음, 두 메커니즘이 충돌하지 않도록 `proxy: false`를 설정하는 것입니다:

```js
import axios from "axios";
import { HttpsProxyAgent } from "https-proxy-agent";

// Note the destructured import — a default import is the classic gotcha.
const agent = new HttpsProxyAgent("http://USER:PASS@residentialpremium.quantumproxies.io:8080");

const client = axios.create({
  httpAgent: agent,   // for http:// targets
  httpsAgent: agent,  // for https:// targets
  proxy: false,       // disable axios' own broken proxy handling
  timeout: 15000,
});

const r = await client.get("https://httpbin.org/ip");
console.log(r.data); // { origin: "<proxy exit IP>" }
```

두 가지 세부 사항이 시간을 절약합니다. 첫째, **구조 분해된** `{ HttpsProxyAgent }` 가져오기를 사용하세요 — 최근 버전은 이를 명명된 심볼로 내보내며, 기본 가져오기는 생성 시 예외를 발생시키는 객체를 제공합니다. 둘째, 프록시 URL 스킴은 프록시가 HTTPS 트래픽을 전달할 때에도 `http://`로 유지됩니다: 스킴은 프록시에 도달하는 방법을 설명하며, 대상에 대한 TLS는 터널 내부에서 실행됩니다. 동일한 실패 클래스가 Python에서도 나타납니다; requests에서도 작업하는 경우, 패턴은 우리의 [ProxyError 및 SSLError 수정 가이드](https://quantumproxies.io/blog/python-requests-proxyerror-fix)와 유사합니다.

## 인증 및 환경 변수

인증된 프록시는 HTTP 기본 인증을 사용합니다. 에이전트와 함께, URL에 `http://user:pass@host:port`로 포함하세요; 비밀번호에 `@`, `:` 또는 `/`가 포함되어 있으면 먼저 `encodeURIComponent()`로 URL 인코딩하세요, 그렇지 않으면 URL이 잘못된 위치에서 분할됩니다. `407 Proxy Authentication Required`는 자격 증명이 거부되었거나 소스 IP가 허용 목록에 포함되지 않았음을 의미합니다. Axios는 또한 `HTTP_PROXY`, `HTTPS_PROXY`, `NO_PROXY`를 환경에서 읽습니다 — 코드에 손대지 않고 타사 라이브러리를 프록시하는 데 유용합니다 — 그리고 `proxy: false`를 설정하여 이를 비활성화합니다. 중요한 경고: 네이티브 `fetch`는 이러한 변수를 **읽지 않습니다**, 따라서 env-var 프록시를 사용하는 스크래퍼는 axios에서 fetch로 전환하는 순간 조용히 직접 연결됩니다.

![같은 요청을 위한 세 코드 조각: axios는 proxy: false와 함께 httpAgent와 httpsAgent에 HttpsProxyAgent를 받고, 네이티브 fetch는 호출마다 undici 디스패처를 받으며, undici의 setGlobalDispatcher와 ProxyAgent는 프로세스 전체를 프록시합니다. 이를 혼동하는 것이 'proxy not working'의 가장 흔한 원인입니다.](https://res.cloudinary.com/dmqliab0t/image/upload/f_auto,q_auto,w_1200/v1785335145/blog/generated/qp-b9-007-diag1.png)
*동일한 요청, 세 가지 배관 시스템. Axios는 에이전트를 원하고, 네이티브 fetch는 undici 디스패처를 원하며, 이들을 혼합하는 것이 '프록시 작동 안 함'의 가장 큰 원인입니다.*

## undici로 네이티브 fetch 프록시하기

Node의 글로벌 `fetch`는 undici를 기반으로 하며, 프록시는 undici에 있습니다. `ProxyAgent`를 생성하고 이를 비표준 `dispatcher` 옵션으로 전달하세요 — 이것이 fetch를 프록시하는 현대적이고 종속성이 적은 방법이며, HTTPS CONNECT를 상자 밖에서 올바르게 처리합니다:

```js
import { ProxyAgent } from "undici";

const dispatcher = new ProxyAgent({
  uri: "http://residentialpremium.quantumproxies.io:8080",
  token: "Basic " + Buffer.from("USER:PASS").toString("base64"),
});

const res = await fetch("https://httpbin.org/ip", { dispatcher });
console.log(await res.json());
```

글로벌 fetch가 아닌 undici를 직접 사용하는 경우, 동일한 `ProxyAgent`를 `request()` 또는 `setGlobalDispatcher()`에 연결하여 프로세스 내 모든 fetch를 한 번에 프록시할 수 있습니다. 이 단일 호출 글로벌 스위치는 모든 코드베이스를 프록시를 통해 라우팅하는 가장 깔끔한 방법입니다.

## Node.js의 SOCKS5 프록시

axios나 undici는 SOCKS를 기본적으로 지원하지 않습니다 — axios `proxy` 구성에 `socks5://` 문자열을 전달하면 `protocol mismatch` 예외가 발생합니다. `socks-proxy-agent`를 설치하고 이를 HTTPS 에이전트와 동일한 방식으로 사용하세요, `httpAgent`와 `httpsAgent` 모두에 연결합니다. `socks5://`보다 `socks5h://`를 선호하세요: 뒤에 오는 `h`는 프록시 측에서 DNS를 해결하여 DNS 누출을 방지하고 출구 위치에서 지리적으로 제한된 호스트 이름을 해결합니다. 모든 [SOCKS5 프록시](https://quantumproxies.io/socks5-proxies) 계획은 HTTP와 SOCKS5를 통해 동일한 게이트웨이를 노출하므로, 이는 새로운 구매가 아닌 스킴 전환입니다:

```js
import axios from "axios";
import { SocksProxyAgent } from "socks-proxy-agent";

const agent = new SocksProxyAgent("socks5h://USER:PASS@residentialpremium.quantumproxies.io:1080");

const client = axios.create({ httpAgent: agent, httpsAgent: agent });
const r = await client.get("https://httpbin.org/ip");
console.log(r.data);
```

## 프록시 목록 없이 회전

오래된 방법 — IP 배열, 요청당 `Math.random()`, 죽은 IP 제거 — 는 더 이상 유지하지 않는 코드입니다. 회전 게이트웨이는 서버 측에서 매 요청마다 새로운 출구 IP를 할당하므로 하나의 엔드포인트가 전체 풀처럼 동작합니다. [회전 주거 프록시](https://quantumproxies.io/rotating-proxies)를 통해 그 풀은 90M+ IP를 200+ 국가에 걸쳐 포함하며, 아래 루프는 회전 로직 없이 각 반복마다 다른 출처를 출력합니다:

```js
import axios from "axios";
import { HttpsProxyAgent } from "https-proxy-agent";

const agent = new HttpsProxyAgent("http://USER:PASS@residentialpremium.quantumproxies.io:8080");
const client = axios.create({ httpAgent: agent, httpsAgent: agent, proxy: false });

for (let i = 0; i < 3; i++) {
  const r = await client.get("https://httpbin.org/ip");
  console.log(r.data.origin); // a different exit IP each time
}
```

흐름이 여러 요청 — 로그인, 장바구니에 추가, 결제 — 에 걸쳐 있을 때, 요청당 회전은 세션을 깨뜨립니다. 프록시 사용자 이름의 고정 세션 매개변수는 설정된 시간 동안 하나의 출구 IP를 고정한 다음 회전합니다; 동일한 엔드포인트, 문자열 변경 하나. 이를 수천 개의 동시 요청으로 확장하려면, 작업을 axios 루프에서 벗어나게 하고 [대규모 스크래핑 아키텍처](https://quantumproxies.io/blog/large-scale-web-scraping-architecture)에 대한 가이드를 읽어 대기열 및 동시성 예산을 확인하세요.

## 프록시를 통한 스트리밍 응답

파일이나 큰 JSON 페이로드를 다운로드하는 것은 에이전트가 연결된 후 모든 요청과 동일하게 작동합니다 — `responseType: "stream"`을 설정하고 본문을 디스크로 파이프합니다. 프록시는 전송을 투명하게 처리하므로 200MB 내보내기가 메모리에 버퍼링되지 않습니다:

```js
import fs from "node:fs";

const r = await client.get("https://example.com/large.json", {
  responseType: "stream",
});
r.data.pipe(fs.createWriteStream("out.json"));
```

![httpbin.org/ip 페이지가 보여주는 네 가지 Node.js 프록시 실수: 실제 IP를 반환하거나 멈추는 axios의 proxy 설정, 예외를 던지는 기본 agent import, 프로토콜 불일치를 일으키는 axios의 socks5://, HTTP_PROXY를 무시하는 fetch. 카드는 각각의 해결책을 제시하고, agent와 proxy 설정을 중복으로 쓰지 말라는 경고를 덧붙입니다.](https://res.cloudinary.com/dmqliab0t/image/upload/f_auto,q_auto,w_1200/v1785335146/blog/generated/qp-b9-007-diag2.png)
*대부분의 Node.js 프록시 실패는 다섯 가지 실수로 귀결됩니다: axios HTTPS 버그, 기본 에이전트 가져오기, 중복된 프록시 구성, 잘못된 SOCKS 스킴, 또는 fetch가 환경 변수를 읽을 것으로 기대하는 것.*

## 자주 묻는 질문

### 왜 내 axios 프록시가 작동하지 않나요?

가장 일반적인 원인은 HTTP 프록시 뒤의 HTTPS 대상입니다: axios의 내장 `proxy` 구성은 CONNECT 터널을 열지 못하고 실제 IP를 반환하거나 멈춥니다. 프록시 에이전트로 전환하세요 — `HttpsProxyAgent`를 `httpAgent` 및 `httpsAgent`에 연결하고, axios가 자체적으로 프록시를 처리하려고 하는 것을 중지하도록 `proxy: false`를 설정하세요.

### axios는 SOCKS5 프록시를 지원하나요?

기본적으로는 아닙니다 — `socks5://` URL을 `proxy` 구성에 전달하면 프로토콜 불일치 오류가 발생합니다. `socks-proxy-agent`를 설치하고 `SocksProxyAgent`를 빌드하여 `httpAgent` 및 `httpsAgent`에 연결하세요. `socks5h://` 스킴을 사용하여 DNS가 프록시 측에서 해결되도록 하여 기기에서 누출되지 않도록 하세요.

### Node.js에서 네이티브 fetch와 함께 프록시를 어떻게 사용하나요?

네이티브 fetch는 프록시 옵션이 없으며 `HTTP_PROXY` 환경 변수를 무시합니다. undici `ProxyAgent`를 생성하고 fetch 호출에서 `dispatcher` 옵션으로 전달하거나, `setGlobalDispatcher()`를 한 번 호출하여 프로세스 내 모든 fetch를 프록시하세요. Undici는 추가 구성 없이 HTTPS CONNECT를 올바르게 처리합니다.

### 환경 변수로 axios 프록시를 어떻게 설정하나요?

전체 프록시 URL로 `HTTP_PROXY`, `HTTPS_PROXY` 및 선택적으로 `NO_PROXY`를 내보내세요; axios는 이를 자동으로 읽습니다. 요청이나 인스턴스에서 `proxy: false`를 설정하여 axios가 환경을 무시하도록 하세요. 이는 axios에만 영향을 미친다는 점을 기억하세요 — undici 및 네이티브 fetch는 이러한 변수를 인식하지 않습니다.

전체 이야기는 카드에 맞습니다: axios는 에이전트와 `proxy: false`가 필요하고, 네이티브 fetch는 undici 디스패처가 필요하며, SOCKS는 자체 에이전트가 필요하고, 회전은 루프가 아닌 게이트웨이에 속합니다. 배관을 올바르게 설정하면 마지막 변수는 IP 품질입니다 — 깨끗한 주거 출구는 동일한 코드에서 403을 받는 플래그된 데이터센터 IP를 통과합니다.

[첫 번째 요청에서 통과하는 주거 프록시를 얻으세요](https://quantumproxies.io/residential-proxies)
