https://youtu.be/Kb37Q5GCyZs?si=OPyiQb3Yz0MZdNnT
토비님 유튜브를 보고 한 번 따라해보았습니다. api 호출을 할 때 쉽게는 RestTemplate을 이용하는데요. 현재 업무를 진행하는 곳에서는 스프링 기반의 프레임워크가 아니라 다른 방식으로 api 호출을 하는데, 친구들 이야기를 들어보면 FeignClient, WebClient 등을 이용하는 것 같습니다.
토비님이 제시한 3가지 REST clients는 RestTemplate, WebClient, 그리고 spring 6부터 나오게 된 HTTP interface 입니다. RestTemplate과 WebClient는 실습 코드만 간단히 올리고, HTTP interface에 대해서 좀 더 자세히 살펴보겠습니다.
기본 환경설정과 의존성은 유튜브에 잘 나와있기 때문에 생략하고 코드에 집중해보겠습니다.
1. RestTemplate
String url = "https://open.er-api.com/v6/latest";
RestTemplate rt = new RestTemplate();
Map res = rt.getForObject(url, Map.class);
System.out.println(res);
- RestTemplate은 간편하고 직관적입니다. 손쉽게 api를 호출하는 코드를 작성하는 데 무리가 없습니다.
- 하지만 기본적으로 blocking 하는 sync 호출만 가능하기에 async 호출이 필요한 상황이거나 복잡한 구현이 필요한 경우에는 쓰기에 무리가 있습니다.
2. WebClient
WebClient client = WebClient.create("https://open.er-api.com");
Map res = client.get()
.uri("/v6/latest")
.retrieve()
.bodyToMono(Map.class)
.block();
System.out.println(res);
- WebFlux를 사용할 경우에 가능한 호출입니다. reactive programming이 가능하고, 필요 시 비동기 호출도 지원합니다.
- 그렇지만 WebFlux를 사용하지 않는 경우 사용할 수 없고, 토비님이 말씀해주신 것처럼 Mono, Flux 등의 개념을 이해해야 하는 등 러닝 커브가 존재합니다. 추가적으로 throughput을 늘리기 위해 비동기 호출을 기본적으로 활용하므로 호출 간 의존적인 구조 등이 있다면 활용하기에 어려운 점이 있을겁니다. Mono로 받아서 blocking 호출을 할 것이라면 사실 WebFlux를 사용하는 이점을 버리는 것이므로 RestTemplate을 활용하는 것이 더 나을 것입니다.
3. HTTP interface
https://docs.spring.io/spring-framework/reference/integration/rest-clients.html#rest-http-interface
proxy 코드를 만드는 bean을 만들어두고, interface만 선언하여 주입 받아 사용만 하는 방식입니다. 토비님 영상은 1년 전이라 아마 6.0.x 대 spring 기준으로 설명하여 WebClientAdapter.forClient() 메서드를 호출하고 있습니다. spring 6.1 부터 해당 메서드가 deprecated 되어 builderFor - WebClientAdapter.create() 메서드를 호출하도록 변경하였습니다.
(WebClientAdapter의 java docs를 확인해보면 다른 adapter implementations와 맞추기 위해 deprecated 되어 있다고 나와 있습니다)
@Bean
ErApi erApi() {
WebClient client = WebClient.create("https://open.er-api.com");
HttpServiceProxyFactory httpServiceProxyFactory = HttpServiceProxyFactory
.builderFor(WebClientAdapter.create(client))
.build();
return httpServiceProxyFactory.createClient(ErApi.class);
}
interface ErApi {
@GetExchange("/v6/latest")
Map getLatest();
}
//runner(ErApi 주입 후)
Map res = erApi.getLatest();
System.out.println(res);
- proxy 코드를 만들어 두고 특정 도메인을 호출하는 client를 생성해두면, 해당 api를 호출하는 쪽에서는 interface만을 선언해 간편히 관리할 수 있습니다.
- 위에서 예시로 사용한 ErApi interface를 사용하는 측은 어떤 구현체인지는 신경 쓸 필요 없이 해당 인터페이스의 spec만 알면 손쉽게 사용할 수 있습니다. interface layer를 추가하여 사용하는 측이 활용하는 데 필요한 정보만을 전달할 수 있게 된 것입니다.
- 토비님 말씀으로는 앞으로 spring data jpa 처럼 interface만 선언해두면 구현체를 자동으로 생성해주는 것이 나올 것으로 예상된다고 합니다. 1년이 지난 시점에서 아직 지원은 하지 않는 것 같지만(제가 못 찾은 걸수도..), jpa에서 jpql을 사용할 필요가 생기는 상황이라던지, 인터페이스를 사용하면서 생기는 특수한 상황들에 대응하는 기술들이 계속해서 나올 것으로 보입니다.
- 개인적으로는 캡슐화를 해두면 해당 기능에 대해서 파악하는 데 드는 시간과 비용이 더 드는 것 같습니다. 러닝커브가 있음에도 해당 이점을 누릴 수 있다면 충분히 도입해볼 필요가 있지만, 아직 기술 지원이 이루어지지 않고 있고 레퍼런스가 부족한 상태에서 무작정 운영에 도입하는 것은 다소 위험할 수 있을 것 같습니다.
'spring' 카테고리의 다른 글
Spring Security에서 많이 쓰는 패스워드 암호화(Bcrypt) (0) | 2024.07.07 |
---|---|
spring rabbitmq 무작정 따라하기 (1) | 2024.06.02 |
chaos engineering과 chaos monkey 사용법 (0) | 2024.05.26 |
Spring boot 도커 hub에 올리고 로컬 DB(MySQL)와 연동하기(feat. github actions) (0) | 2023.05.19 |