문제

원인

해결 방안

구현 예시

// 상태 및 참조 변수 선언
const [notification, setNotification] = useState<InviteWebSocketMessage | null>(null);
const hasSubscribed = useRef(false);

// 1. 소켓 연결 전용 useEffect
useEffect(() => {
  if (!isConnected) connectWebSocket();
}, []);

// 2. 구독 등록용 useEffect (한 번만 실행)
useEffect(() => {
  if (!isConnected || hasSubscribed.current) return;

  hasSubscribed.current = true;
  subscribeToNotification(userId, (message: InviteWebSocketMessage) => {
    console.log('Notification received:', message);
    setNotification(message);
  });
}, [isConnected]);

// 3. 메시지 처리용 useEffect (notification 상태 변경 시)
useEffect(() => {
  if (!notification) return;

  if (notification.messageType === 'INVITE_PLAYER') {
    const inviteMessage = notification;
    if (inviteMessage.payload.action === 'INVITE') {
      console.log('Invite notification received:', inviteMessage.payload);
      InviteNotificationToast({
        senderNickname: inviteMessage.payload.senderNickname,
        targetNickname: inviteMessage.payload.targetNickname,
        roomName: inviteMessage.payload.roomName,
        onAccept: () => {
          console.log('Invitation accepted:', inviteMessage.payload);
          // 서버 API 호출, 소켓 이벤트 등 처리
        },
        onDecline: () => {
          console.log('Invitation declined:', inviteMessage.payload);
          // 서버 API 호출, 소켓 이벤트 등 처리
        },
      });
    }
  }
}, [notification]);

관련 개념 보충 설명: useRef

기본 개념 및 용도

이 코드에서의 useRef 용도

이처럼 useRef를 활용하면 렌더링과 상관없이 값이 유지되며, 중복 실행을 효과적으로 막을 수 있습니다.