스프링 REST 클라이언트

스프링 REST 클라이언트

  • Spring에서 제공하는 Http 통신에 사용 을 할 수 있는 Rest한 Client Template
  • REST 클라이언트는 스프링 프레임워크가 제공하는 것이고, 스프링부트는 REST 클라이언트를 쉽게 사용할 수 있도록 RestTemplateBuilder, WebClient.Builder을 Bean으로 등록
  • Builder를 주입받아, 필요 시 build()를 통해 rest client 인스턴스를 생성해서 사용

 

spring6 (spring-boot 3) 에서 활용 가능한 3가지 Rest API 활용 방법

  1. RestTemplate
  2. WebClient
  3. HttpInterface

 

RestTemplate


  • Blocking I/O 기반의 Synchronous API이다.
  • 많은 사람들이 사용하고 있으며, 가장 단순하고 사용 방법도 가장 간단하다.
  • Spring Web 의존성을 추가하여 프로젝트를 생성하고 Test
//2개의 Handler 생성
@RestController
public class SampleController {

    @GetMapping("/test")
    public String test() throws InterruptedException {
        Thread.sleep(5000L);
        return "test";
    }

    @GetMapping("/test2")
    public String test2() throws InterruptedException{
        Thread.sleep(3000L);
        return "test2";
    }
}
/*
	REST API의 클라이언트라고 가정.
	Spring web 모듈이 들어있기 때문에 RestTemplate을 사용가능 하기에 
	RestTemplateBuilder를 주입
*/

@Component
public class RestRunner implements ApplicationRunner {
    
    @Autowired RestTemplateBuilder restTemplateBuilder;
    @Override
    public void run(ApplicationArguments args) throws Exception {
				// build를 하면 RestTemplate가 생성
				RestTemplate restTemplate = restTemplateBuilder.build(); 

				// Blocking I/O 기반의 Synchronous API임을 확인하기 위해 Thread를 적용
				// 타이머를 통해 시간 체크
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        
				// 2번 째 인자 ->원하는 응답 타입
        String testResult = restTemplate.getForObject("http://localhost:8080/test", String.class);
        System.out.println(testResult);
        String testResult2 = restTemplate.getForObject("http://localhost:8080/test2", String.class);
        System.out.println(testResult2);

        stopWatch.stop();
        System.out.println(stopWatch.prettyPrint());
    }
}
위 코드를 실제로 실행해보면 Blocking I/O 기반의 Synchronous API이기에 첫 번째 호출이 끝나기 전까지는 다음 호출이 수행되지 않고 지연된다.

 

 

WebClient


  • Non-Blocking I/O 기반의 Asynchronous API
  • WebClient를 사용하려면, WebFlux가 의존성 추가
// Maven
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

//Gradle
implementation 'org.springframework.boot:spring-boot-starter-webflux'
@Component
public class WebClientRestRunner implements ApplicationRunner {

    //WebClient.Builder 주입
    @Autowired WebClient.Builder builder;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        // WebClient 생성
        WebClient webClient = builder.build();
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        Mono<String> testMono = webClient.get().uri("http://localhost:8080/test")
                .retrieve()
                .bodyToMono(String.class);

				// stream api이기 때문에 subscribe
        testMono.subscribe(s -> {
            System.out.println(s);

            if(stopWatch.isRunning()){
                stopWatch.stop();
            }
            System.out.println(stopWatch.prettyPrint());
            stopWatch.start();
        });

        Mono<String> test2Mono = webClient.get().uri("http://localhost:8080/test2")
                .retrieve()
                .bodyToMono(String.class);

        test2Mono.subscribe(s -> {
            System.out.println(s);

            if(stopWatch.isRunning()){
                stopWatch.stop();
            }
            System.out.println(stopWatch.prettyPrint());
            stopWatch.start();
        });
    }
}
다음과 같이 결과를 보면 Non-Blocking I/O 기반의 Asynchronous API이기에 자체적으로 설정한 3초 지연이 끝나면 바로 응답되는 것을 확인할 수 있다.

 

 

HttpInterface


  • WebClient를 확장해 사용하는 방법
  • 바로 사용하기 위한게 아니라 @Bean 으로 서비스를 등록해 Reactive하게 사용
  • 복잡한 Reactive 처리과정은 모두 spring 에게 맡기기 위한 방법
  • 복잡하기에 그냥 WebClient 로 잘 활용하자
interface ErApi {
    @GetExchange("/v6/latest")
    fun latest(): Map<String, Any>
}
val factory = HttpServiceProxyFactory
    .builder(WebClientAdapter.forClient(webClient))
    .build()
val service = factory.createClient<ErApi>()
val res = service.latest() as Map<String, Map<String, Double>>
println("${res["rates"]?.get("KRW")} from HttpInterface 1")

 

  • ErApi 인터페이스를 서비스로 만들어 @Bean으로 등록
  • @Configuration 선언된 클래스 안에서 선언
@Bean
fun erApi(webClient: WebClient): ErApi = HttpServiceProxyFactory
    .builder(WebClientAdapter.forClient(webClient))
    .build()
    .createClient()

 

  • @Component 등의 스프링 주입을 받을 수 있는 클래스에서 해당 서비스를 주입받아 사용 가능
@Bean
fun runner(erApi: ErApi) = ApplicationRunner {
    val res = erApi.latest() as Map<String, Map<String, Double>>
    println("${res["rates"]?.get("KRW")} from HttpInterface 1")
}

 

 

참고자료

Spring Rest 클라이언트(Client) : RestTemplate과 WebClient (goodgid.github.io)

 

Spring Rest 클라이언트(Client) : RestTemplate과 WebClient

Index

goodgid.github.io

spring6 3가지 rest-client 사용하기 (tistory.com)

 

spring6 3가지 rest-client 사용하기

spring6-rest-clients-sample spring6 (spring-boot 3) 에서 활용 가능한 3가지 Rest API 활용 방법을 확인해 보자. 환율을 공개하는 API https://open.er-api.com/v6/latest를 통해 한국 원화 환율을 확인해 보는 샘플을 작성

erim1005.tistory.com

 

 

'F.W > Spring & Spring Boot' 카테고리의 다른 글

@SpringBootTest, @WebMvcTest  (1) 2023.05.12
스프링 컨테이너  (0) 2023.05.11