[Spring Boot #31] 스프링 부트 RestTemplate, WebClient
| RestTemplate, WebClient
Spring 기반 프로젝트를 진행하면 컴포넌트 내부에서 URL을 요청해야하는 경우가 생깁니다. Spring에서는 Http 요청을 간단하게 이용할 수 있도록 Blocking I/O 기반의 RestTemplate, Non-Blocking I/O 기반의 WebClient 모듈을 제공하고 있습니다.
| RestTemplate 예제
프로젝트 구조
+---src
| +---main
| | +---java
| | | \---com
| | | \---tutorial
| | | \---webclient
| | | HelloController.java
| | | RestRunner.java
| | | WebclientApplication.java
| | |
| | \---resources
| | | application.properties
| | |
| | +---static
| | \---templates
의존성 관리
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
</dependencies>
소스 코드
@SpringBootApplication
public class WebclientApplication {
public static void main(String[] args) {
SpringApplication.run(WebclientApplication.class, args);
}
}
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() throws InterruptedException {
Thread.sleep(5000);
return "hello";
}
@GetMapping("/world")
public String world() throws InterruptedException {
Thread.sleep(3000);
return "world";
}
}
- /hello, /world 요청이 왔을 때 각각 5초, 3초 후 값을 리턴하는 메서드들을 작성하였습니다.
@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를 통해 스프링 어플리케이션 컴포넌트 내에서 http 요청을 보냅니다. RestTemplate는 Blocking I/O 기반이기 때문에 대략 8초 정도 지나서 모든 요청을 끝마치게 됩니다.
결과 화면
hello
world
StopWatch '': running time (millis) = 8104
-----------------------------------------
ms % Task name
-----------------------------------------
08104 100%
| WebFlux 예제
소스 코드
@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> 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는 Non-Blocking I/O 기반이기 때문에 각 Http 요청이 비동기적으로 발생하게 됩니다. 따라서 위 RestTemplate를 이용하여 Http 요청을 진행했을 때와 다르게 동작하게 되며 총 합쳐 대략 8초 정도가 걸리는 것이 아닌 각각 5초, 3초 걸리는 Http 요청을 동시에 처리하게 됩니다.
- Mono는 WebClient의 결과를 0 또는 1개의 결과를 받는 추상클래스며 Publisher 인터페이스를 구현하여 작성되었습니다. 이 Publisher는 바로 즉각적으로 로직을 실행하는 것이 아닌 subscribe 메서드를 통해 결과를 받아올 코드가 실행될 시 그때서야 로직을 실행하게 됩니다.
결과 화면
world
StopWatch '': running time (millis) = 3527
-----------------------------------------
ms % Task name
-----------------------------------------
03527 100%
hello
StopWatch '': running time (millis) = 5486
-----------------------------------------
ms % Task name
-----------------------------------------
03527 064%
01959 036%
| 커스터마이징
소스 코드
@SpringBootApplication
public class WebclientApplication {
public static void main(String[] args) {
SpringApplication.run(WebclientApplication.class, args);
}
@Bean
public WebClientCustomizer webClientCustomizer() {
return webClientBuilder -> webClientBuilder.baseUrl("http://localhost:8080");
}
@Bean
public RestTemplateCustomizer restTemplateCustomizer() {
return restTemplate -> restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());
}
}
- WebClient를 이용하는 경우 WebClientCustomizer를 반환하는 WebClientBuilder를 통하여 WebClient에 대한 설정을 할 수 있습니다. 위 예시는 baseUrl을 설정함으로써 WebClient를 통한 Http 요청을 할 때 주소를 생략하고 해당 자원을 요청하는 (/hello, /world) 부분만 명시하도록 한 것입니다.
- RestTemplate도 마찬가지로 커스터마이징이 가능하며 위 예시는 아파치의 HttpClient를 쓰도록 커스터마이징 한 것입니다. 기본적으로는 java.net.HttpURLConnection을 사용하지만 위 설정을 통해 HttpClient로 Http 요청을 하도록 바꾼 것입니다.
'Spring Framework > Spring boot #2' 카테고리의 다른 글
[스프링 부트/ Spring Boot] 스프링 게시판 만들기 - 부트로 쉽게 구현한 Spring 게시판 (0) | 2021.03.25 |
---|---|
스프링 부트로 OAuth2 구현(페이스북, 구글, 카카오, 네이버) (0) | 2021.03.25 |
[Spring Boot #32] 스프링 부트 Actuator, JConsole, VisualVM, 스프링 Admin (0) | 2021.03.25 |
[Spring Boot #30] 스프링 부트 시큐리티 커스터마이징 (0) | 2021.03.25 |
[Spring Boot #29] 스프링 부트 시큐리티 (0) | 2021.03.25 |
[Spring Boot #28] 스프링 부트 몽고DB(Mongo DB) 연동하기 (0) | 2021.03.25 |
[Spring Boot #27] 스프링 부트 레디스(Redis) 연동하기 (0) | 2021.03.25 |
[Spring Boot #26] Flyway를 이용한 데이터 마이그레이션 (0) | 2021.03.25 |