RestClient Rest를 클라이언트에서 요청하였을 때 어떻게 응답할지 결정하는 방식입니다. RestClient에는 두 종류가 있는데 각각의 특징과 사용법을 학습해보겠습니다.
RestTemplate
Blocking I/O 기반의 Synchronous(동기식) API입니다.
RestTemplateAutoConfiguration에서 자동설정 됩니다.
프로젝트에 spring-web 의존성이 있다면 RestTemplateBuilder를 빈으로 등록합니다.
참고자료 : 스프링공식문서
WebClient
Non-Blocking I/O 기반의 Asynchronous(비동기식) API
WebClientAutoConfiguration에서 자동설정 됩니다.
프로젝트에 spring-webflux 의존성이 있다면 WebClient.Builder를 빈으로 등록합니다.
참고자료 : 스프링공식문서
RestTemplate 사용하기 동기식 으로 응답하는 RestTemplate을 사용해보겠습니다. Web 의존성을 추가한 프로젝트를 생성합니다.
Controller 클래스 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @RestController public class SampleController { @GetMapping ("/hello" ) public String hello () throws InterruptedException { Thread.sleep(5000l ); return "hello" ; } @GetMapping ("/world" ) public String world () throws InterruptedException { Thread.sleep(3000l ); return "world" ; } }
Thread를 활용하여 hello는 5초, world는 3초의 시간을 줍니다. 그리고 클라이언트 입장에서 값을 받기 위해 ApplicationRunner를 사용할 클래스를 생성하고 생성된 클래스에 구현합니다.
ApplicationRunner 클래스 클라이언트의 요청을 가정하여 두 가지의 방법을 사용해보겠습니다.
RestTemplate 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @Component public class RestRunner implements ApplicationRunner { @Autowired 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); System.out.println(helloResult); String worldResult = restTemplate.getForObject("http://localhost:8080/world" , String.class); System.out.println(worldResult); stopWatch.stop(); System.out.println(stopWatch.prettyPrint()); } }
RestTemplate를 사용할 떈 RestTemplateBuilder 의존성을 주입받습니다. 그리고 build()함수를 사용하여 변수에 담고 변수의 기능을 사용해서 REST 요청시 String 값을 가져오도록 하고 출력합니다. 위처럼 맨 처음 요청한 hello 의 5초 + 다음 world 요청인 3초 = 총 8초 정도의 시간에 응답을 완료했다고 출력됩니다. 이처럼 RestTemplate은 요청이 들어오면 다음 코드로 넘어가지 않고 완료되면 넘어가는 동기식 응답을 하는 것을 볼 수 있습니다.
WebClient 1 2 3 4 <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-webflux</artifactId > </dependency >
webflux 의존성을 추가해야만 사용할 수 있습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 @Component public class RestRunner implements ApplicationRunner { @Autowired WebClient.Builder builder; @Override public void run (ApplicationArguments args) throws Exception { WebClient webClient = builder.build(); StopWatch stopWatch = new StopWatch(); stopWatch.start(); Mono<String> helloMono = webClient.get().uri("http://localhost:8080/hello" ) .retrieve() .bodyToMono(String.class); helloMono.subscribe(s -> { System.out.println(s); if (stopWatch.isRunning()) { stopWatch.stop(); } System.out.println(stopWatch.prettyPrint()); stopWatch.start(); }); Mono<String> worldMono = webClient.get().uri("http://localhost:8080/world" ) .retrieve() .bodyToMono(String.class); worldMono.subscribe(s -> { System.out.println(s); if (stopWatch.isRunning()) { stopWatch.stop(); } System.out.println(stopWatch.prettyPrint()); stopWatch.start(); }); } }
WebClient는 WebClient.Builder로 의존성을 주입받습니다. 여기선 Stream API 를 사용할 것인데 Subscribe하기 전 까지 작동하지 않고 대기하다가 Callback이 오면 로직을 수행합니다. 현재 로직에선 Rest 요청이 들어오면 String을 Mono 타입으로 변경 후 대기하다가 subscribe를 하게되면 출력한다고 생각하면 됩니다. 먼저 출력된 world 3초 + hello 2초 = 총 5초가 소요되었고 비동기로 출력된 것을 볼 수 있습니다. 이처럼 동기식보다 시간적 효율성이 높고 응답 시간도 빨라 개발자들은 보통 WebClient를 선호합니다.