스프링 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를 빈으로 등록
예제로 이해하기
- @RestController 구현
- 3초를 sleep하는 GetMapping
- 5초를 sleep하는 GetMapping
- 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();
});
}
}
해당 코드의 결과
- RestTemplate
- 5초 째에 "hello"를 출력하고, 8초 째에 "sample"을 출력한다.
- sync-blocking : /hello GET요청이 끝날 때까지 기다리고, /sample GET요청이 끝날 때까지 기다림
- 따라서 두 요청이 모두 끝나려면 8초가 걸린다.
- WebClient
- 3초 째에 "hello"를 출력하고, 5초 째에 "sample"을 출력한다.
- async-nonblocking : /hello GET요청과 /sample GET요청이 병렬적으로 수행된다.
- 따라서 두 요청이 모두 끝나려면 5초가 걸린다.
스프링 REST Client 커스터마이징
WebClient 커스터마이징
- webClientBuilder.build()로 빌드하기 전에 필요한 설정 가능
- baseUrl("http://localhost:8080")
- defaultCookie( )
- defaultHeader( )
- 글로벌 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 커스터마이징
- 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/
'학습 > Spring' 카테고리의 다른 글
[spring] Spring State Machine (0) | 2020.10.31 |
---|---|
[spring] 스프링 부트에서 @WebMvcTest 사용하며 RestTemplate 주입받기 (0) | 2020.06.11 |
[spring] 스프링 부트에서 Spring Security 커스터마이징하기 (0) | 2020.05.21 |
[spring] 스프링 부트에서 Spring Security 사용하기 (0) | 2020.05.19 |
[spring] 스프링 부트에서 Redis 사용하기 (0) | 2020.05.18 |
댓글