티스토리 뷰

Java/spring

spring boot RestClient 설정하기

LichKing 2025. 1. 29. 16:35

spring 은 전통적으로 http client 로 RestTemplate 을 제공해왔다. 그러다가 webflux 의 등장과 함께 비동기로 동작하는 http client 인 WebClient 라는 모던 http client 를 제공하기 시작했는데, 초기엔 mvc 에서도 이 WebClient 를 이용하는걸 권장했다. 하지만 WebClient 를 이용하기 위해선 webflux 전체에 의존해야하고, reactive 라이브러리인 proejct reactor API 를 사용해야하는 불편함이 있었다. 불편함이라고 썼지만 개인적으론 최악의 경험이었다. 그러다가 spring boot 3.2 에 신규 동기 http client 가 들어왔으니 그게 RestClient 이다. 오늘은 RestClient 를 잘 사용하기 위한 설정을 알아보려 한다. 사실 RestClient 든, 옛 RestTemplate 이든 http 호출을 래핑하고있는 수준에 불과하고, 진짜 http 호출은 내부에서 이루어지기 때문에 RestTemplate 설정과 거의 동일하다. 때문에 몇 년전 포스팅과 내용은 거의 같다.

 

# RestClient 생성하기

RestClient customClient = RestClient.builder()
  .requestFactory()
  .messageConverters()
  .baseUrl()
  .defaultUriVariables()
  .defaultHeader()
  .defaultCookie()
  .requestInterceptor()
  .requestInitializer()
  .build();

RestClient 는 빌더를 제공하며 빌더는 다양한 파라미터를 받는다. 이중에 성능에 영향을 주는건 requestFactory 부분이며 이 부분을 알아보고자 한다.

 

# ClientHttpRequestFactory

빌더의 requestFactory 는 ClientHttpRequestFactory 인터페이스를 파라미터로 받고 있는데 대표적인 구현체를 몇개만 살펴보자.

 

## SimpleClientHttpRequestFactory

이름에서 알 수 있듯이 가장 기본 구현체이다. 이 구현체를 사용할 경우 JDK 표준을 이용한 http 호출을 하게된다. 기본적인 타임아웃 설정들을 할 수 있다.

 

## JdkClientHttpRequestFactory

이 구현체 또한 JDK 표준을 이용한다. JDK 도 버전이 올라가면서 표준으로 지원하는 http client 인터페이스가 진화했는데 SimpleClientHttpRequestFactory 는 JDK 1.1 부터 존재하던 HttpURLConnection 을 이용하고, JdkClientHttpRequestFactory 는 11에 추가된 HttpClient 를 기반으로 한다.

 

## BufferingClientHttpRequestFactory

데코레이터 패턴을 적용하여 다른 RequestFactory 를 감싸는 구현체다. response body 가 들어온 스트림은 한번 읽으면 소비되어 사라지고 마는데 해당 스트림을 버퍼링하여 여러번 읽을 수 있게 한다. 대표적으로 response body 에 대한 로그를 남길때 사용한다.

 

## HttpComponentsClientHttpRequestFactory

apache httpclient 모듈을 기반으로한 방식이다. 가장 다양한 방식을 지원한다. 해당 구현체를 이용하려면 apache httpclient 모듈에 의존해야한다. 이 구현체는 HttpClient 라는 인터페이스를 파라미터로 받는데, 여기서 등장하는 HttpClient 와 JdkClientHttpRequestFactory 가 요구하는 HttpClient 는 서로 다른 모듈에 있는 관계이니 혼동하지 말자.

ConnectionConfig connectionConfig = ConnectionConfig.custom()                                                  
        .setConnectTimeout(Timeout.ofSeconds(1))
        .setSocketTimeout(Timeout.ofSeconds(1))        
        .build();                                                                                              
                                                                                                               
