Server-Sent Events (TypeScript + React, Spring Boot)

이홍준·2024년 10월 6일
1

Spring Boot

목록 보기
12/12

알림 서비스과 같이 서버에서 클라이언트에게 데이터를 전송하는 방법들중에 Polling, WebSocket 방식을 사용해 본적이 있습니다. 또 하나의 방법인 Server Sent Events 에 대해 간단하게 구현해보고 다른 방식들과 장단점도 정리해 보았습니다.

What is SSE?

server-sent events 란 클라이언트(웹페이지)의 요청없이도 언제든지 서버가 새로운 데이터를 보내는 것이 가능하게 해주는 방법입니다. 단방향 통신 방법입니다.

특징을 정리하자면,

  1. 단방향 통신: 서버 → 클라이언트만 데이터를 전송 할 수 있습니다. (클라이언트 → 서버 전송시 HTTP로 따로 보내줘야 합니다)
  2. 간단한 구현: 브라우저의 기본 API인 EventSource 를 사용해 간단히 구현할 수 있습니다.
  3. 자동 재연결: EventSource 는 연결이 끊어질 경우 자동으로 재연결을 시도하므로 안정적인 데이터 스트림을 제공합니다.
  4. 텍스트 기반 전송: 주로 텍스트 데이터 전송을 지원하며, JSON이나 일반 문자열로 데이터를 전송할 수 있습니다.
  5. 브라우저 지원: 대부분 최신 브라우저에서 기본적으로 지원됩니다. (주의: Firefox는 서비스워커에서 사용을 지원하지 않습니다.)

단점

  1. 바이너리 데이터 및 미디어 전송은 불가능합니다.
  2. 오래된 브라우저 지원 제한

그래서 해당 특징을 고려해서 적합한 곳은

  1. 실시간 알림: 피드, 주식 가격, SNS 알림 등
  2. 서버에서 주기적으로 업데이트하는 정보: 모니터링 데이터, 실시간 메시지 방송
  3. 단순한 클라이언트-서버 이벤트 스트림: 양방향이 필요없는 경우

A Comparative Overview

SSE, WebSocket 그리고 Polling과 서로 특징을 비교한 것을 정리해보았습니다.

특징Server-Sent EventsWebSocketPolling
통신 방향단방향 (서버 → 클라이언트)양방향단방향 (클라이언트 → 서버)
데이터 전송텍스트 (UTF-8)텍스트/바이너리텍스트
복잡성낮음높음낮음
실시간성높음매우 높음주기에 따라 다름
브라우저 지원최신 브라우저 지원모든 주요 브라우저모든 브라우저
방화벽/프록시 문제적음있음없음
적합한 예시실시간 알림, 단순 스트림채팅, 게임, 실시간 협업주기적인 상태 업데이트

운영하는데에 중요한 시스템 자원 관점에서도 정리해보았습니다.

자원 요소SSEWebSocketPolling
서버 자원 사용중간 (지속적인 연결 유지)높음 (지속적인 TCP 연결 관리)높음 (주기적 요청 처리 부하)
네트워크 사용량낮음 (텍스트 전송, 재연결)낮음 (연결 재사용, 바이너리 지원)높음 (HTTP 헤더 오버헤드)
클라이언트 자원 사용낮음높음 (양방향 통신 관리)중간 (주기적 요청)

적합한 사용을 정리해보면

  • SSE: 서버에서 클라이언트로 실시간 업데이트가 필요한 경우에 적합하며, 서버 자원과 네트워크 사용량을 적절히 유지해야 할 때 유리합니다. 예: 실시간 알림, 주식 가격 업데이트 등.
  • WebSocket: 실시간 양방향 통신이 필요한 경우에 가장 적합하며, 대량의 데이터 교환이나 빈번한 상호작용이 필요한 경우에 최적입니다. 예: 실시간 채팅, 온라인 게임, 실시간 협업 애플리케이션 등.
  • Polling: 주기적인 데이터 업데이트가 필요한 간단한 애플리케이션에서 사용할 수 있지만, 효율성이 떨어지므로 실시간성이 매우 중요하지 않은 경우에만 적합합니다. 예: 상태 모니터링, 정기적인 데이터 업데이트.

How to use SSE in Spring Boot?

생각했던것보다 엄청 간단하게 구현할 수 있었습니다. 따로 추가해야할 의존성패키지도 없었습니다.

구현해야하는 부분들을 간단히 정리해 보자면

  1. SseEmitter 서버에서 클라이언트로 이벤트를 스트리밍할 수 있도록 하는 객체
  2. ScheduledExecutorService: 비동기 처리를 스케줄링 해주는 서비스
  3. scheduleAtFixedRate: 반복 실행 함수(javaScript의 setTimeout와 유사)
  4. send: 메시지 전송
  5. completeWithError: 에러메시지 전송 후 스트림 종료
  6. onCompletion: 완료시 취해야할 동작 지정
// 간단하게 사용할 메시지 데이터
record MessageData(String content, String author, String currentDate){}
@RestController
public class SseController{
    @GetMapping("/sse")
    public SseEmitter streamEvents(){
        SseEmitter emitter = new SseEmitter(30*60*1000L);
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
        executor.scheduleAtFixedRate(()->{
            try {
                emitter.send(new MessageData("안녕","홍길동", LocalDateTime.now().toString()));
            }catch (IOException e){
                emitter.completeWithError(e);
            }
        }, 0, 1, TimeUnit.SECONDS);

        emitter.onCompletion(executor::shutdown);
        return emitter;
    }
}

How to use SSE in React?

Client 에도 따로 설치해야할 부분이 없어서 매우 간단했습니다.

간단히 정리해보면

  1. eventSource: 이벤트 스트림을 관리하는 객체, 매개 변수에 sse에 의 endpoint입력
  2. onmessage: 전송된 메시지 수신 메서드
  3. oneerror : 오류 처리 메서드

// 전송 데이터 객체
interface MessageData{
    content: string;
    author: string;
    currentDate: string;
}

const SseComponent = () => {
  const [message, setMessage] = useState<MessageData>();

  useEffect(() => {
    // EventSource 인스턴스 생성
    const eventSource = new EventSource('http://localhost:8080/sse');

    // 메시지 수신
    eventSource.onmessage = (event) => {
        const data: MessageData = JSON.parse(event.data);
      setMessage(data);
    };

    // 오류 처리
    eventSource.onerror = (error) => {
      console.error('SSE 연결 오류:', error);
      eventSource.close();
    };

    // 컴포넌트 언마운트 시 연결 종료
    return () => {
      eventSource.close();
    };
  }, []);

  return (
    <div>
      <h1>Server-Sent Events</h1>
      <p>수신한 메시지</p>
      <p>content: {`${message?.content}`}</p>
      <p>author: {`${message?.author}`}</p>
      <p>currentTime: {`${message?.currentDate}`}</p>
    </div>
  );
};

Conclusion

SSE는 단방향 통신이 필요한 실시간 데이터 전송에 적합하며, 간단한 구현과 자동 재연결 기능등을 제공합니다. 하지만 양방향 통신과 같은 실시간성, 바이너리 데이터를 전송하는 경우에는 적절하지 않습니다. 각 기술들간의 특징들을 잘 파악해서 요구사항에 대해 적절하게 사용하는 것이 중요하다고 생각합니다.


References

profile
I'm a web developer.

0개의 댓글