[Spring] 외부 API 호출 로직에 관해

최재혁·2022년 11월 10일
3

WebClient

목록 보기
3/3
post-thumbnail

🤨들어가며

최근 진행하는 프로젝트에서 저는 HttpURLConnection을 사용하여 외부 API를 호출하고 있었습니다.

[외부 API 호출하는 코드]

public interface ApiService {

    default HttpURLConnection makeConnection(String urlStr) throws IOException {
        // URL 객체 생성 (절대경로)
        URL url = new URL(urlStr);

        // OpenConnection() 메소드로 연결
        // url 주소의 원격 객체에 접속한 뒤 --> 통신할 수 있는 URLconnection 객체 리턴
        HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();

        urlConnection.setRequestMethod("GET");
        // 발급받은 API 키 Request 헤더에 설정
        urlConnection.setRequestProperty("X-Riot-Token", RiotApiKey.KEY);
        return urlConnection;
    }
}

ApiService는 여러 외부 API를 호출하기 전에 필수로 수행해야 하는 작업(HttpURLConnection을 맺고, 헤더 설정 등) 을 공통화한 인터페이스입니다. default 메서드로 설정했을까요? 애초에 인터페이스에서 제공하는 외부 API 호출 로직이 변경될 가능성을 염두에 두고 설계했기 때문입니다.

저 때 당시에는, 외부 API를 호출하는 로직을 직접 Http Connection을 맺고 끊는 방식의 HttpURLConnection을 채택했습니다. (그때 당시에는 저 방법밖에 몰랐기 때문입니다..) 그런데 HttpURLConnection의 진정한 설계 용도는 외부 REST API를 받아오는 것이 아니었습니다.

물론 가능은 하지만, HttpURLConnection은 말 그대로 Http 프로토콜을 다루기 위함이고 HttpURLConnection 보다 REST API를 받아오는 더 추상화된 명세를 스프링은 제공하고 있었습니다. 바로 RestTemplate 입니다. RestTemplate을 사용하면, 코드의 가독성도 더 좋아지고, 테스트에서도 빈 주입을 통한 API 호출 로직의 단위 테스트가 가능할 것이므로 HttpURLConnection을 대체하는 것이 좋다고 생각합니다.

API 호출되는 로직을 default 메서드로 설정하여, 후에 HttpURLConnection이 아닌 다른 방법을 통해 로직을 수행한다 하더라도, ApiService를 구현하는 모든 클래스에서 강제로 추가 구현할 필요가 없게끔 설계했습니다.

RestTemplate 설계 코드

제가 RestTemplate를 사용하여 외부 REST API를 받아오는 코드는 다음과 같습니다.

void faker(){
    System.out.println("힝 속았지?");
}

...사실 저는 RestTemplate를 사용하지 않았습니다.. 짜잔

(죄송합니다.)

사실 제가 HttpURLConnection에서 다른 방법으로 API를 호출하는 로직을 바꾸려는 주된 이유는 따로 있었습니다.


😎외부 API 호출 로직 변경 이유

저의 프로젝트에서는 하나의 클라이언트 요청 당 외부 API를 10번 정도 호출하는 로직이 있었습니다. 그러다 보니 창 하나를 띄우는 데에도 평균 5초 정도의 시간이 걸렸습니다. 이는 네트워크 사정에 따라 달라졌지만 매번 10번의 HttpURLConnection을 맺고 끊는 과정에서, 필수적으로 지연되는 시간이 발생했고, 비즈니스 로직 상에서는 이를 근본적으로 극복하기가 힘들었습니다.

따라서, 애초에 Http 연결을 맺고 끊는 과정을 손보기로 했습니다. 맨 처음 고려했던 것은 RestTemplate입니다.

RestTemplate이란?

간단하게 말해서, Rest API를 호출하고, 응답을 제어할 수 있는 스프링에서 제공하는 클래스입니다. 제가 사용하지 않았기 때문에 자세한 내부 메서드들은 공유드리기가 어렵지만, 채택하지 않은 이유는 분명합니다.

Synchronous client to perform HTTP requests, exposing a simple, template method API over underlying HTTP client libraries such as the JDK HttpURLConnection, Apache HttpComponents, and others.

공식 RestTemplate의 설명 중 일부입니다. 내부적으로 HttpURLConnection, Apache HttpComponents 등을 사용하고 있음을 알리고 있습니다. Rest API 호출 로직을 조금 더 추상화했을 뿐, 성능 상으로 큰 차이가 없음을 예상할 수 있습니다.

또 하나의 이유가 있습니다.

NOTE: As of 5.0 this class is in maintenance mode, with only minor requests for changes and bugs to be accepted going forward. Please, consider using the org.springframework.web.reactive.client.WebClient which has a more modern API and supports sync, async, and streaming scenarios.

RestTemplate의 설명 중 또다른 일부입니다. 주석에서부터, 다른org.springframework.web.reactive.client.WebClient이라는 modern한 API를 사용하라고 하네요. 실제로 RestTemplate은 스프링 3.0부터 지원된 api인데, 스프링 5.0부터는 WebClient 를 사용하라고 권장하고 있습니다.

그렇다면 WebClient란?

WebClient는 기존의 RestTemplate가 지원하는 동기적인 방식부터, Non-blocking 하고 반응형 방식의 HTTP 통신을 지원하고 있습니다. 내부 구조는 Reactor Netty 라이브러리를 사용하였습니다. 사용하기 위해서는 implementation 'org.springframework.boot:spring-boot-starter-webflux' 의존성을 build.gradle에 주입해줘야 합니다. 즉 Spring Webflux에서 지원하는 기술입니다.

dependencies{
    implementation 'org.springframework.boot:spring-boot-starter-webflux'
}

🍀WebClient 선택 이유

기존의 HttpURLConnection은 동기 방식이기 때문에 외부 Http 커넥션을 10번 호출할 시, 앞 순서에서 발생한 지연 시간이 뒷 순서의 커넥션에도 영향을 미칠 수 있습니다. Head-Of-Line Blocking이 발생하는 것이죠.

하지만, WebClient는 말씀드렸다시피 Non-Blocking 방식으로 통신합니다. 이는, 호출한 네트워크 연결의 처리를 기다리지 않고 다음 로직을 수행할 수 있음을 의미합니다. 따라서 HOL Blocking 문제를 해결할 수 있고, 속도 향상을 기대해볼 수 있습니다.

저도 현재 추가적인 공부를 진행하고 있습니다. 다음 포스트에서 제가 WebClient를 사용하여, API를 통신하는 방법에 대해서 설명드리고, 각각의 방식에 대한 성능 측정 또한 해보겠습니다.


마치며..

Spring 3.0에 출시된 RestTemplateSpring 5.0에서 WebClient로 대체되었죠. 불과 8년 만에 RestTemplate는 레거시가 됐습니다. 개발자는 정말 끊임 없이 공부해야 함을 다시 한번 실감하는 공부가 되었습니다. 대체되지 않기 위해서.

📚Reference

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/reactive/function/client/WebClient.html

https://stackoverflow.com/questions/53795268/should-i-use-httpurlconnection-or-resttemplate

https://suyeonchoi.tistory.com/61

https://www.baeldung.com/spring-5-webclient

profile
잘못된 고민은 없습니다

0개의 댓글