본문 바로가기
Develop/Spring˙Spring Boot

[Spring Reactive] WebFlux를 사용해 Reactive Spring Boot 구성해보기

by 독서왕뼝아리 2023. 3. 8.

Spring Boot WebFlux를 사용해 리액티브 스프링을 사용할 것입니다. maven으로 빌드를 해야 해서 골치가 조금 아픕니다. (^..^) 

 

 

리액티브 스트림이란? 

발행자(publisher)와 구독자(subscriber) 사이의 간단한 계약을 정의하는 명세다. 트래픽을 가능한 한 빨리 발행하는 대신에 구독자가 '난 10개만 더 받을 수 있어.'라고 발행자에게 알리는 방식으로 트래픽을 제어할 수 있다.

 

리액터란?

리액티브 스트림은 수요 조절에 기반하고 있다. 프로젝트 리액터는 핵심 타입인 Flux<T>를 사용해 수요 조절을 구현한다. Flux<T>는 일련의 T 객체를 담고 있는 컨테이너다.

예시를 들자면 레스토랑에서 서빙 점원과 비슷하다고 할 수 있다. 주방에서 요리가 완성되면, 점원이 요리를 받아서 손님에게 가져다 주고, 다시 제자리로 돌아와서 다음 요리를 기다린다. 점원은 요리가 언제 완성될지 모른다.

 

점원(Reactor)은 언제가 됐든 요리 완성에 대한 반응-행동(React)한다.

 

 

코드

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

pom.xml 에 <dependencies> 태그 하위에 webflux 의존성을 추가한다.

 

도메인

public class Dish {
    private String description;
    private boolean delivered = false;

    public static Dish deliver(Dish dish){
        Dish deliveredDish= new Dish(dish.description);
        deliveredDish.delivered= true;
        return deliveredDish;
    }

    public Dish(String description) {
        this.description = description;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public boolean isDelivered(){
        return delivered;
    }

    @Override
    public String toString() {
        return "Dish{" +
                "description='" + description + '\'' +
                ", delivered=" + delivered +
                '}';
    }
}

 

간단히 개념을 확인하기 위한 하드코딩된 도메인이다.


 
컨트롤러

@RestController // 1
public class ServerController {
    private final KitchenService kitchen;

    public ServerController(KitchenService kitchen){
        this.kitchen=kitchen;
    }

    @GetMapping(value="/server", produces = MediaType.TEXT_EVENT_STREAM_VALUE) // 2
    Flux<Dish> serveDishes(){
        return this.kitchen.getDishes();
    }
    
}
  1.  결과 데이터를 직렬화하고 HTTP 응답 본문에 직접 써서 반환하는 REST 컨트롤러임을 나타낸다.
  2. 반환되는 미디어 타입은 'text/event-stream'이고, 클라이언트는 서버가 반환하는 스트림을 쉽게 소비할 수 있다. (미디어타입을 지정하지 않으면 스트림을 소비하지 못한다.)

 
 
서비스

@Service
public class KitchenService {
    private List<Dish> menu = Arrays.asList(new Dish("Sesame chicken"),
            new Dish("Lo mein noodles"),
            new Dish("Sweet & sour beef"));

    private Random picker = new Random();

    Flux<Dish> getDishes(){
        return Flux.<Dish> generate(sink -> sink.next(randomDish()))
                .delayElements(Duration.ofMillis(250));
    }

    private Dish randomDish() {
        return menu.get(picker.nextInt(menu.size()));
    }

}

 Flux.generate()를 사용해서 요리를 연속적으로 계속 만들어 제공한다. 요리 제공 속도를 조절하기 위해 delayElements(Duration.ofMillis(250))을 사용해 250 밀리초마다 하나의 요리를 제공한다.
 
 

MVC 모델과 크게 다른 점은 없으나 Flux<?> 리액터 타입으로 반환하는 점이 차이가 난다. 


실행 결과

 

웹을 이용해서 '/server'에 GET 요청을 보낸 후 응답이다. 250미리초 간격으로 data가 생성되는 것을 볼 수 있다.
 
 
 

'/serve-dishes' GET 요청은 Flux<Dish> 결과를 스트림 방식으로 반환하는 것은 동일하지만 dish를 deliver() 매핑함수를 사용해 매핑 후 반환한다는 점이 다르다. delivered 값이 true로 설정된 새로운 요리를 만들어 낸다.
 
 
 
 
CLI로 도구를 사용해서도 요청할 수 있다.

curl -N -v localhost:8080/server