외부 API 호출은 어떤 라이브러리를 선택해야 할까

반응형

외부 API에 접근하기 위해서는 HTTP 통신이 필요합니다. 직접 HTTP 통신 코드를 구현할 수도 있지만, Spring 환경에서는 WebClient, RestTemplate, FeignClient와 같은 HTTP 클라이언트 라이브러리를 사용할 수 있습니다. 그렇다면 어떤 라이브러리를 사용하는 것이 좋을까요?

일단 내가 중요하다고 생각하는걸 생각해보자.

기술을 선택할때 가장 먼저 고려해야 하는 요소는 환경입니다. 현재 어떤 프레임워크를 사용하고 있는지, 팀원들의 숙련도는 어느 정도인지, 현재 프로젝트 구조와 운영 방식은 어떠한지 등을 함께 고려할 필요가 있습니다.

예를 들어 팀 전체가 RestTemplate에 익숙한 환경이라면, 무조건 최신 기술이라는 이유만으로 WebClient를 도입하는 것이 오히려 유지보수 비용을 증가시킬 수 있습니다. 반대로 외부 API 호출이 많고 timeout, retry, logging과 같은 기능이 중요하다면 WebClient가 더 적합할 수 있습니다.

그럼에도 불구하고 webClient를 선택하였습니다. 그 이유는?

일단 간단하게 비교를 진행해봅시다.

RestTemplate

가장 큰 장점은 구축하기 쉽다는 점입니다. RestTemplate은 동기 방식 기반으로 동작하기 때문에 코드 흐름이 직관적이고, 요청과 응답의 흐름을 이해하기 쉽습니다. 또한 기존 프로젝트에서도 많이 사용되어 왔기 때문에 레퍼런스가 많고, 러닝 커브가 낮다는 점 역시 장점이라고 생각합니다.

하지만 기본적으로 동기 방식만 지원하기 때문에 비동기 처리를 하고 싶다면 @Async, CompletableFuture, ExecutorService와 같은 자바 비동기 기능을 추가로 구성해야 합니다.

즉, 비동기 처리를 자연스럽게 지원한다기보다는 별도의 구조를 덧붙여야 하는 방식에 가깝습니다. 그렇기 때문에 외부 API 호출이 많아지고, timeout, retry, logging 같은 세밀한 제어가 필요해질수록 다소 불편함이 생길 수 있습니다.

예를 들어, RestTemplate에서 요청/응답 로깅이나 공통 처리를 하려면 보통 ClientHttpRequestInterceptor를 사용합니다. 

config에서 등록

즉, 공통 logging이나 header 처리를 위해서는 ClientHttpRequestInterceptor를 직접 구성해야 합니다. 물론 확장은 가능하지만, 외부 API 호출이 많아질수록 설정 코드 역시 점점 증가할 가능성이 높습니다.

FeignClient

그렇다면 Feign은 어떨까요? 편의상 Client는 빼고 Feign이라고 부르겠습니다. Feign은 선언형 방식으로 동작합니다.

그렇다면 선언형이란 무엇일까요? 위에서 살펴본 RestTemplate은 필요한 위치에서 직접 객체를 주입받아 메서드를 호출하는 방식입니다. 물론 스프링 빈으로 등록해서 공통으로 사용할 수 있지만, API 호출 코드가 서비스 로직 안에 섞이기 쉽다는 부담이 있습니다.

반면, Feign은 인터페이스 기반으로 외부 API 호출을 정의합니다. 즉, 어떤 URL로 요청을 보낼지, 어떤 메서드를 사용할지, 어떤 값을 파라미터로 전달할지를 인터페이스에 선언해두고 사용할 수 있습니다. 이런 특징 덕분에 외부 API 호출 규격을 한곳에 모아 관리하기 쉽고, 코드도 비교적 간결해집니다.

또한 서비스 간 호출 구조가 많은 MSA 환경에서는 각 서비스의 API 호출을 Feign 인터페이스로 분리해 관리할 수 있기 때문에 자주 사용됩니다.

그렇다면 비동기는 어떻게 지원할까요?

