본문 바로가기
슬기로운 자바 개발자 생활/스프링 및 자바웹 서비스

스프링부트 WebFlux 스트리밍

by 슬기로운 동네 형 2023. 6. 11.
반응형

스프링부트 WebFlux 스트리밍

스프링부트 리액티브


 간단하게 스프링 부트 Reactive 예제를 만들어 보고 어떤 느낌? 서비스? 인지 알아보도록 한다.

 스프링부트에 리액티브 방식(기술)이 추가된 것을 설명하려면 많은 사전지식과 근 10년 내외의 IT서비스 상황을 이해해야 하므로 글이 너무 길어질 수 있으므로 우선은 모노리틱 아키텍처의 한계를 극복하고 MSA(마이크로 분산서비스)에 적합한  대안? 또는 기술 정도로만 이해하고 있자.

 솔직히 내 생각에는 리액티브 프로그램을 제대로 이해하려면 IT전반의 배경지식도 필요하지만, 블럭킹과 논블럭킹, 동기와 비동기, 스레드와 프로세스까지 어느 정도 깊이 있게 이해를 해야만 제대로 된 이 기술을 익힐 수 있고 왜 익혀야 하는지 동기까지 가질 수 있다. 

 모두들 러닝 곡선이 가파르다고 하는데 그 이유가 여기에 있다.


 전통적인 HTTP 통신 방식은 클라이언트가 서버에 요청을 하고 응답을 받는다. 여기서 두 개체 간 통신은 끝이다. 하지만 리액티브는 다르다. 클라이언트가 서버에 요청을 하면 서버는 응답을 주고 통신을 끊지 않고 계속해서 데이터를 보낸다. 클라이언트도 계속해서 데이터를 받는다.

웹브라우저 상단의 탭의 프로그레스 아이콘이 계속 돌고 웹페이지에 데이터가 계속 출력된다.


끈기지 않고 계속 실행된다.

위의 예제를 만들어 볼 건데, 사실 스프링 웹플럭스가 나오기 전에도 구현은 가능했지만 꽤 어려웠다.

하지만 지금은 스프링 웹플럭스 덕분에 쉽게 구현이 가능하다.


1. Pom.xml 

spring-boot-starter-webflux

메이븐 pom.xml에 스프링부트 스타터 웹플럭스를 추가한다. 나머지는 이클립스든 인텔리J든 기본적인 스프링웹 MVC 설정과 동일하다.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.12</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <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>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

2. 자바 클래스를 만든다.

컨트롤러 하나, 서비스하나, Vo객체 하나

역순으로 Dish.java,  KitchenService.java,  ServerController.java

2-1. Dish.java

package com.example.demo;

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 +
                '}';
    }
}

2-2. KitchenService.java

package com.example.demo;

import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

@Service
public class KitchenService {

    /**
     * 요리 스트림 생성
     */
    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()));
    }

    private List<Dish> menu = Arrays.asList(
            new Dish("Sesame chicken"),
            new Dish("Lo mein noodles, plain"),
            new Dish("Sweet & sour beef"));

    private Random picker = new Random();
}

2-3. ServerController.java

package com.example.demo;

import reactor.core.publisher.Flux;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ServerController {

    private final KitchenService kitchen;

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

    @GetMapping(value = "/server", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    Flux<Dish> serveDishes(){
        return this.kitchen.getDishes();
    }
}

http://localhost:8080/server를 호출한다.

딱히 어려운 로직은 없다.

다만 컨트롤러에 MediaType.TEXT_EVENT_STREAM_VALUE 라는 옵션을 넣었다.

예제를 따라 해서 실행이 된다면, 스트리밍 방식으로 동작하는 웹 서비스를 만드는 데 성공했다.

 

간단한 소스라 딱히 설명은 필요 없지만 컨트롤러를 호출하면 랜덤으로 Dish 정보를 생성해서 클라이언트에게 0.25초 단위로 정보를 넘겨준다. 윈도우의 작업지시창을 열어보면 메모리가 문제가 발생하지도 않는다.

 

 Json 스트리밍을 만들어 반환하는 웹 컨트롤러를 만들었다.

 리액티브 웹에 대한 최소한의 이해를 하기 위해 적절한 예제라 본다. 뭔가 더 알고 싶은 욕구가 뿜뿜....?

웹브라우저 및 윈도우 작업지시창

반응형

댓글