티스토리 뷰

우리가 보통 사용하던 Spring MVC + RDBMS 패턴은 Blocking IO 방식이다. Blocking IO 방식이라는 것은 요청을 처리하기 전까지는 다른 작업을 수행할 수 없는 상태라는 것을 말한다. 동시에 여러 요청을 처리하기 위해서는 Thread 수를 늘려서 하는 방법이 존재하기는 하지만 이도 오버헤드가 발생한다. 이를 개선하기 위해 나온 기술이 Non-Blocking IO 방식인 Spring WebFlux 이다. Spring WebFlux 는 동시에 처리되어야 할 많은 요청에 대해 효율적으로 처리해줄 수 있다. 

 

https://spring.io/reactive

The other is a fully reactive stack that takes advantage of Spring WebFlux and Spring Data’s reactive repositories. 

 

지금까지는 프로젝트를 하며 거의 모든 개발을 Servlet Stack을 사용했는데 이제 Reactive Stack을 사용해야 할 프로젝트도 몇개 보인다. 효율적으로 IO를 관리하므로 수많은 요청을 처리해야만 하거나 MSA로 구축을 해야 하는 경우에는 Reactive Stack이 좋을수가 있을것 같다.

 

물론 단점도 있다. 순차적으로 처리되는 방식이 아니라 디버깅이 힘들고 개발이 어렵다. 즉 SI에서 사용하기는 좀 힘든 부분이 있다. 실제로 관련 소스를 찾아보니 이것도 자바인가? 라는 생각이 들정도로 많은 부분이 달랐다. 프로젝트에 본격 적용을 하기에는 시간이 더 필요할것 같다. 그래도 샘플을 살펴보며 공부를 좀 해보자! 


spring.io 에서 Reactive Stack 에 관한 Tutorial 을 제공한다. 

What You’ll Need

이런 기본적인 사항들이 준비가 되어 있어야 한다. 샘플소스는 제공하고 있는 git source 를 사용할 계획이다. 소스를 받아서 import 시켜보면 다음과 같은 모습이다. 

 

하나씩 살펴보자. 

 

pom.xml

<dependencies>
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
  </dependency>

  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
  </dependency>
		
  <dependency>
    <groupId>io.projectreactor</groupId>
    <artifactId>reactor-test</artifactId>
    <scope>test</scope>
  </dependency>
</dependencies>

 

이중 pom.xml 을 열어보면 이렇게 spring-boot-starter-webflux dependency를 가지고 있는것을 확인할 수 있다. 

 

이번엔 hello package 내의 클래스를 하나씩 보면서 어떤 방식으로 동작하는지 알아보자. 

 

Handler

package hello;

import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;

import reactor.core.publisher.Mono;

@Component
public class GreetingHandler {

  public Mono<ServerResponse> hello(ServerRequest request) {
    return ServerResponse.ok().contentType(MediaType.TEXT_PLAIN)
      .body(BodyInserters.fromValue("Hello, Spring!"));
  }
}

 

Spring Reactive 접근 방식에서는 Handler를 사용하여 요청을 처리하고 응답을 생성한다. 이 예제에서는 요청에 대해 "Hello, Spring!"을 반환하는 역할을 해주고 있다. 여기서 Mono라는 객체가 나오는데 이것은 Reactor에서 결과값을 처리하기 위한 객체라고 이해하면 된다. 주로 0개~1개의 결과값은 Mono에 담고 Flux 객체는 Mono와 유사한데 0개~n개의 결과값을 처리하는 객체이다. 

 

Router

package hello;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;

@Configuration
public class GreetingRouter {

  @Bean
  public RouterFunction<ServerResponse> route(GreetingHandler greetingHandler) {

    return RouterFunctions
      .route(RequestPredicates.GET("/hello").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)), greetingHandler::hello);
  }
}

 

Spring MVC의 Controller의 역할 중 RequestMapping의 역할을 하는 녀석이 Router라고 생각하면 이해가 빠를것 같다.  예제에서는 /hello 라는 요청을 greetingHandler::hello 에 매핑을 시켜주고 있다. 

 

WebClient

package hello;

import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;

import reactor.core.publisher.Mono;

public class GreetingWebClient {
  private WebClient client = WebClient.create("http://localhost:8080");

  private Mono<ClientResponse> result = client.get()
      .uri("/hello")
      .accept(MediaType.TEXT_PLAIN)
      .exchange();

  public String getResult() {
    return ">> result = " + result.flatMap(res -> res.bodyToMono(String.class)).block();
  }
}

 

WebClient는 외부와 통신을 하는 창구 역할을 한다고 보면 된다. 기존에는 RestTemplate을 이용해서 HTTP 통신을 했지만 이것은 Blocking IO 방식이다. 그래서 Non-Blocking IO 방식과 비동기 방식을 지원하기 위한 WebClient 를 만들었고 WebFlux에서는 이것을 써야 한다. 가장 간단한 예제라 create()를 통해서 WebClient를 생성했지만 다른 옵션들을 더 추가하려면 build() 를 써야 한다. 더욱 자세한 WebClient의 사용법은 이 포스팅을 참고하면 좋다. 

 

Application.java

package hello;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);

    GreetingWebClient gwc = new GreetingWebClient();
    System.out.println(gwc.getResult());
  }
}

 

SpringBootApplication 클래스는 사용법이 다르지 않다. 그대로 사용해주면 된다. 

 

 

 

서버는 tomcat 이 아닌 embedded Netty 를 사용한다. Application.java 에서 WebClient에서 result를 가지고 오라고 되어 있어서 구동할때도 result = Hello, Spring! 을 출력해준다. 또한 http://localhost:8080/hello 를 입력하면 result 인 Hello, Spring! 가 출력되는 것을 볼수 있다. 

 

spring 공홈에 tutorial 이 잘 되어있어 보고 읽어보고 실행해본것이 전부라 WebFlux에 대한 이해는 아직 많이많이많이 필요한 상황이다. 대략적인 감은 잡았고 나중에 실전에서 쓸때 다시 파보자!

 

끝!

댓글
최근에 올라온 글
최근에 달린 댓글
«   2024/05   »
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