Junjangsee's Blog

SpringBoot - RestClient

2019-05-16

images

RestClient

Rest를 클라이언트에서 요청하였을 때 어떻게 응답할지 결정하는 방식입니다.
RestClient에는 두 종류가 있는데 각각의 특징과 사용법을 학습해보겠습니다.

  • RestTemplate
  • Blocking I/O 기반의 Synchronous(동기식) API입니다.
  • RestTemplateAutoConfiguration에서 자동설정 됩니다.
  • 프로젝트에 spring-web 의존성이 있다면 RestTemplate​Builder​를 빈으로 등록합니다.
  • 참고자료 : 스프링공식문서

  • 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";
}
}

RestClient 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 값을 가져오도록 하고 출력합니다.
RestClient 위처럼 맨 처음 요청한 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를 하게되면 출력한다고 생각하면 됩니다.
RestClient 먼저 출력된 world 3초 + hello 2초 = 총 5초가 소요되었고 비동기로 출력된 것을 볼 수 있습니다.

이처럼 동기식보다 시간적 효율성이 높고 응답 시간도 빨라 개발자들은 보통 WebClient를 선호합니다.