아쉽게도 Feign 또한 기본적으로 동기 방식으로 동작합니다. 그렇기 때문에 RestTemplate와 비슷하게 직관적으로 사용할 수 있다는 장점이 존재합니다.

물론 비동기 방식으로 사용하는 방법도 존재합니다. CompletableFuture와 같은 자바 비동기 기능을 함께 사용할 수도 있고, 비동기를 지원하는 별도의 설정을 추가할 수도 있습니다. 하지만 기본적으로는 동기 호출에 가까운 방식이라고 볼 수 있습니다.

얼핏봤을때 RestTemplate와 비슷하게 보이는데 선언형으로 만들었을때 무슨 장점이 있나?

하지만 선언형으로 구성했을때 가장 큰 차이는 외부 API 호출 자체를 하나의 역할로 분리할 수 있다는 점에 있다고 생각합니다.

예를 들어 RestTemplate는 아래처럼 서비스 내부에서 직접 URL, header, 요청 방식을 작성하는 경우가 많습니다.

이 방식은 간단하지만, 외부 API 호출 코드가 서비스 로직 안으로 들어오게 됩니다. 즉, 비즈니스 로직과 HTTP 호출 로직이 서로 섞이기 쉬워집니다.

반면 Feign은 외부 API 자체를 인터페이스로 분리합니다.

이렇게 구성하면 서비스에서는 외부 API 호출 방법을 신경쓰기보다는, 그냥 메서드를 호출하듯 사용할 수 있습니다. 즉, 선언형 방식의 장점은 단순히 코드가 짧아지는 것이 아니라 외부 API 역할 분리, 유지보수성 증가, 중복 감소,API 호출 규격 관리 용이,테스트 구조 분리 쉬움과 같은 구조적인 장점에 있다고 생각합니다. 특히 MSA처럼 서비스 간 호출이 많아질수록 “HTTP 요청 코드”를 여기저기 작성하는 것보다, 인터페이스 단위로 관리하는 방식이 훨씬 깔끔해질 수 있습니다.

WebClient

이제 마지막으로 WebClient에 대해 이야기해봅시다. WebClient는 Spring WebFlux에서 제공하는 HTTP Client입니다. 기존 RestTemplate와 다르게 비동기 및 논블로킹 방식을 지원한다는 특징이 존재합니다.

그렇다면 논블로킹은 무엇일까요?

기존 RestTemplate는 외부 API 요청을 보내면 응답이 올때까지 현재 스레드가 대기하게 됩니다. 즉, 응답이 돌아오기 전까지 다른 작업을 처리하지 못합니다.

반면 WebClient는 요청을 보낸 이후 응답을 기다리는 동안 스레드를 계속 점유하지 않습니다. 즉, 다른 작업을 수행하다가 응답이 도착했을때 다시 이어서 처리하는 방식에 가깝습니다.

이러한 특징 때문에 외부 API 호출이 많거나, 동시에 많은 요청을 처리해야 하는 환경에서 장점이 존재합니다.

또한 WebClient는 단순히 비동기만 지원하는 것이 아니라, 외부 API 호출 흐름 자체를 세밀하게 제어할 수 있습니다.

예를 들어 timeout, retry, logging, 공통 header, 응답 상태별 예외 처리와 같은 기능들을 비교적 유연하게 구성할 수 있습니다.

물론, RestTemplate 역시 interceptor나 config를 이용해 공통 설정을 구성할 수 있습니다. 하지만 timeout, logging, header 처리와 같은 기능들이 점점 늘어나게 되면 설정 코드가 커지고, 요청 흐름을 파악하기 어려워질 수 있습니다.

반면, WebClient는 builder와 filter 기반으로 흐름을 구성할 수 있기 때문에 요청 처리 과정을 비교적 명확하게 관리할 수 있었습니다.

즉, WebClient의 핵심은 단순히 비동기를 지원한다는 것이 아니라, 외부 API 호출 과정에서 발생할 수 있는 다양한 흐름을 유연하게 제어할 수 있다는 점에 있다고 생각합니다.

