백엔드/스프링, 스프링부트, JPA, Spring Webflux

[Spring] 스프링에서 외부 API 호출하는 방법들

Kangjieun11 2023. 5. 27. 23:40
728x90

 

✅ 개요 

외부 API를 사용해야하는 상황이 생겨서 호출 방법에 고민을 하고 있다. 

 

 

빠른 사용/개발을 위해서사용하고자 하는 API의 클라이언트 라이브러리 자체를 이용해도 되지만,

 

이 경우 특정 API에 종속되면서, 정책변경 및 가격변경에 영향을 받게 된다. 

 

요구 스킬의 변화와 유연한 확장을 위해 외부 API를 호출하는 방법들을 선택해야 할듯 해 이 글을 적게 되었다.

 

 

 

✅ 외부 API를 호출을 위한 방법들 비교

 

외부 API 를 호출하고, 응답값을 받아오는 방법에는 5가지 정도가 있다. 

각 기술의 장단점과 특징을 살펴보자

 

  • HttpURLConnection/URLConnection
  • RestTemplate
  • HttpClient
  • WebClient
  • OpenFeign

 


 

1️⃣ HttpURLConnection/URLConnection

http통신을 가능하게 해주는 클래스이다. 

 

 

HttpURLConnection (Java Platform SE 8 )

Returns the error stream if the connection failed but the server sent useful data nonetheless. The typical example is when an HTTP server responds with a 404, which will cause a FileNotFoundException to be thrown in connect, but the server sent an HTML hel

docs.oracle.com

 

  • 자바에서 제공하는 기본적인 API
  • 순수 자바로 HTTP통신이 가능하다.
  • URL을 이용하여 외부 API에 연결하고 데이터를 전송할 수 있다.
  • 데이터의 타입/길이에 거의 제한이 없다.

 

😵 단점

오래된 자바 버전에서 사용하는 클래스로 동기적 통신을 기본으로 사용한다.

동기적이므로 요청을 보내고, 응답을 받을 때까지 스레드가 대기 상태에 있다.

 

URLConnection은 상대적으로 저수준의 API에 해당된다. 

기본적인 요청/응답기능이 제공되지만, 추가적인 기능들에 대해선 직접 구현해야 된다.

 

 

더 많은 기능과 유연성을 위해서는 다른것을 채택하는게 좋겠다.

 

 

 

2️⃣ HttpClient

HTTP프로토콜을 쉽게 사용할 수 있도록 도와주는 Apache HTTP 컴포넌트

 

😉 장점

  • 객체 생성이 쉽다. 

 

아래 예제 코드를 보자. 

HttpClient  clent = HttpClientBuilder.create().build();처럼 굉장히 간단하게 생성하는것을 확인할 수 있다.

public void get(String requestURL) {
 try {
     HttpClient client = HttpClientBuilder.create().build(); // HttpClient 생성
     HttpGet getRequest = new HttpGet(requestURL); //GET 메소드 URL 생성
     getRequest.addHeader("x-api-key", RestTestCommon.API_KEY); //KEY 입력

     HttpResponse response = client.execute(getRequest);

     //Response 출력
     if (response.getStatusLine().getStatusCode() == 200) {
         ResponseHandler<String> handler = new BasicResponseHandler();
         String body = handler.handleResponse(response);
         System.out.println(body);
     } else {
         System.out.println("response is error : " + response.getStatusLine().getStatusCode());
     }

  } catch (Exception e){
     System.err.println(e.toString());
  }
 }

 

 

😵 단점

  • URLConnection방식보다 코드가 간결해졌지만, 반복적이고 코드가 길다.
  • 응답의 컨텐츠 타입에 따라 별도의 로직이 필요하다. 

 

 

 

 


 

3️⃣ spring RestTemplate 

 

스프링에서 제공하는 http 통신에 유용하게 사용가능한 템플릿이다.

 

 

RestTemplate (Spring Framework 6.0.9 API)

postForLocation Create a new resource by POSTing the given object to the URI template, and returns the value of the Location header. This header typically indicates where the new resource is stored. URI Template variables are expanded using the given URI v

docs.spring.io

 

 

 

