Notice
Recent Posts
Recent Comments
Link
«   2024/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
Tags
more
Archives
Today
Total
관리 메뉴

DevYGwan

외부 API 사용 :: 마지막까지 깔끔하게 본문

Study/Spring

외부 API 사용 :: 마지막까지 깔끔하게

YGwan 2023. 12. 30. 02:59

프로젝트를 진행하는 동안 외부 API를 사용해본적이 있으실겁니다. Spring에서 외부 API를 사용하기 위한 대표적인 방법은 

  1. RestTemplate
  2. WebClient
  3. ForeignClient

가 있습니다. 하지만, 단순히 외부 API만 사용한다면 큰 문제가 발생할 수 있습니다. 외부 API를 제공하는 서버는 저희가 컨트롤 할 수 없기 때문입니다. 즉, 만약 해당 서버가 문제가 생겨 응답을 못받게 되는 상황이 온다면, 저희 서버도 영향을 미치기 때문입니다. 단순히 해당 서버가 터진다면 외부 API로 사용 시 에러를 반환해 큰 문제가 되진 않습니다. 하지만 만약 에러를 반환하는게 아닌 커넥션을 계속해서 가지고 있다면 이를 동기적으로 처리했을때는 서비스 장애로, 비동기적으로 처리했을때는 리소스 낭비의 문제가 발생할 수 있습니다. 따라서 이에 대한 처리는 필수적으로 해야합니다.

 

외부 API란?

오픈 API라고도하며, 누구나 사용할 수 있도록 공개된 API를 말합니다. 데이터를 표준화하고 프로그래밍해 외부 소프트웨어 개발자나 사용자가 바로 개발(어프리케이션)에 활용할 수 있는 형태의 개방 형식입니다. 개방된 오픈API를 이용해 다양하고 재미있는 서비스나 애플리케이션, 다양한 형태의 플랫폼을 개발할 수 있습니다. 해당 API를 제공한 업체에서 설정한 표준 규격에 맞게 요청을 보내고 응답을 받아 이를 활용합니다. 따라서 업체에서 제공하는 API 문서를 확인하여 개발할때 사용하는 것이 중요합니다. 아래는 예시를 위해 OpenAI api docs에서 제공하는 여러 외부 API 중 하나에 관한 API 문서입니다.

 

 

 

보시는 바와 같이 Request Body에 어떤 값이 들어가야되고 이로 인한 응답값은 어떻게 나오는지를 설명해주는 것을 알 수 있습니다. 따라서 이 api를 사용하는 경우 이러한 docs를 확인하는 것이 매우 중요합니다.

 

 

 


그렇다면 이제부터 Spring에서 외부 api를 사용하는 방법을 알려드리겠습니다. spring에서 외부 api를 사용하는 방법은 크게

 

1. RestTemplate 사용

2. WebClient 사용

3. FeignClient 사용

 

스프링에서 외부 API을 사용하는 대표적인 방법은 위의 3가지가 존재합니다. 이들의 사용 방법은 여러 블로그들에서 설명을 하고 있고 어렵지 않으니 생략하고 저는 이 외부 API를 사용할때 장애 대비를 위한 connection-timeout과 response-timeout 값 설정에 대해서 설명하도록 하겠습니다.)

 

 

외부 API 장애 대비 정책

 

기본적으로 스프링 서버는 Servlet기반으로 동작하기 때문에 요청 당 쓰레드를 생성해서 처리하는 멀티 쓰레드 방식으로 동작합니다. 정확히 말하면 스프링 서버가 띄워질때, Thread Pool에 서버에서 설정한 쓰레드를 미리 생성하고 필요할때 생성된 쓰레드를 가져와 사용합니다. 만약 서버에서 외부 API를 사용하는 로직이 있을때 이 외부 API서버에 문제가 생기면 어떻게 될까요? 그렇게되면 '대기 상태'에 빠집니다. 결국 해당 요청에 대한 쓰레드를 계속해서 잡고 있어 사용자에게 요청도 안가고 서버의 쓰레드 자원을 계속해서 점유하기 때문에 불필요한 자원 낭비가 됩니다. 만약 이런 요청이 계속해서 발생한다면 계속해서 서버의 쓰레드를 점유할테고 이러면 후에 서버에 남아 있는 쓰레드 자원이 존재하지 않아 서버에 심각한 문제가 발생합니다. 이러한 문제를 해결하기 위해 여러가지 방법이 있지만 저같은 경우 이중 가장 기본이 되는 Connection-timeout과 response(read) timeout값에 대해서 설명하도록 하겠습니다.

 

 

 


※  Connection - Timeout

