반응형
여승철
INTP 개발자
여승철
  • 분류 전체보기 (376)
    • CS (16)
      • 면접 준비 (7)
      • 운영체제 (0)
      • 네트워크 (2)
      • HTTP (6)
      • 스프링(Spring) IoC 컨테이너 (0)
      • 알고리즘 (1)
    • Web (13)
    • AWS (6)
    • Java (43)
    • JSP & Servlet (65)
      • 개념 (42)
      • 실습 (23)
    • Spring Framework (33)
    • Spring Boot (10)
    • Spring Data (22)
      • JPA (14)
      • Query DSL (7)
      • Redis (1)
    • Spring Security (9)
    • Spring Batch (4)
    • MyBatis (10)
    • Front-End (51)
      • JS (27)
      • Vue.js (17)
      • React (5)
      • JQuery (0)
      • d3.js (2)
    • DBMS (24)
      • SQL, RDBMS (16)
      • MongoDB (5)
      • Redis (3)
    • Kafka (3)
    • 리눅스 (Linux) (4)
    • 디자인 패턴 (3)
    • VCS (8)
    • API (0)
    • TOOL (3)
    • Reading Book (28)
      • 이펙티브 자바 (11)
      • Clean Code (10)
      • 1분 설명력 (4)
      • HOW TO 맥킨지 문제해결의 기술 (3)
    • C# (4)
    • NSIS (6)
    • ETC (11)

블로그 메뉴

  • 홈
  • 태그

인기 글

태그

  • 스트림
  • jsp
  • mybatis
  • HTTP
  • 이펙티브 자바
  • JSTL
  • 회원 관리
  • 환경 세팅
  • 로그인
  • Dao
  • JDBC
  • Spring Batch
  • 게시판
  • 디자인 패턴
  • 맥킨지
  • querydsl
  • EC2
  • controller
  • ubuntu
  • servlet

최근 댓글

최근 글

hELLO· Designed By 정상우.
여승철

INTP 개발자

실시간 데이터 전송 방법 Server-Sent Events(SSE)와 웹소켓 차이
Web

실시간 데이터 전송 방법 Server-Sent Events(SSE)와 웹소켓 차이

2023. 6. 26. 16:44
반응형

SSE: Server-Sent Events

SSE(Server-Sent Events)는 웹 애플리케이션, `서버에서 클라이언트`로 `단방향`으로 `실시간` 이벤트를 전송하는 `웹` 기술입니다.  SSE는 단방향 통신 방식으로 서버에서 클라이언트로 데이터를 전송합니다. 이를 통해 서버에서 발생하는 업데이트나 알림 등을 실시간으로 클라이언트에게 전달할 수 있습니다. SSE는 이런 특징으로 실시간 알림 ,실시간 주가 업데이트 등에 사용됩니다.

 

SSE는 단방향 통신이기 때문에 서버에서 클라이언트로만 데이터를 전송할 수 있습니다. 클라이언트는 HTTP 프로토콜을 통해 SSE 연결을 설정하고, 서버는 HTTP 응답을 유지한 상태에서 데이터를 전송합니다. SSE는 재연결 기능을 제공하기 때문에 연결이 끊어졌을 때 자동으로 다시 연결합니다. 이는 기존의 폴링 방식과 비교했을 때 효율적이며, 서버와 클라이언트 간의 불필요한 통신을 최소화합니다. SSE는 이벤트 스트림 형태로 데이터를 전송하며, 클라이언트는 이벤트를 수신하여 처리할 수 있습니다.

폴링은 클라이언트가 주기적으로 서버에 요청을 보내고, 서버는 새로운 데이터가 있는지 확인하여 응답하는 방식인 반면, SSE는 클라이언트와서버 간에 지속적인 연결을 유지하고, 서버는 필요한시점에 데이터를전송합니다.
폴링에 비해 서버 리소스와 네트워크 트래픽을 절약할 수 있습니다.     

 

SSE와 웹소켓 차이점

SSE와 웹소켓의 가장 큰 차이점은 `데이터의 흐름`입니다. `SSE`는 서버에서 클라이언트로 데이터를 전송하는 단방향 통신 방식입니다. 반면 `웹소켓`은 양방향 통신을 지원하여 서버와 클라이언트가 양방향으로 데이터를 주고받을 수 있습니다. 때문에 `SSE`는주로서버에서 클라이언트로 일방적인 데이터 전송이 필요한 주가 업데이트나, 실시간 알림 메시지에 적합하고, `웹소켓`은 양방향 통신이 필요한 실시간 채팅 등에 사용됩니다.

`SSE`는 웹기술이기 때문에 `HTTP 프로토콜` 위에서 동작합니다. 또한 기존의 HTTP 연결을 유지한 상태에서 재연결이나 추가 설정 없이 서버로부터 지속적인 데이터 스트림을 받을 수 있습니다. 반면 `웹소켓`은 독립적인 프로토콜을 사용하고, HTTP와는 별도의 연결을 만들어 데이터를 주고 받습니다. SSE는 CORS (Cross-Origin Resource Sharing)를 통해 다른 도메인에서도 데이터를 수신할 수 있습니다. 웹소켓도 동일한 도메인 간 통신을 제공하지만, 보안상의 이유로 추가 구성이 필요할 수 있습니다.

 

SSE 구현 방법

