티스토리 뷰

코드 ver 0.2

import React, { useEffect, useState, useRef } from "react";

const SocketTest = () => {
  const [socketConnected, setSocketConnected] = useState(false);
  const [sendMsg, setSendMsg] = useState(false);
  const [items, setItems] = useState([]);

  const webSocketUrl = `ws://websocket.com`;
  let ws = useRef(null);

  // 소켓 객체 생성
  useEffect(() => {
    if (!ws.current) {
      ws.current = new WebSocket(webSocketUrl);
      ws.current.onopen = () => {
        console.log("connected to " + webSocketUrl);
        setSocketConnected(true);
      };
      ws.current.onclose = (error) => {
        console.log("disconnect from " + webSocketUrl);
        console.log(error);
      };
      ws.current.onerror = (error) => {
        console.log("connection error " + webSocketUrl);
        console.log(error);
      };
      ws.current.onmessage = (evt) => {
        const data = JSON.parse(evt.data);
        console.log(data);
        setItems((prevItems) => [...prevItems, data]);
      };
    }

    return () => {
      console.log("clean up");
      ws.current.close();
    };
  }, []);

  // 소켓이 연결되었을 시에 send 메소드
  useEffect(() => {
    if (socketConnected) {
      ws.current.send(
        JSON.stringify({
          message: sendMessage,
        })
      );

      setSendMsg(true);
    }
  }, [socketConnected]);

  return (
    <div>
      <div>socket</div>
      <div>socket connected : {`${socketConnected}`}</div>
      <div>res : </div>
      <div>
        {items.map((item) => {
          return <div>{JSON.stringify(item)}</div>;
        })}
      </div>
    </div>
  );
};

export default SocketTest;

onmessage 메서드는 send 후 데이터가 올 때 트리거가 되므로 웹소켓 객체 정의 안으로 이동했습니다. send 메소드는 따로 빼지 않고 onopen 함수 안에 넣어도 됩니다. 


코드 ver 0.1

import React, { useEffect, useState, useRef } from "react";

const SocketTest = () => {
  const [socketConnected, setSocketConnected] = useState(false);
  const [sendMsg, setSendMsg] = useState(false);
  const [items, setItems] = useState([]);

  const webSocketUrl = `ws://websocket.com`;
  let ws = useRef(null);

  // 소켓 객체 생성
  useEffect(() => {
    if (!ws.current) {
      ws.current = new WebSocket(webSocketUrl);
      ws.current.onopen = () => {
        console.log("connected to " + webSocketUrl);
        setSocketConnected(true);
      };
      ws.current.onclose = (error) => {
        console.log("disconnect from " + webSocketUrl);
        console.log(error);
      };
      ws.current.onerror = (error) => {
        console.log("connection error " + webSocketUrl);
        console.log(error);
      };
    }

    return () => {
      console.log("clean up");
      ws.current.close();
    };
  }, []);

  // 소켓이 연결되었을 시에 send 메소드
  useEffect(() => {
    if (socketConnected) {
      ws.current.send(
        JSON.stringify({
          message: sendMessage,
        })
      );

      setSendMsg(true);
    }
  }, [socketConnected]);

  // send 후에 onmessage로 데이터 가져오기
  useEffect(() => {
    if (sendMsg) {
      ws.current.onmessage = (evt) => {
        const data = JSON.parse(evt.data);
        console.log(data);
        setItems((prevItems) => [...prevItems, data]);
      };
    }
  }, [sendMsg]);

  return (
    <div>
      <div>socket</div>
      <div>socket connected : {`${socketConnected}`}</div>
      <div>res : </div>
      <div>
        {items.map((item) => {
          return <div>{JSON.stringify(item)}</div>;
        })}
      </div>
    </div>
  );
};

export default SocketTest;

 

코드를 짜면서 고민했던 점은 컴포넌트가 unmount 되었을 때 socket 연결이 끊어지기를 바랬습니다. 여러 시행 착오 끝에 소켓 객체를 생성하는 useEffect에 clean up을 하는 것이 원하는대로 동작하였습니다.

 

소켓 객체를 컴포넌트 밖에 전역으로 생성하기 보다는 컴포넌트에 ref를 통해 저장이 되도록 하고, 소켓 객체의 send 메소드와 onmessage 메소드가 순차적으로 실행되기를 바래서 위와 같이 작성했습니다.

 

반응형
댓글