특징

  • 스프링3.0에서 추가되었다.
  • org.springframework.http.client 패키지에 존재
  • HpptClient를 추상화해서 제공한다.
  • 대부분이 다른 API를 호출할 때 RestTemplate를 사용한다.
  • HTTP요청 후 JSON, XML, String 과 같은 응답을 받을 수 있다.
  • Blocking 기반의 동기방식을 사용한다.
  • HTTP 서버와의 통신을 단순화하고 Restful 원칙을 지킨다. 
  • header, content-type등을 설정하며 외부 API를 호출한다.
  • 서버-서버 통신에 사용한다.

 

😉 장점

사용하기 편하고, 직관적이다.

 

😵 단점

동기적인 HTTP 요청을한다.

커넥션 풀을 기본적으로 사용하지 않아서 연결할 때마다 로컬 포트를 열고, tcp connection을 맺는데....

이것을 해결하기 위해 connection pool을 사용해야한다. 

 

https://stackoverflow.com/questions/31869193/using-spring-rest-template-either-creating-too-many-connections-or-slow/

 

Using Spring REST template, either creating too many connections or slow

I have a RESTful service that works very fast. I am testing it on localhost. The client is using Spring REST template. I started by using a naive approach: RestTemplate restTemplate = new RestTemp...

stackoverflow.com

 

 

🤔 Deprecate 논란/이슈

구글링을 하다보면 RestTemplate이 deprecate되었다는 이야기가 정말 많았다.

 

 

✔️ deprecate 의미는?

  • 대안기술이 나왔기 때문에, 사용하는걸 권장하지 않음
  • 곧 제거될 예정

 

 

그러다가 다음과 같은 글을 찾았는데 .....

 

https://velog.io/@dailylifecoding/Spring-RestTemplate-wont-Deprecate

 

[Spring] RestTemplate Deprecated? Nope!

RestTemplate Deprecate 된다고? 응, 아냐~

velog.io

 

<토비의 스프링> 유튜브 채널에서 RestTemplate의 deprecated의 오해와 진실에 대한 이야기를 말했다.

 

https://youtu.be/S4W3cJOuLrU

 

👩‍💻 이슈 정리

 

➡️ RestTemplate는 deprecated?

 

 

1. 스프링 프로젝트의 리드 개발자분이 올린 Issue 

 

Deprecation는 정확하지 않은 표현이라고 하는 의미는  -> 제거되지 않는다로 받아들여도 될것 같다. 

유지관리모드에 있다가 더 정확한 표현임을 말해주고 계셨다.

 

 

2. 공식문서에도 유지관리모드에 있다는 표현으로 변경되어있다.

 

Java Doc에 WebClient 사용을 권고한다는 말이 존재한다.

RestTemplate가 Deprecated 라는 말은 적혀있지 않다.

 

참고: 5.0부터 이 클래스는 유지 관리 모드에 있으며 앞으로 허용될 변경 및 버그에 대한 사소한 요청만 있습니다. 보다 현대적인 API가 있고 동기화, 비동기 및 스트리밍 시나리오를 지원하는 org.springframework.web.reactive.client.WebClient 사용을 고려하십시오.

 

 

동기적인 기술이기 때문에

대규모 요청이 들어올 것을 대비해선 비동기/넌블로킹 방식을 지원하는 WebClient를 고려해볼 수 있다는 결론

 

* Spring4에서 추가된 비동기 RestTemplate인 AsyncRestTemplate도 존재한다.

 

 

 

 


 

4️⃣ webClient 

스프링 5부터 도입된 웹 클라이언트 라이브러리

비동기/논블로킹 방식으로 외부 API를 호출할 수 있다. 

 

 

 

😉 장점

비동기/논블로킹 방식을 지원하여, 높은 처리량과 확장성을 지원한다.

리액티브 프로그래밍을 할 수 있다. 데이터 스트림을 효과적으로 처리할 수 있다.

 

 

😵 단점

웹플럭스 학습곡선이 존재한다.

 

웹플럭스(Webflux)를 추가해야만 사용할 수 있다.  >> (웹플럭스 모듈이 통채로 추가된다.)

작은 규모의 간단한 프로젝트에서는 웹플럭스모듈이 통채로 추가되는것이 좋을까..?

 

 

예제코드를 봐보자.

가독성 좋은것이 아주 잘 느껴진다

package com.webclient.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;

import java.util.HashMap;
import java.util.Map;

@Service
@Slf4j
public class WebClientServiceImpl {