Connection - Timeout은 서버에 연결되는데 소요되는 시간을 제한두는 것을 의미합니다. 사용할 외부 api 서버에 연결시 지정된 시간 내에 연결이 되지 않으면 Timeout 예외가 발생하고 연결을 강제로 끊어줍니다. 주로 연결된 서버가 응답을 주지 않거나 서버 자체가 다운된 장애를 대비하기 위해 사용됩니다.

 

※  Read - Timeout

Read - Timeout은 Response - Timeout이라고도 하며, 서버로부터 응답을 받는데 소요되는 시간을 제한 두는 것을 의미합니다. 사용할 외부 api 서버로부터 응답이 오는데 지정된 시간 이내에 응답이 오지 않으면 timeout 예외가 발생하고 연결을 강제로 끊어줍니다. 주로 서버가 응답을 처리하는데 오래 걸리는 대용량 데이터 같은 경우 이를 설정해 장애를 대비하기 위해 사용합니다.

 

그렇다면 지금부터 해당 적용 코드를 RestTemplate에서 적용하는 방법, WebClient에서 적용하는 방법, FeignClient에서 적용하는 방법을 설명하도록 하겠습니다.

 

 


RestTemplate



RestTemplate에서 Connection - Timeout, Read - Timeout 설정
@Configuration
public class SpringConfig {

    @Value("${server.connection-timeout.second}")
    private int connectionTimeout;

    @Value("${server.response-timeout.second}")
    private int responseTimeout;
    
    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
        return restTemplateBuilder
                .setConnectTimeout(Duration.ofSeconds(connectionTimeout))
                .setReadTimeout(Duration.ofSeconds(responseTimeout))
                .build();
    }
}

 

ResTemplate관련 설정을 Configuration 클래스에서 진행할때 위와 같은 코드를 사용해서 Connection - Timeout, Read - Timeout 값을 설정할 수 있습니다. 스프링 부트는 기본적으로 RestTemplateBuilder 등록되어 있기 때문에 이를 매개변수로 받아 커스텀 해주면 값 설정이 가능하다.

 

 

WebClient

WebClient에서 Connection - Timeout, Read - Timeout 설정
@Configuration
public class SpringConfig {

    @Value("${server.connection-timeout.second}")
    private int connectionTimeout;

    @Value("${server.response-timeout.second}")
    private int responseTimeout;
    
    @Bean
    public WebClient webClient() {
        return WebClient.builder()
                .defaultHeaders(
                        headers -> {
                            headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
                            headers.setBearerAuth(key);
                        }
                )
                .clientConnector(new ReactorClientHttpConnector(
                        HttpClient.create()
                                .responseTimeout(Duration.ofSeconds(responseTimeout))
                                .option(
                                        ChannelOption.CONNECT_TIMEOUT_MILLIS, connectionTimeout * 1000
                                )
                ))
                .build();
    }
}

RestTemmplate와 마찬가지로 WebClient 관련 설정을 Configuration 클래스에서 진행할때 위와 같은 코드를 사용해서 Connection - Timeout, Read - Timeout 값을 설정할 수 있습니다. 

 

 

 

FeignClient

 

 

 

FeignClient에서 Connection - Timeout, Read - Timeout 설정

 

# feignClient 장애 대비를 위한 env
feign:
  client:
    config:
      default:
        connectTimeout: 5000
        readTimeout: 10000

 

FeignClient의 경우 간단하게 application.properties 파일에서 값을 설정해주면 간단하게 Connection - Timeout, Read - Timeout 값을 설정할 수 있습니다.

 

 

 


정리

 

이번에는 외부 API를 사용할때 장애가 발생했을때 이를 효율적으로 처리하기 위한 방법을 정리했습니다. 외부 API의 경우 개발자가 직접 스프링 서버로 개발한 서버가 아니기 때문에 만약 해당 서버에서 문제가 발생하면 이를 사용한 요청 처리에 문제가 발생할 것입니다. 이를 제대로 처리해주지 않으면 심각한 문제가 발생할 것입니다. 따라서 단순히 외부 API를 사용하기 위해 해당 서버에 요청을 보내고 응답을 받는 기본적인 로직 뿐만 아니라 문제가 생겼을경우에 이를 처리할 수 있는 로직을 추가하는 것은 필수적입니다.

이번을 계기로

 

기능을 구현할때는 해당 기능 사용시에 발생할 수 있는 장애를 예상하고
이를 대비 & 처리를 하기 위한 로직을 구현하는 것은 필수적이다.

 

라는 생각이 들었습니다. 단순한 기능 구현은 누구나 할 수 있습니다. 이에 대한 장애를 예상하고 대비하는 것이야말로 중요한 개발자의 덕목이지 않을까라는 생각이 들었습니다.

감사합니다!!