본문 바로가기
학습/Spring

[spring] 스프링 부트에서 REST Client 이용하기

by KKambi 2020. 5. 23.

스프링 REST Client

  • 스프링에서 제공
  • 스프링 부트는 쉽게 사용할 수 있도록 자동설정 제공
  • 주의 : RestTemplate / WebClient 자체를 빈으로 등록 X
    → RestTemplateBuilder / WebClient.Builder를 빈으로 등록 O

 

 

1. RestTemplate

  • Blocking I/O 기반의 Synchronous API
  • RestTemplateAutoConfiguration
  • 프로젝트에 spring-web 모듈이 있다면 RestTemplateBuilder를 빈으로 등록

 

 

2. WebClient

  • Non-Blocking I/O 기반의 Asynchronous API
  • WebClientAutoConfiguration
  • 프로젝트에 spring-webflux 모듈이 있다면 WebClient.Builder를 빈으로 등록

 

 

예제로 이해하기

  1. @RestController 구현
    • 3초를 sleep하는 GetMapping
    • 5초를 sleep하는 GetMapping
  2. REST Client요청은 ApplicationRunner 구현 클래스에서 보내보자
@RestController
public class SampleController {
	
    @GetMapping("/hello")
    public String hello() throws InterruptedException {
    	Thread.sleep(3000L);
        return "hello";
    }
    
    @GetMapping("/sample")
    public String sample() throws InterruptedException {
    	Thread.sleep(5000L);
        return "sample";
    }
}

 

 

방법1. RestTemplateBuilder를 주입받아 RestTemplate 생성

  • getForObject로 get요청 보내기
  • blocking i/o : 호출된 함수가 자신의 작업을 모두 마칠 때까지 호출한 함수에게 제어권을 넘겨주지 않고 대기하게 만듬
  • sync : 호출된 함수의 작업 완료 여부를 신경쓰기 때문에 작업을 마칠 때까지 기다린다.
@Component
public class RestRunner implements ApplicationRunner {

	@Autowired
	public RestTemplateBuilder restTemplateBuilder;

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

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

          String helloResult = restTemplate.getForObject("http://localhost:8080/hello", String.class);

          String sampleResult = restTemplate.getForObject("http://localhost:8080/sample", String.class);

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

 

방법2. WebClient.Builder를 주입받아 WebClient 생성

  • get().uri().retrieve().bodyToMono(String.class)로 GET요청 보내기
  • Stream API를 사용하기 때문에 subscribe()로 결과를 반환해야 함
  • non-blocking i/o : 호출된 함수가 바로 결과를 반환하여, 호출한 함수에게 제어권을 바로 넘겨준다.
  • async : 호출된 함수의 작업 완료 여부를 신경쓰지 않기 때문에, 작업 완료 시 호출된 함수는 전달받은 콜백을 실행하기만 한다.

cf) subscribe()는 Javascript의 .then()을 생각하면 될 듯!

Promise 객체가 fulfilled되도 then을 쓰지 않으면 resolve()의 인자값을 꺼낼 수 없는 것처럼

내가 보냈던 요청에 대한 응답 이벤트를 받을 때, 어떤 행동을 수행할 것인지 이벤트 핸들러를 정의해주는 일이다.

 

@Component
public class RestRunner implements ApplicationRunner {

    @Autowired
    WebClient.Builder webClientBuilder;


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

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

        Mono<String> helloResult = webClient.get().uri("http://localhost:8080/hello")
                                        .retrieve()
                                        .bodyToMono(String.class);
        helloResult.subscribe(result -> {
            System.out.println(result);

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

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

        Mono<String> worldResult =  webClient.get().uri("http://localhost:8080/sample")
                                        .retrieve()
                                        .bodyToMono(String.class);
        worldResult.subscribe(result -> {
            System.out.println(result);

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

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

 

 

해당 코드의 결과

  1. RestTemplate
    • 5초 째에 "hello"를 출력하고, 8초 째에 "sample"을 출력한다.
    • sync-blocking : /hello GET요청이 끝날 때까지 기다리고, /sample GET요청이 끝날 때까지 기다림
    • 따라서 두 요청이 모두 끝나려면 8초가 걸린다.
  2. WebClient
    • 3초 째에 "hello"를 출력하고, 5초 째에 "sample"을 출력한다.
    • async-nonblocking : /hello GET요청과 /sample GET요청이 병렬적으로 수행된다.
    • 따라서 두 요청이 모두 끝나려면 5초가 걸린다.

 


스프링 REST Client 커스터마이징

WebClient 커스터마이징

  1. webClientBuilder.build()로 빌드하기 전에 필요한 설정 가능
    • baseUrl("http://localhost:8080")
    • defaultCookie( )
    • defaultHeader( )
  2. 글로벌 WebClient 객체 사용하기
    • WebClientCustomizer 빈 재정의
    • ApplicationContext에서 전역적으로 적용한다.
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(Application.class);
        app.run(args);
    }

    @Bean
    public WebClientCustomizer webClientCustomizer(){
        return new WebClientCustomizer() {
            @Override
            public void customize(WebClient.Builder webClientBuilder) {
                webClientBuilder.baseUrl("http://localhost:8080");
            }
        };
    }
}

 

 

RestTemplate 커스터마이징

  1. RestTemplateBuilder 빈 재정의
    • org.apache.httpcomponents:httpclient 의존성 추가 필요
    • 마찬가지로 ApplicationContext에서 전역적으로 정의
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication app = new SpringApplication(Application.class);
        app.run(args);
    }

    @Bean
    public RestTemplateCustomizer restTemplateCustomizer() {
        return new RestTemplateCustomizer() {
            @Override
            public void customize(RestTemplate restTemplate) {
                restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());
            }
        };
    }
}

 

 

블록킹, 논블록킹 / 동기, 비동기 참고 링크 (이해가 쉽게 설명 잘 해주신다)

homoefficio.github.io/2017/02/19/Blocking-NonBlocking-Synchronous-Asynchronous/

 

Blocking-NonBlocking-Synchronous-Asynchronous

꽤 자주 접하는 용어다. 특히나 요즘들어 더 자주 접하게 되는데, 얼추 알고는 있고 알고 있는게 틀린 것도 아니지만, 막상 명확하게 구분해서 설명하라면 또 만만치가 않은.. 그래서 찾아보면 ��

homoefficio.github.io

 

댓글