    public void get() {
        String code = "myCode";

        // webClient 기본 설정
        WebClient webClient =
                WebClient
                        .builder()
                        .baseUrl("http://localhost:8080")
                        .build();

        // api 요청
        Map<String, Object> response =
                webClient
                        .get()
                        .uri(uriBuilder ->
                                uriBuilder
                                        .path("/api/get")
                                        .queryParam("code", code)
                                        .build())
                        .retrieve()
                        .bodyToMono(Map.class)
                        .block();

        // 결과 확인
        log.info(response.toString());
    }
}

 

 

 


 

 

5️⃣  OpenFeign

최근에 처음 알게된 기술이름이다. 

 

 

선언적 웹서비스 클라이언트

 

 

✔️선언적?

어노테이션 사용을 의미 

인터페이스에 어노테이션들만 붙혀줌으로써 구현이 된다.  (Spring Data JPA와 유사) 

 

 

스프링

 

Spring Cloud OpenFeign

Feign is a declarative web service client. It makes writing web service clients easier. To use Feign create an interface and annotate it. It has pluggable annotation support including Feign annotations and JAX-RS annotations. Feign also supports pluggable

docs.spring.io

 

  • Netflix 에서 개발된 Http client binder
  • 웹서비스 클라이언트를 쉽게 작성할 수 있도록 지원한다.
  •  interface를 작성하고, annotation을 선언하기만 하면 된다. >> 스프링데이터JPA에서 인터페이스만 지정하여, 자동으로 구현체를 사용하는것과 유사

 

RestTemplate 으로 API호출시..

> API호출이 잦은 MSA시대에선 이런 코드를 반복하는것은 번거롭다 (인용-  망나니개발자)

@Component
@RequiredArgsConstructor
class ExchangeRateRestTemplate {

    private final RestTemplate restTemplate;
    private final ExchangeRateProperties properties;
    private static final String API_KEY = "apikey";

    public ExchangeRateResponse call(final Currency source, final Currency target) {
        return restTemplate.exchange(
                        createApiUri(source, target),
                        HttpMethod.GET,
                        new HttpEntity<>(createHttpHeaders()),
                        ExchangeRateResponse.class)
                .getBody();
    }

    private String createApiUri(final Currency source, final Currency target) {
        return UriComponentsBuilder.fromHttpUrl(properties.getUri())
                .queryParam("source", source.name())
                .queryParam("currencies", target.name())
                .encode()
                .toUriString();
    }

    private HttpHeaders createHttpHeaders() {
        final HttpHeaders headers = new HttpHeaders();
        headers.add(API_KEY, properties.getKey());
        return headers;
    }
}

 

 

OpenFeign으로 작성되면?

@FeignClient(name = "ExchangeRateOpenFeign", url = "${exchange.currency.api.uri}")
public interface ExchangeRateOpenFeign {

    @GetMapping
    ExchangeRateResponse call(
            @RequestHeader String apiKey,
            @RequestParam Currency source,
            @RequestParam Currency currencies);

}

 

😉 장점

  • 인터페이스/어노테이션 기반으로 코드 작성이 쉽다.
  • RestTemplate보다 RESTAPI를 사용하는데 필요한 설정이 간편해진다.
    • 즉 개발자가 비즈니스 로직에 더 집중할 수 있다.
  • 다른 라이브러리에 비하면 학습하기 쉽다.
  • 다른 Spring Cloud 기술들과의 통합이 쉽다.

 

😵 단점

  • spring cloud 모듈을 추가해야한다. > 필요없는 모듈들이 추가되는걸수도
  • HttpClient가 Http2를 지원하지 않는다. (Http Client에 대한 추가 설정이 필요하다)
  • 공식적으로 Reactive모델을 지원하지 않는다. 
  • 테스트 도구를 제공하지 않는다 (테스트 코드에 문제가 없는지 빠르게 확인하기 어렵다)

 

 

 


References

 

https://docs.oracle.com/javase/8/docs/api/java/net/HttpURLConnection.html

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

https://techblog.woowahan.com/2630/

https://blog.naver.com/hj_kim97/222295259904

https://youtu.be/S4W3cJOuLrU

https://mangkyu.tistory.com/278

 

 

 

 

 

추후 참고해볼 글들..

 

스프링 RestTemplate

https://blog.naver.com/hj_kim97/222295259904

 

HttpClient를 사용할 경우 ConnectionPool을 생성하는것 관련?

https://www.hyoyoung.net/103

 

webclient로 외부 API 호출하기

https://jforj.tistory.com/319