NodeJS에서 프록시 설정하기

NodeJS에서 프록시 설정하기


최근 한 프로젝트에서 NodeJS가 프록시를 사용해야 했습니다. 처음에는 단순한 설정이라고 생각했지만, 생각보다 많은 노력이 필요했습니다.

NodeJS는 기본적으로 시스템 프록시를 읽지 않으며, NodeJS에는 네트워크 요청을 보내는 방법이 여러 가지가 있어서 프록시 설정 방법도 다양합니다. 여기에 요약해 보겠습니다.

전제 조건

프록시 서버 주소가 환경 변수에서 온다고 가정합니다. 이는 많은 웹 앱의 표준 방식입니다. 편의상 이를 HTTP_PROXYHTTPS_PROXY라고 부르겠습니다.

Fetch

최근 NodeJS는 Fetch 인터페이스를 지원하며, 브라우저의 Fetch와 일관성을 유지하려고 하지만, 완전히 일관성을 유지하면 프록시를 설정할 방법이 없어집니다. 다행히 NodeJS는 dispatcher라는 고유한 매개변수를 추가하여 프록시 서버를 포함한 네트워크 요청 매개변수를 수정할 수 있게 했습니다.

undici 패키지를 사용하여 dispatcher를 설정할 수 있습니다:

npm install undici

fetch 프록시 설정

ProxyAgent를 사용하여 설정합니다:

import { ProxyAgent } from "undici";

const proxyUrl = process.env.HTTPS_PROXY || process.env.HTTP_PROXY;
if (proxyUrl) {
  await fetch(url, { dispatcher: new ProxyAgent(proxyUrl) });
}

전역 fetch 프록시 설정

setGlobalDispatcher는 NodeJS 프로그램의 모든 fetch 호출에 대한 프록시를 설정하는 데 사용됩니다:

import { setGlobalDispatcher, ProxyAgent } from "undici";

const proxyUrl = process.env.HTTPS_PROXY || process.env.HTTP_PROXY;
if (proxyUrl) {
  setGlobalDispatcher(new ProxyAgent(proxyUrl));
}

프록시를 제거하려면 빈 Agent를 설정할 수 있습니다:

import { Agent, setGlobalDispatcher } from "undici";
setGlobalDispatcher(new Agent());

작동 방식

setGlobalDispatcher는 실제로 NodeJS의 숨겨진 진입점을 사용합니다. NodeJS의 전역 객체에는 전역 디스패처를 설정하기 위한 Symbol이 있습니다:

global[Symbol.for("undici.globalDispatcher.1")] = yourDispatcher;

이는 NodeJS 문서에 기재되어 있지 않은 것 같습니다(적어도 제가 찾지 못했습니다). 자세한 내용은 https://github.com/nodejs/node/issues/43187 을 참조하세요.

프록시 설정 외의 dispatcher 기능

ProxyAgent 대신 Agent를 가져오면 keepAlive와 timeout과 같은 네트워크 매개변수도 설정할 수 있습니다:

import { Agent } from "undici";

const res = await fetch("https://example.com", {
  dispatcher: new Agent({
    keepAliveTimeout: 10,
    keepAliveMaxTimeout: 10,
  }),
});
const json = await res.json();
console.log(json);

자세한 내용은 undici 문서를 참조하세요.

NodeJS 네이티브 모듈 http와 https

위에서 언급했듯이 NodeJS에는 프록시를 설정하기 위한 내장 메서드가 없으므로 http.Agenthttps.Agent를 사용하여 개발해야 합니다. 현재 인기 있는 패키지는 http-proxy-agenthttps-proxy-agent입니다.

https-proxy-agent 사용 예:

import https from "https";
import { HttpsProxyAgent } from "https-proxy-agent";

const proxyUrl = process.env.HTTPS_PROXY || process.env.HTTP_PROXY;
if (proxyUrl) {
  const agent = new HttpsProxyAgent(proxyUrl);

  https.get(
    {
      host: "example.com",
      path: "/",
      agent,
    },
    (res) => {
      // 응답 처리
    }
  );
}

Axios

Axios는 JavaScript의 업계 표준이라고 해도 과언이 아닙니다. 프록시를 설정하는 방법은 여러 가지가 있습니다(우선순위가 낮은 순서대로):

  1. 환경 변수: Axios 자체가 환경 변수 HTTP_PROXYHTTPS_PROXY를 읽습니다

  2. proxy 매개변수:

    import axios from "axios";
    
    axios.get("https://example.com", {
      proxy: {
        host: "127.0.0.1",
        port: 7890,
      },
    });
  3. httpAgenthttpsAgent 매개변수: NodeJS의 내장 http.Agenthttps.Agent를 전달하여 네트워크 매개변수를 제어할 수 있습니다.

    이전 섹션에서 언급한 http-proxy-agenthttps-proxy-agent를 사용합니다:

    import axios from "axios";
    import { HttpsProxyAgent } from "https-proxy-agent";
    import { HttpProxyAgent } from "http-proxy-agent";
    
    const proxyUrl = process.env.HTTPS_PROXY || process.env.HTTP_PROXY;
    if (proxyUrl) {
      const httpsAgent = new HttpsProxyAgent(proxyUrl);
      const httpAgent = new HttpProxyAgent(proxyUrl);
    
      const instance = axios.create({
        httpAgent,
        httpsAgent,
      });
    }

자세한 내용은 Axios 문서를 참조하세요.

HTTPS 연결 제한

처음 두 가지 방법으로 HTTP 프록시를 설정하면 Axios가 HTTPS 연결을 설정할 수 없다는 것을 발견했습니다. 그 이유는 Axios가 CONNECT 메서드를 보내지 않기 때문에 TLS 연결을 설정할 수 없기 때문입니다(CONNECT 메서드를 이해하지 못하는 경우 이 글을 읽어보세요).