그래서 webClient를 선택한 이유가 무엇인가요?

지금까지 RestTemplate, FeignClient, WebClient에 대해 간단하게 알아봤습니다.

가장 러닝 커브가 낮은 것은 RestTemplate, 가장 선언적으로 사용하기 좋은 것은 FeignClient, 그리고 비동기 및 논블로킹 방식을 지원하는 것은 WebClient입니다.

그렇다면 어떤 기술을 도입하는 것이 가장 현실적일까요?

과거에는 러닝 커브가 기술 선택에 굉장히 큰 영향을 주었다고 생각합니다. 하지만 최근에는 AI의 발전으로 인해 새로운 기술을 학습하고 적용하는 비용이 이전보다 많이 낮아졌다고 느껴졌습니다. 그렇기 때문에 단순히 익숙하다는 이유만으로 RestTemplate를 선택해야 할 필요성은 크지 않다고 생각했습니다.

그렇다면 FeignClient는 어떨까요?

FeignClient는 사용처가 굉장히 명확하다고 생각합니다. 특히 MSA 환경에서 서비스 간 통신을 구성할때 장점이 존재합니다. 인터페이스 기반으로 외부 API를 선언하기 때문에 어떤 서비스가 어떤 데이터를 요청하는지 구조를 파악하기 쉽고, 패키지 단위로 역할을 분리해서 관리하기도 편리합니다.

하지만 현재 플랫폼은 아직 MSA 구조가 아닙니다. 즉, 서비스 간 통신이 빈번하게 발생하는 환경이 아니었고, FeignClient의 장점을 크게 활용할 수 있는 상황은 아니라고 판단했습니다.

마지막으로 남은 것은 WebClient입니다.

WebClient의 가장 큰 단점은 러닝 커브라고 생각합니다. Reactive 방식 자체가 기존 동기 방식과 흐름이 다르기 때문에 처음에는 익숙하지 않을 수 있습니다. 하지만 이 역시 AI와 레퍼런스의 증가로 인해 이전보다 학습 부담이 많이 낮아졌다고 느껴졌습니다.

또한, WebClient는 다른 기술들과 다르게 비동기 및 논블로킹 방식을 지원합니다. 즉, 외부 API 응답을 기다리는 동안 스레드를 계속 점유하지 않기 때문에 동시에 많은 요청이 들어오는 환경에서도 비교적 효율적으로 처리할 수 있습니다.

현재 시점에서 실제로 트래픽이 얼마나 증가할지는 확신할 수 없습니다. 하지만 트래픽이 증가한 이후에 외부 API 호출 구조 자체를 변경하는 것은 비용이 크다고 생각했습니다. 반면 초기 단계에서 WebClient를 도입하는 것은 상대적으로 부담이 적다고 판단했습니다.

그렇기 때문에 현재 프로젝트에서는 WebClient를 선택하게 되었습니다.

마무리

AI의 발전으로 인해 과거보다 훨씬 적극적으로 새로운 기술을 선택할 수 있는 환경이 되었다고 생각합니다. 예전에는 러닝 커브나 레퍼런스 부족 때문에 익숙한 기술을 우선적으로 선택하는 경우가 많았습니다.

만약 3년 전에 동일한 프로젝트를 진행했다면, 저 역시 WebClient보다는 기존에 많이 사용해왔던 RestTemplate를 선택했을 가능성이 높다고 생각합니다.

물론 RestTemplate를 사용하는 것이 잘못되었다는 의미는 아닙니다. 실제로 지금도 많은 프로젝트에서 안정적으로 사용되고 있는 기술입니다. 다만 현재 프로젝트에서 중요하게 생각했던 비동기 처리, 외부 API 호출 제어, 그리고 향후 확장성 측면에서는 WebClient가 조금 더 적합한 선택이라고 느껴졌습니다.

결국 중요한 것은 최신 기술을 사용하는 것이 아니라, 현재 프로젝트의 목표와 환경에 가장 잘 맞는 기술을 선택하는 것이라고 생각합니다.

반응형

댓글

Designed by JB FACTORY