Spring5 버전에서 새롭게 추가된 기술 스틱이 바로 리액티브(Reactive)
스택이자 웹 프레임워크입니다.
Flux
는 Reactor
의 Mono
와 함께 사용되는 N개 이상의 데이터를 emit하는 publisher
가 맞습니다.
Spring MVC가 이미 많은 역할을 수행할 수 있는데 Spring WebFlux가 별도로 존재하는 데는 둘의 차이점을 비교함으로 이해할 수 있을 것입니다.
Reactive | Servlet |
---|---|
Netty,Servlet 3.1+ | Servlet |
Reactive Streams Adapters | Servlet API |
Spring Security Reactive | Spring Security |
Spring WebFlux | Spring MVC |
Mongo,Redis,R2DBC... | JDBC,JPA,NoSQL... |
이외에도 리액티브
는 Non-blocking
통신을 지원하지만 MVC
는 Blocking
통신 방식을 지원합니다. 리액티브는 Non-Blocking
을 지원하기 위해 Blocking
될 수 있는 부분들이 모두 Non-Blocking
통신이 가능하도록 만들어야 합니다.
Spring MVC와 Spring WebFlux는 에너테이션
, 웹 서버
를 그대로 사용할 수 있으며 로직이나 DB등에서 다른 구현체를 사용함을 알 수 있습니다.
MVC
를 사용할때와 WebFlux
를 사용할때 어떻게 다른 지는 간단한 예제를 확인 해봄으로써 조금 더 이해할 수 있을 것 입니다.
우선 구현하기전 A서버와 B서버를 다음과 같이 가정하겠습니다.
A서버
: 다른 서버로 요청을 보냅니다. MVC
인 경우와 WebFlux
인 경우 2가지로 나뉘어 보겠습니다.
B서버
: 단순히 A서버에 응답합니다. 응답 직전 5초
를 기다립니다.
A서버-애플리케이션
-> A서버-컨트롤러
-> B서버-컨트롤러
순으로 호출하여 각각의 동작 결과가 어떻게 다른지 확인하겠습니다.
@RestController
@RequestMapping("B서버")
public class B서버 {
@GetMapping("/{URI})
public ResponseEntity 핸들러메서드(@PathVariable("URI") long id) throws InterruptedException {
리턴클래스 리턴객체 = new 리턴클래스(...);
Thread.sleep(5000);
return ResponseEntity.ok(리턴객체);
}
}
B서버
는 단순 리턴객체를 만들고 5초를 기다린 다음 다시 리턴합니다.
@Slf4j
@SpringBootApplication
public class A서버-Application{
...
@Bean
public CommandLineRunner run(){
return (String... args) -> {
for(int i=0; i<5; i++){
리턴클래스 리턴객체 = this.get객체();
log.info("응답완료");
}
};
}
private 리턴클래스 get객체(){
RestTemplate restTemplate = new RestTemplate();
String uri = "A서버/URI"
ResonseEntity<리턴클래스> response = restTemplate.getForEntity(uri,리턴클래스.class);
return response.getBody();
}
}
CommandLineRunner
: 웹 애플리케이션 실행 시 수행할 작업을 리턴합니다. 단순히 5번 this.get객체()
를 호출합니다.get객체()
: A서버-컨트롤러
를 호출하여 작업을 위임 합니다.A서버/URI
: A서버의 컨트롤러를 호출하기위한 URL입니다.RestTemplate
: 요청후 응답을 기다리는 동기방식 입니다.getForEntity()
: A서버의 핸들러메서드를 호출함으로써 작업을 위임합니다.A서버-애플리케이션
은 A서버
의 컨트롤러에 요청을 전달하는 요청을 수행합니다. 그렇다면 A서버
의 컨트롤러도 정의해야 합니다.
@Slf4j
@RestController
@RequestMapping("A서버")
public class A서버-MVC{
private final RestTemplate restTemplate;
String uri = "B서버/URI"
public A서버-MVC(RestTemplateBuilder restTemplateBuilder){
this.restTemplate = restTemplateBuilder.build();
}
@GetMapping("{URI}")
public ResponseEntity 핸들러메서드(@PathVariable("URI") long id){
log.info("요청 수신");
ResponseEntity<리턴클래스> response = restTeoplate.getForEntity(uri,리턴클래스.class);
return ResponseEntity.ok(response.getBody());
}
}
RestTemplateBuilder
: 스프링 컨테이너에 존재하고, RestTemplate를 반환하는 builder 클래스입니다.uri
: B서버를 호출하기 위한 주소입니다.getForEntity()
: B서버를 단순히 호출합니다.순서를 보자면 A서버-애플리케이션
-> A서버-컨트롤러
-> B서버-컨트롤러
순으로 호출이 됩니다.
실행결과 로그는 아래와 같습니다.
요청 수신
//5초
응답 완료
요청 수신
//5초
응답 완료
요청 수신
//5초
응답 완료
요청 수신
//5초
응답 완료
요청 수신
//5초
응답 완료
MVC
방식은 Blocking
방식이기 때문에 요청후 응답을 기다립니다. 그래서 B서버가 5초
동안 대기하는동안 A서버도 같이 대기합니다.
이번에는 SpringMVC방식을 WebFlux방식으로 바꾸었을때 위의 결과가 어떻게 달라지는지 확인해보겠습니다.
@RestController
@RequestMapping("B서버")
public class B서버 {
@ResponseStatus(HttpStatus.OK)
@GetMapping("/{URI})
public Mono<리턴클래스> 핸들러메서드(@PathVariable("URI") long id) throws InterruptedException {
리턴클래스 리턴객체 = new 리턴클래스(...);
Thread.sleep(5000);
return Mono.just(리턴객체);
}
}
기존의 Blocking
방식의 ResponseEntity<>
를 리턴하던것에서 Non-Blocking
통신을 위해 Reacotr의 publisher인 Mono
를 리턴합니다.
@Slf4j
@SpringBootApplication
public class A서버-Application{
...
@Bean
public CommandLineRunner run(){
return (String... args) -> {
for(int i=0; i<5; i++){
this.get객체()
.subscribe(
response -> {
log.info("응답 완료");
}
);
}
};
}
private Mono<리턴클래스> get객체(){
String uri = "A서버/URI"
return WebClient.create()
.get()
.uri(uri)
.retrieve()
.bodyToMono(리턴클래스.class);
}
}
A서버-애플리케이션
도 마찬가지로 publisher-subscribe
구조로 작동할 수 있도록 Mono클래스로 감싸서 리턴합니다.
메서드의 역할은 메서드명으로 기존의 역할들을 직적접으로 해석이 가능하므로 생략하겠습니다.
@Slf4j
@RestController
@RequestMapping("A서버")
public class A서버-WebFlux{
String uri = "B서버/URI"
@ResponseStatus(HttpStatus.OK)
@GetMapping("{URI}")
public Mono<리턴클래스> 핸들러메서드(@PathVariable("URI") long id){
log.info("요청 수신");
return WebClient.create()
.get()
.uri(uri)
.retrieve()
.bodyToMono(리턴클래스.class);
}
}
A서버-애플리케이션
-> A서버-컨트롤러
-> B서버 컨트롤러
순으로 호출되고 리턴클래스의 객체를 호출하는것, B서버
에서 5초 대기, 출력등은 모두 동일합니다. 다만 기존의 RestTeplate
,ResposeEntity
로 감싸서 리턴하던 것을 Mono
클래스로 감싸서 Non-Blocking
통신하는 것만 달라졌습니다.
실행 결과는 아래와 같습니다.
요청 수신
요청 수신
요청 수신
요청 수신
요청 수신
//5초
응답 완료
응답 완료
응답 완료
응답 완료
응답 완료
A서버
가 B서버
가 5초 대기하는 것을 기다려 주지 않고 다음 작업을 요청하는 Non-Blocking
통신을 하고 있는 것을 확인할 수 있습니다.
더 깊게 학습을 하려면 MVC와 별개로 학습하고 익혀야 하는 내용이 매우 많습니다. 아직은 MVC와는 다른 WebFlux는 이런게 있고 이런식으로 다르게 동작한다 정도로 익혀두는게 좋을 것 같습니다.
없음!