[실전 프로젝트] React와 SSE

G-NOTE·2022년 10월 7일
3

항해99

목록 보기
35/36

SSE란

SSE(Server Sent Event)란 서버에서 클라이언트로 데이터를 보내는 단방향 통신을 의미한다.
실시간 알림처럼 서버가 클라이언트에게 데이터를 '단방향'으로 전달해야 할 때 사용한다.
(실시간 알림에서 양방향 통신은 불필요)

SSE는 첫 연결 시 데이터를 주고받은 뒤 연결 상태를 유지하고 서버가 클라이언트에 일방적으로 데이터를 전송한다.

websocket과 달리 별도의 프로토콜 없이 HTTP 프로토콜 만으로 사용 가능하고 훨씬 가볍다.

SSE vs. HTTP 통신

  • SSE는 서버와 클라이언트의 연결 상태를 유지하고, 서버는 클라이어트에게 지속적으로 데이터를 전송할 수 있다.
  • HTTP 통신은 하나의 request-response 과정을 거치면 연결을 종료한다. (stateless)

EventSource

서버와 클라이언트 간 SSE 통신을 하려면 처음에 클라이언트에서 서버로 연결 요청을 보내야 한다.
EventSource는 SSE 연결 요청을 보내기 위해 JavaScript가 제공하는 객체이며, SSE Client API는 EventSource 객체에 포함된다.
EventSource는 text/event-stream 포맷으로 이벤트를 전송하는 HTTP 서버에 지속적으로 연결되고, 연결은 EventSource.close() 호출로 종료되기 전까지 지속된다.

EventSource의 이벤트 핸들러

  • EventSource.onopen : 서버와 연결이 open되었을 때 호출하는 이벤트 핸들러
  • EventSource.onmessage : 서버로부터 message를 수신했을 때 호출하는 이벤트 핸들러
  • EventSource.onerror : 에러가 발생하거나 EventSource 객체에서 error event가 감지되었을 때 호출하는 이벤트 핸들러

실전 프로젝트에서 실시간 알림 구현하기

  • 어디서 subscribe request를 보내야 할까?
    ➡️ 로그인 후 어디서든 실시간 알림을 수신해야 하므로 페이지들의 상위에 있는 Router로 결정
  • 서버 쪽 withCredentials 쪽 값에 문제가 있었는데 event-source-polyfill 이라는 라이브러리를 사용해 해결했다.
  • 다만, 이 라이브러리를 사용하면서 45000ms마다 연결이 끊겼고, 다시 연결해주는 로직을 구현해야 했다.
    ➡️ useEffect를 이용하여 eventSource.close()을 클리어하고 EventSource를 다시 연결하는 방식으로 해결했다.

Router.js

  const EventSource = EventSourcePolyfill || NativeEventSource;

  useEffect(() => {
    if (loading && isLogin) {
      let eventSource;
      const fetchSse = async () => {
        try {
          eventSource = new EventSource(
            `${process.env.REACT_APP_BASE_URL}/api/subscribe`,
            {
              headers: {
                Authorization: getCookie("accessToken"),
              },
              withCredentials: true,
            }
          );

          /* EVENTSOURCE ONMESSAGE ---------------------------------------------------- */
          eventSource.onmessage = async (event) => {
            const res = await event.data;
            if (!res.includes("EventStream Created.")) setNewAlarms(true); // 헤더 마이페이지 아이콘 상태 변경
            queryClient.invalidateQueries("myprofile"); // 프로필 업데이트
            queryClient.invalidateQueries("alertNoti"); // 알람 리스트 개수 변경
            queryClient.invalidateQueries("alertLists"); // 알림 목록 업데이트
          };

          /* EVENTSOURCE ONERROR ------------------------------------------------------ */
          eventSource.onerror = async (event) => {
            if (!event.error.message.includes("No activity"))
              eventSource.close();
          };
        } catch (error) {}
      };
      fetchSse();
      return () => eventSource.close();
    }
  });

참조

https://boxfoxs.tistory.com/403
https://developer.mozilla.org/ko/docs/Web/API/EventSource
https://intrepidgeeks.com/tutorial/real-time-data-streaming-using-responsive-server-transport-events-sse-js-and-node-js
https://velog.io/@beomseokwee/react-spring-boot-%EC%95%8C%EB%A6%BC-%EA%B8%B0%EB%8A%A5
https://velog.io/@max9106/Spring-SSE-Server-Sent-Events%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EC%8B%A4%EC%8B%9C%EA%B0%84-%EC%95%8C%EB%A6%BC
https://jellybeanz.medium.com/server-sent-events-server-push-%EB%9E%80-da6775de5c5d

profile
FE Developer

1개의 댓글

comment-user-thumbnail
2024년 3월 15일

잘안되네요...

답글 달기