SSE를 구현하기 위해서는 서버 측과 클라이언트 측에서의 설정과 처리 로직을 구현해야 합니다.

  • `서버 측`에서는 `SSE 프로토콜`을 생성하고 클라이언트에게 전송할 이벤트 데이터를 준비해야 합니다.
  • `클라이언트 측`에서는 EventSource 객체를 생성하고 이벤트를 처리하는 로직을 작성해야 합니다. EventSource 객체는 SSE 프로토콜을 처리하고 이벤트를 수신하는 기능을 제공합니다.

 

Client

<template>
  <div>
    <ul>
      <li v-for="event in events" :key="event.id">{{ event.message }}</li>
    </ul>
  </div>
</template>

<script>
export default {
  data() {
    return {
      events: [] // 수신된 이벤트를 저장할 배열
    };
  },
  mounted() {
    this.setupEventSource(); // SSE 연결 설정
  },
  methods: {
    setupEventSource() {
      const eventSource = new EventSource('/sse/stream'); // SSE 연결 생성
      eventSource.onmessage = this.handleEvent; // 이벤트 핸들러 등록
      eventSource.onerror = this.handleConnectionError; // 연결 오류 핸들러
      eventSource.onopen = this.handleConnectionOpen; // 연결 성공 핸들러
    },
    handleEvent(event) {
      const eventData = JSON.parse(event.data); // 수신된 이벤트 데이터 파싱
      this.events.push(eventData); // 이벤트 데이터를 배열에 추가
      this.showNotification(eventData.message); // 브라우저 알림 표시
    },
    handleConnectionOpen() {
      console.log('Connection to SSE server established'); // 연결 성공 로그 출력
    },
    handleConnectionError(error) {
      console.error('SSE connection error:', error); // 연결 오류 로그 출력
      // 연결 오류 처리 로직 구현
    },
    showNotification(message) {
      // 브라우저 알림 표시 로직 구현
    }
  }
};
</script>

Server

import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@RestController
@RequestMapping("/sse")
public class SseController {
    private final Map<String, SseEmitter> emitters = new ConcurrentHashMap<>();

    /**
     * 특정 클라이언트에게 SSE 연결을 생성합니다.
     * 클라이언트마다 고유한 클라이언트 ID를 사용합니다.
     * 클라이언트 ID를 경로 변수로 받아와 SSEEmitter를 생성하고 관리합니다.
     *
     * @param clientId 클라이언트 ID
     * @return SSEEmitter
     */
    @GetMapping(value = "/stream/{clientId}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public SseEmitter stream(@PathVariable String clientId) {
        SseEmitter emitter = new SseEmitter();

        // 연결 완료, 오류, 타임아웃 이벤트 핸들러 등록
        emitter.onCompletion(() -> {
            emitters.remove(clientId);
            cleanupEmitter(emitter);
        });
        emitter.onError((ex) -> {
            emitters.remove(clientId);
            cleanupEmitter(emitter);
        });
        emitter.onTimeout(() -> {
            emitters.remove(clientId);
            cleanupEmitter(emitter);
        });

        // 클라이언트 ID와 SSEEmitter를 맵에 저장
        emitters.put(clientId, emitter);
        return emitter;
    }

    /**
     * 특정 클라이언트에게 이벤트를 전송합니다.
     * 클라이언트 ID를 경로 변수로 받아와 해당 클라이언트의 SSEEmitter를 찾아 이벤트를 전송합니다.
     *
     * @param clientId 클라이언트 ID
     * @param message  전송할 메시지
     */
    @GetMapping("/send/{clientId}")
    public void sendEventToClient(@PathVariable String clientId, String message) {
        SseEmitter emitter = emitters.get(clientId);
        if (emitter != null) {
            try {
                emitter.send(SseEmitter.event().data(message));
            } catch (IOException e) {
                emitter.completeWithError(e);
            }
        }
    }

    /**
     * 모든 클라이언트에게 이벤트를 전송합니다.
     * 현재 연결된 모든 SSEEmitter를 순회하며 이벤트를 전송합니다.
     *
     * @param message 전송할 메시지
     */
    @GetMapping("/send/all")
    public void sendEventToAll(String message) {
        for (SseEmitter emitter : emitters.values()) {
            try {
                emitter.send(SseEmitter.event().data(message));
            } catch (IOException e) {
                emitter.completeWithError(e);
            }
        }
    }

    /**
     * SSEEmitter를 정리하고 완료 처리합니다.
     *
     * @param emitter SSEEmitter
     */
    private void cleanupEmitter(SseEmitter emitter) {
        try {
            emitter.complete();
        } catch (Exception e) {
            // 예외 처리
        }
    }
}
반응형

'Web' 카테고리의 다른 글

웹 서버 vs 웹 어플리케이션 서버 vs CGI 프로그램: 차이 쉽게 이해하기  (0) 2023.08.31
Nginx: 웹 서버와 리버스 프록시의 개념과 용도, 사용법 설명  (0) 2023.06.28
확장성을 위한 Tomcat 클러스터링 구성과 설정 방법  (0) 2023.06.28
Tomcat 성능 향상: 스레드 풀과 커넥터 설정 사용하기  (0) 2023.06.28
Apache Tomcat 아파치 톰캣: 웹 애플리케이션 서버의 기능과 사용법 알아보자  (0) 2023.06.27
    여승철
    여승철

    티스토리툴바