Spring에서 외부 API를 호출하는 방법들

개발하는 구황작물·2023년 10월 3일
0

회사 프로젝트

목록 보기
6/8

프로젝트를 진행하다 외부 API와 Backend쪽 데이터를 합쳐야 했다.

처음에는 단순 무식하게 Frontend를 통해 API 통신을 하려고 했으나

위의 사진과 같이 데이터 전달 받겠다고 통신을 몇번 왔다갔다 하는 것은 아무리 봐도 비효율적이라고 생각했다.

이를 위해 다른 방법을 찾기로 하였다.


생각해낸 방법

  1. Backend와 외부API 정보를 모두 Frontend에서 받아서 Frontend 측에서 가공한다.
    --> 가장 간편한 방법이었으나 추후 데이터가 더 추가되거나 페이지네이션이 추가될 수 있어서 기각.
  2. Backend에서 외부 API를 호출하여 Backend내에서 가공한다.
    --> 1번의 단점을 해결할 수 있어서 채택

RestTemplate

외부 API를 호출하고 응답값을 받아오는 방법 중 가장 간단한 방법에는 RestTemplate이 있다.

Spring 3부터 지원하는 객체로 간편하게 Rest 방식으로 API를 호출할 수 있는 Spring 내장 클래스이다.

특징

  • Multi-Thread, Blocking 방식을 사용
  • Restful 형식에 맞추어진 템플릿
  • Header, Content-Type등 설정하여 외부 API 호출
  • Http 요청 후 json, xml, String 같은 응답을 받을 수 있음
@GetMapping("/example/{id}")
    public ResponseEntity<ResponseDto<?>> exampleApi(@AuthenticationPrincipal User user) {
        String email = user.getUsername();

        RestTemplate restTemplate = new RestTemplate();
        HttpHeaders headers = new HttpHeaders();
        headers.add("key","123455678");
        headers.add("email",email);

        HttpEntity request = new HttpEntity(headers);
        ResponseEntity<UserLogDto> response
                = restTemplate.exchange("https://외부API.com",
                HttpMethod.GET,
                request,
                UserLogDto.class);
        
        List<UserLogResponseDto> response = userService.getUserLogResponse(response.getBody(), email);

        return ResponseEntity.ok(ResponseDto.success(res));
    }

RestTemplate 단점

그러나 RestTemplate는 명확한 단점이 있다.

이유는 RestTemplate 동작 방식에 있다.

위에서 말했듯이 RestTemplate는 Multi-thread, Blocking 방식으로 동작된다.

Thread Pool은 어플리케이션 구동시 미리 Thread를 미리 만들어 놓는다.
이후 Request가 오면 먼저 Queue에 쌓이고 비어있는 스레드가 있으면 그 스레드에 할당된다.
각 스레드는 Blocking 방식으로 처리되어 응답이 올 때까지 그 스레드는 다른 요청에 할당될 수 없다.

이러한 방식은 만들어놓은 스레드가 다 찰 경우 문제가 발생한다.
스레드가 다 차는 경우 Queue에서 대기를 하게 되고 가용 가능한 스레드 수가 줄어들어 병목 현상이 나타나 서비스 성능을 저하시킬 수 있다. (이를 위해 Connection pool 값을 설정할 수 있으나 서버 사양에 따라 한계가 있다.)

당시에는 RestTemplate 방법만 알고 있었고, webclient는 webflux 환경에서 사용해야 해서 RestTemplate 방법을 활용하였다. (지금 돌이켜보면 OpenFeign을 활용할 것 같다...)

WebClient

RestTemplate에서 발생하는 단점은 WebClient를 사용하여 없엘 수 있다.

WebClient는 스프링 5에서 추가된 인터페이스로 Single-thread, Non-Blocking 방식을 사용한다.

Single-thread, non-blocking

  1. 각 요청은 Event loop 내의 job로 등록된다.
  2. event loop는 각 job를 제공자에게 요청하고 결과를 기다리지 않고 다른 job를 처리한다.
  3. event loop는 제공자로부터 callback로 응답이 오면 그 결과를 요청자에게 전달핟다.

이러한 방식을 사용하기 때문에 RestTemplate 보다 더 성능이 좋다.

성능 참고 글

WebClient 예시

dependency

implementation 'org.springframework.boot:spring-boot-starter-webflux'

를 build.gradle에 추가해야 한다.

예제 코드

@GetMapping("/v1/api")
    public Mono<String> useWebClient() {
        HttpClient httpClient = HttpClient.create()
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS,5000) //timeout 시간 조절
                .responseTimeout(Duration.ofMillis(5000))
                .doOnConnected(conn ->
                        conn.addHandlerLast(new ReadTimeoutHandler(5000, TimeUnit.MILLISECONDS))
                                .addHandlerLast(new WriteTimeoutHandler(5000, TimeUnit.MILLISECONDS)));


        var webClient = WebClient.builder()
                .baseUrl("http://외부api.com")
                .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .clientConnector(new ReactorClientHttpConnector(httpClient))
                .build();

        return webClient.get()
                .retrieve()
                .bodyToMono(String.class);
    }

이로써 spring 내부에서 api를 호출할 수 있었다.


이외에도 OpenFeign, HttpInterface 등 외부 API와 통신할 수 있는 방법이 있다고 한다.

자세한 내용은 여기에

Reference

https://mangkyu.tistory.com/291
https://thalals.tistory.com/377
https://tecoble.techcourse.co.kr/post/2021-07-25-resttemplate-webclient/

profile
어쩌다보니 개발하게 된 구황작물

0개의 댓글