RequestConfig requestConfig = RequestConfig.custom()                                                           
        .setConnectionRequestTimeout(Timeout.ofSeconds(1))                                                     
        .setResponseTimeout(Timeout.ofSeconds(1))                                                              
        .build();                                                                                              
                                                                                                               
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();               
connectionManager.setDefaultMaxPerRoute(150);                                                                  
connectionManager.setMaxPerRoute(new HttpRoute(new HttpHost("localhost", 8080)), 10);                          
connectionManager.setMaxTotal(150);                                                                            
connectionManager.setDefaultConnectionConfig(connectionConfig);                                                
                                                                                                               
CloseableHttpClient httpClient = HttpClientBuilder.create()                                                    
        .setConnectionManager(connectionManager)                                                               
        .setDefaultRequestConfig(requestConfig)                                                                
        .build();                                                                                              
                                                                                                               
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);

HttpClient 는 또 HttpClientConnectionManager 라는 인터페이스를 이용해 커넥션을 관리하게 되는데 이 인터페이스의 구현체 중 하나인 PoolingHttpClientConnectionManager 를 이용해 커넥션 풀을 만들어서 사용할 수 있다. HttpClientConnectionManager 의 다른 구현체는 멀티스레드에서 이용할 수도 없고, 실무에 쓸만한 구현체는 아니니 PoolingHttpClientConnectionManager 만 보면된다.

 

위 코드에서 보듯이 PoolingHttpClientConnectionManager 는 스레드풀을 관리하는 옵션을 제공한다.

 

- defaultMaxPerRoute: 기본 경로당 최대 연결 수

- maxPerRoute: 특정 경로당 최대 연결 수

- maxTotal: 모든 경로를 합친 최대 연결 수, maxTotal 이 150 이라면 다른 옵션은 150을 넘겨도 의미가 없다

 

이 외에 타임아웃 설정은 별도 ConnectionConfig, RequestConfig 라는 객체를 통해 관리하는데 설정과 설정 주입이 다소 난잡하다.

 

- ConnectionConfig#connectionTimeout: 일반적으로 알고있는 커넥션 타임아웃

- ConnectionConfig#socketTimeout: 일반적으로 알고있는 소켓 타임아웃(read timeout)

- RequestConfig#connectionRequestTimeout: 커넥션 풀에서 커넥션을 가져오는데 대기하는 타임아웃

- RequestConfig#responseTimeout: 응답의 첫번째 패킷이 오기까지 대기하는 타임아웃

 

ConnectionConfig 쪽은 기존에 잘 알고있던 개념이라 크게 어려울게 없는데 RequestConfig 쪽이 좀 난해한 편이다.

 

# RestClient 설정하기

ConnectionConfig connectionConfig = ConnectionConfig.custom()                                                  
        .setConnectTimeout(Timeout.ofSeconds(1))                                                               
        .setSocketTimeout(Timeout.ofSeconds(1))                                                                
        .build();                                                                                              
                                                                                                               
RequestConfig requestConfig = RequestConfig.custom()                                                           
        .setConnectionRequestTimeout(Timeout.ofSeconds(1))                                                     
        .setResponseTimeout(Timeout.ofSeconds(1))                                                              
        .build();                                                                                              
                                                                                                               
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();               
connectionManager.setDefaultMaxPerRoute(150);                                                                  
connectionManager.setMaxPerRoute(new HttpRoute(new HttpHost("localhost", 8080)), 10);                          
connectionManager.setMaxTotal(150);                                                                            
connectionManager.setDefaultConnectionConfig(connectionConfig);                                                
                                                                                                               
CloseableHttpClient httpClient = HttpClientBuilder.create()                                                    
        .setConnectionManager(connectionManager)                                                               
        .setDefaultRequestConfig(requestConfig)                                                                
        .build();                                                                                              
                                                                                                               
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
                                                                                                               
var restClient = RestClient.builder()                                                                          
        .requestFactory(requestFactory)                                                                        
        .build();

위처럼 설정하면 커넥션 풀을 이용한 RestClient 설정을 할 수 있다. 이 외 RestClient 의 다른 부분들을 설정해서 사용하면 된다.

 

# 참고자료

https://www.baeldung.com/httpclient-connection-management

https://www.baeldung.com/httpclient-advanced-config

https://hc.apache.org/index.html

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함