실시간 알림(실시간 통신방식 3가지)

김현준·2025년 1월 8일
0

리액트 이모저모

목록 보기
25/27

실시간 통신 방식 비교

1. WebSocket

  • 클라이언트와 서버 간 양방향 통신 가능
  • 지속적인 연결을 유지하여 데이터를 주고받음
  • ws 프로토콜을 사용하며, 초기 연결 시 한 번만 핸드셰이크를 수행 → 통신 오버헤드가 낮음

2. Polling

  • 클라이언트가 주기적으로 서버에 요청을 보내고 응답을 받음
  • 데이터 업데이트가 없는 상황에서도 요청을 보내므로 비효율적
  • 주기적인 요청으로 서버 부하가 발생할 수 있음

3. SSE (Server-Sent Events)

  • 서버에서 클라이언트로 데이터를 푸시하는 단방향 통신 방식
  • HTML5 표준안이라 브라우저 호환성이 좋고 가벼움
  • 클라이언트가 서버에 요청을 보내고, 연결을 유지한 채 서버에서 이벤트가 발생할 때마다 데이터를 전송

실시간 알림에 적합한 방식: SSE

실시간 알림은 서버에서 데이터가 업데이트될 때 클라이언트로 푸시하는 단방향 통신이므로 SSE가 적합하다.

오류 사항 및 해결 방법

문제: 연결 후 No activity within 45000 milliseconds 오류 발생

Error: No activity within 45000 milliseconds. 41 chars received.
Reconnecting.
  • 원인:
    EventSourcePolyfill의 기본 timeout 값이 45초로 설정되어 있다. 만약 백엔드의 타임아웃 설정이 이보다 크다면 45초마다 재연결을 시도하게 된다.
  • 해결 방법:
    heartbeatTimeout 옵션을 설정해 백엔드 타임아웃보다 충분히 큰 값으로 조정해야 한다.

SSE로 알림 기능 구현하기

SSE 연결 코드

import React, { useEffect, useRef, useState } from "react";
import { Link } from "react-router-dom";
import { FaRegBell } from "react-icons/fa";
import { EventSourcePolyfill } from "event-source-polyfill";

const Alarm = () => {
  const [realtimeData, setRealtimeData] = useState(false);
  const eventSource = useRef<EventSource | null>(null);

  useEffect(() => {
    const fetchSSE = () => {
      eventSource.current = new EventSourcePolyfill(
        `${import.meta.env.VITE_SERVER}/notification/subscribe`,
        {
          headers: {
            Authorization: `Bearer ${localStorage.getItem("ACCESS_TOKEN")}`,
          },
          heartbeatTimeout: 60000,// 백엔드의 타임아웃보다 충분히 큰 값(예: 60초)으로 설정
          withCredentials: true,
        }
      );

      eventSource.current.addEventListener("sse", (e: MessageEvent) => {
        const parsedData = JSON.parse(e.data);
        setRealtimeData(parsedData);
      });

      eventSource.current.onerror = () => {
        console.error("SSE 연결 오류 발생");
        eventSource.current?.close();
        setTimeout(fetchSSE, 3000); // 3초 후 재연결 시도
      };

      eventSource.current.onopen = () => {
        console.log("SSE 연결 성공");
      };
    };

    fetchSSE();

    return () => {
      eventSource.current?.close();
    };
  }, []);

  return (
    <Link
      to="/alarm"
      className="w-14 h-full flex justify-center items-center absolute right-0 top-0"
    >
      <div className="relative">
        <FaRegBell className="text-xl text-bk" />
        {realtimeData && (
          <span className="absolute -top-1 -right-2 w-2 h-2 bg-red-500 rounded" />
        )}
      </div>
    </Link>
  );
};

export default Alarm;
profile
기록하자

0개의 댓글

관련 채용 정보