프록시 서버를 구성하면서 JVM 옵션에 프록시 서버 정보(IP, PORT)를 넣었다. 작업 환경은 Spring Framework 4.3.7이다.
export JAVA_OPTS=" ${JAVA_OPTS} -Dhttp.proxyHost=(프록시 서버 아이피) -Dhttp.proxyPort=(프록시 서버 포트)"
export JAVA_OPTS=" ${JAVA_OPTS} -Dhttps.proxyHost=(프록시 서버 아이피) -Dhttps.proxyPort=(프록시 서버 포트)"
JVM 전체에 거는 옵션이니 애플리케이션에서 나가는 HTTP 요청은 전부 프록시를 탈 것 같다. 그렇지 않다. 이 옵션을 읽는 건 JDK의 네트워크 스택이고, HTTP 클라이언트가 그 스택 위에 있느냐에 따라 결과가 갈린다. 이 글에서는 자주 쓰는 클라이언트 네 가지가 이 옵션을 따르는지 정리한다.
HttpURLConnection은 시스템 프로퍼티를 그대로 따른다
HttpURLConnection은 JDK에 들어 있는 클래스다. 연결할 때 JVM 시스템 프로퍼티에서 프록시 설정을 가져오므로 -Dhttp.proxyHost와 -Dhttp.proxyPort로 지정한 프록시가 코드 수정 없이 자동으로 적용된다.
URL url = new URL("http://example.com");
// 별도 설정 없이도 -Dhttp.proxyHost/-Dhttp.proxyPort 프록시를 거친다
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
Apache HttpClient는 기본적으로 무시한다
여기서 말하는 HttpClient는 JDK 클래스가 아니라 Apache HttpComponents 라이브러리의 클라이언트다. 자체 연결 계층을 쓰기 때문에 기본적으로 JVM 시스템 프로퍼티를 무시한다. 프록시를 쓰려면 빌더에서 직접 지정해야 한다.
HttpClient httpClient = HttpClients.custom()
.setDefaultRequestConfig(requestConfig)
// JVM 옵션을 읽지 않으니 프록시를 코드에 명시한다
.setProxy(new HttpHost("{프록시 서버 아이피}", {프록시 서버 포트}))
.build();
프록시 주소를 코드에 두 번 적기 싫다면 HttpClientBuilder의 useSystemProperties()를 켜서 시스템 프로퍼티를 읽게 하는 방법도 있다.
RestTemplate은 따르고, WebClient는 따르지 않는다
Spring이 HTTP 통신을 쉽게 하도록 감싼 RestTemplate과 WebClient도 결국 아래에 어떤 클라이언트가 있느냐로 갈린다.
RestTemplate은 기본 설정에서 내부적으로 HttpURLConnection을 쓰므로 JVM 시스템 프로퍼티의 프록시 설정을 그대로 따른다.
RestTemplate restTemplate = new RestTemplate();
// 기본 요청 팩토리가 HttpURLConnection이므로 JVM 프록시 옵션이 적용된다
String result = restTemplate.getForObject("http://example.com", String.class);
WebClient는 HTTP 요청을 Reactor Netty 라이브러리로 처리한다. 이쪽은 JVM 시스템 프로퍼티의 프록시 설정을 무시하므로 따로 설정해야 한다.
// 여기서 HttpClient는 Apache가 아니라 Reactor Netty의 HttpClient다
HttpClient httpClient = HttpClient.create().proxy(proxy -> proxy.type(Proxy.HTTP)
.host("{프록시 서버 아이피}")
.port({프록시 서버 포트}));
WebClient webClient = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
참고로 WebClient는 Spring 5부터 들어온 클래스라 이 글의 작업 환경인 Spring 4.3.7에서는 쓸 수 없다. 버전을 올릴 때를 위해 같이 적어 둔다.
정리
| 클라이언트 | 내부 구현 | JVM 프록시 옵션 |
|---|---|---|
HttpURLConnection | JDK | 따른다 |
Apache HttpClient | Apache HttpComponents | 무시한다 (코드에서 setProxy) |
RestTemplate (기본 설정) | HttpURLConnection | 따른다 |
WebClient | Reactor Netty | 무시한다 (코드에서 proxy) |
JVM 옵션에 프록시를 넣었는데 일부 요청만 프록시를 타지 않는다면, 그 요청을 보내는 클라이언트의 내부 구현부터 보면 된다.