Spring SSE

오븐·2023년 7월 12일

하루 종일 WebSocket이랑 씨름하다가 우연히 유튜브의 영상을 봤다. 대충 내용이

야 한방향 소켓이면 SSE가 더 좋아 WebSocket은 쌍방향이라서 비용 나간다

왜 구현할 방법은 밤에 찾게 되는걸까.....

급한대로 유튜브 클론코딩을 했다. WebFlux SSE로 구현하셨다.

package com.example.sse;

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

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;

@RestController
@RequestMapping("/server-events")
public class ServerEventsController {

    @GetMapping(produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<ServerSentEvent<String>> getEvents() throws IOException {
        // 스트링 타입의 서버센트이벤트를 만듦
        Stream<String> lines = Files.lines(Path.of("C:\\Users\\유저명\\sse\\build.gradle"));

        AtomicInteger counter = new AtomicInteger(1);
        return Flux.fromStream(lines)
                .filter(line -> !line.isBlank())
                .map(line ->
                    ServerSentEvent.<String> builder()
                            .id(String.valueOf(counter.getAndIncrement()))
                            .data(line)
                            .event("lineEvent")
                            .retry(Duration.ofMillis(1000))
                            .build())
                .delayElements(Duration.ofMillis(300));
    }
}
  • @Controller: 모델 객체를 만들어 데이터를 담고 View를 찾음
  • @RestController: 객체만을 반환하고 JSON 또는 XML 형식으로 HTTP 응답에 담아 전송
  • @RequestMapping: 들어온 요청을 특정 메서드와 매핑하기 위해 사용
  • @GetMapping = @RequestMapping(method = RequestMtho.GET)
  • MediaType.Text_EVENT_STREAM_VALUE: SSE는 기본적으로 text/event-stream을 사용. 일반 텍스트 응답을 사용해야함
  • AtomicInteger: 다중 스레드 환경에서 원자 정수 카운터로 사용할 수 있는 클래스
  • Flux: 리액터=리액트 스트림 객체. 0~N개의 데이터 전달
  • ServerSentEvent: 스프링 리액티브 웹에서 사용하기 위한 SSE 표현(representation)
    (Flux는 Spring MVC의 SseEmitter와 같다.)

기본적인 것은 알았으니 하나씩 파해쳐 보자

Stream<String> lines = Files.lines(Path.of("C:\\Users\\유저명\\sse\\build.gradle"));

파일을 읽어 Stream으로 만들어 준다. 이 링크를 참고하자.

AtomicInteger counter = new AtomicInteger(1);

카운터를 생성. 여기서는 증가하는 수를 만들기 위해서다.

Flux.fromStream(lines).filter(line -> !line.isBlank())

제공된 스트림 안에 있는 아이템을 하나씩 뱉는 Flux를 만들어서 그 아이템이 비지 않았다면

.map(line -> ServerSentEvent.<String> builder()

한 줄마다 ServerSentEvent로 만드는 빌더를 불러서

.id(String.valueOf(counter.getAndIncrement()))
                            .data(line)
                            .event("lineEvent")
                            .retry(Duration.ofMillis(1000))
                            .build())

아이디는 정말 아이디. 호출될 때매다 1씩 증가, 데이터는 line, event는 "lineEvent", retry는 실패가 있으면 해당 시간이 지나고 다시 접속을 시도해본다. -> 그리고 이런것들을 전부 생성해서 빌드. 그리고 반환!

.delayElements(Duration.ofMillis(300));

각 Flux 요소를 주어진 시간만큼 지연시킴

이러면 웹사이트에 들어가면 0.3초마다 한줄씩 프린트가 된다. 와!

출처

profile
하루에 한번 정권 찌르기

0개의 댓글