본문 바로가기
Java, Kotlin, Spring/Spring, Spring Boot

Springboot - Spring REST Client

by Wordbe 2021. 1. 21.
728x90

 

Springboot - Spring REST Client

 

RestTemplate, WebClient 는 기존 spring-web 에 있던 모듈이다.

스프링부트는 이들을 Builder 로 감싸서 빈으로 등록해주고, 편안한 기능을 제공해준다.

 

 

RestTemplate

Blocking I/O 기반의 Synchronous API 이다.

spring-boot-starter-web 의존성을 등록하면, RestTemplateAutoConfiguration 이 자동 설정된다. 또한 RestTemplateBuilder 를 빈으로 자동 등록해준다.

우선 예제를 위해 컨트롤러를 등록해주자.

@RestController
public class MarketController {

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

    @GetMapping("/snack")
    public String snack() throws InterruptedException {
        Thread.sleep(3000l);
        return "snack";
    }
}

/fruit 을 요청하는데는 5초, /snack 을 요청하는데는 3초가 걸리도록 만들었다.

RestTemplateBuilder 에서 요청한 getForObject 는 다른 기본 프로그램들과 같이 동기적으로 동작할 것이다. 코드 윗줄부터 순차대로 실행된다는 뜻이다. 윗줄이 실행되고 종료가 되어야 아랫줄이 실행된다. 따라서 아래 프로그램은 5초 + 3초 = 8초정도의 시간이 소요될 것이다.

@Component
public class ClientRunner implements ApplicationRunner {

    @Autowired
    RestTemplateBuilder restTemplateBuilder;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        RestTemplate restTemplate = restTemplateBuilder.build();

        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        // 5초 (/fruit)
        String fruitResult = restTemplate.getForObject("http://localhost:8080/fruit", String.class);
        System.out.println(fruitResult);

          // 3초 (/snack)
        String snackResult = restTemplate.getForObject("http://localhost:8080/snack", String.class);
        System.out.println(snackResult);

        stopWatch.stop();
        System.out.println(stopWatch.prettyPrint());
    }
}

 

 


WebClient

Non-Blocking I/O 기반의 Asynchronous(비동기) API 이다.

WebClientAutoConfiguration 이 자동설정되고, spring-boot-starter-web 이 의존성으로 등록되어 있다면 RestTemplateBuilder를 빈으로 등록해준다.

WebClient 사용을 위해서는 webflux를 추가한다.

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
@Component
public class ClientRunner implements ApplicationRunner {

    @Autowired
    WebClient.Builder builer;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        WebClient webClient = builer.build();

        StopWatch stopWatch = new StopWatch();
        stopWatch.start();


        // 5초 (/fruit)
        // Stream 생성 (Non-blocking)
        Mono<String> fruitMono = webClient.get().uri("http://localhost:8080/fruit")
                .retrieve()
                .bodyToMono(String.class);
        // Subscribe (Non-blocking)
        fruitMono.subscribe(s -> {
            System.out.println(s);

            if (stopWatch.isRunning()) stopWatch.stop();

            System.out.println(stopWatch.prettyPrint());
            stopWatch.start();
        });

          // 3초 (/snack)
        // Stream 생성 (Non-blocking)
        Mono<String> snackMono = webClient.get().uri("http://localhost:8080/snack")
                .retrieve()
                .bodyToMono(String.class);
        // Subscribe (Non-blocking)
        snackMono.subscribe(s -> {
            System.out.println(s);

            if (stopWatch.isRunning()) stopWatch.stop();

            System.out.println(stopWatch.prettyPrint());
            stopWatch.start();
        });
    }
}

webClient.get() 요청하는 코드를 Mono 에 담아놓는 스트림을 생성한다. 이는 마치 벽으로 막힌 물처럼 흐르지 않고 제자리에 있다. 즉, 아무 실행도 일어나지 않는다. 그리고 코드는 이것이 실행되지 않아도 아래줄을 읽게 된다. (Non-blocking)

subscribe 메소드를 쓰면 위에서 만들어 놓았던 stream 이 흐른다. 하지만 이역시 Non-blocking 이기 때문에 이 작업이 콜백으로 호출되면서 아래줄의 코드가 그대로 실행된다.

따라서 위 코드를 실행시켜보면, 3초가 걸린 /snack 의 호출이 먼저 종료되고, 동시에 진행중이던 /fruit 요청이 5초 후 종료된다. 따라서 총 소요된 시간은 5초이다.

 

 

 


커스터마이징

RestTemplate

기본적으로 java.net.HttpURLConnection을 사용한다.

지역적(로컬)으로 커스터마이징하는 방법은 아래와 같다.

@Component
public class ClientRunner implements ApplicationRunner {

    @Autowired
    RestTemplateBuilder restTemplateBuilder;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        RestTemplate restTemplate = restTemplateBuilder
                .rootUri("http://localhost:8080") // rootUri 정의
                .build();
                ...
        // 5초 (/fruit)
        String fruitResult = restTemplate.getForObject("/fruit", String.class);
        System.out.println(fruitResult);

        // 3초 (/snack)
        String snackResult = restTemplate.getForObject("/snack", String.class);
        System.out.println(snackResult);
                ...
    }
}

전역적(글로벌) 커스터마이징은 아래와 같다.

@SpringBootApplication
public class SpringgothreeApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringgothreeApplication.class, args);
    }

    @Bean
    public RestTemplateCustomizer restTemplateCustomizer() {
        return restTemplate -> restTemplate.setUriTemplateHandler(new DefaultUriBuilderFactory("http://localhost:8080"));
    }
}

 

RestTemplate 은 기본으로 java.net.HttpURLConnection 을 사용한다고 했다. 이것을 Apache HttpClient 로바꾸고 싶다면 아래와 같이 하면 된다.

1) 의존성을 추가한다.

pom.xml

<dependency>
  <groupId>org.apache.httpcomponents</groupId>
  <artifactId>httpclient</artifactId>
</dependency>

2) 새로운 빈을 HttpComponentsClientHttpRequestFactory 로 재정의하면 된다.

@Bean
public RestTemplateCustomizer restTemplateBuilder() {
  return (RestTemplateCustomizer) restTemplate -> restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());
}

 

 

WebClient

기본적으로 Reactor Netty 기반 HTTP 클라이언트를 사용한다.

지역적(로컬)으로 커스터마이징하는 방법은 아래와 같다.

@Component
public class ClientRunner implements ApplicationRunner {

    @Autowired
    WebClient.Builder builer;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        WebClient webClient = builer
                .baseUrl("http://localhost:8080") // base url 정의
                .build();

          ...
        // Stream 생성 (Non-blocking)
        Mono<String> fruitMono = webClient.get().uri("/fruit")

        ...
        // Stream 생성 (Non-blocking)
        Mono<String> snackMono = webClient.get().uri("/snack")
        ...
    }
}

전역적(글로벌) 커스터마이징은 아래와 같다.

package co.wordbe.springgothree;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.reactive.function.client.WebClientCustomizer;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class SpringgothreeApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringgothreeApplication.class, args);
    }

    @Bean
    public WebClientCustomizer webClientCustomizer() {
        return webClientBuilder -> webClientBuilder.baseUrl("http://localhost:8080");
    }
}

 

 

 

 

 

728x90

'Java, Kotlin, Spring > Spring, Spring Boot' 카테고리의 다른 글

Springboot - Actuator, 운영  (0) 2021.01.21
Springboot - Security  (0) 2021.01.21
Spring Data - MongoDB  (0) 2021.01.21
Spring Data - Redis  (0) 2021.01.21
Spring Data - Neo4j 사용  (0) 2021.01.20

댓글