프로젝트/UNY

socket.io 실시간 chat 구현 (react - Node.js) - (2)

Roothyo 2021. 7. 8. 18:25

다음은 앞서 설명했던 단계들이 socket.io에서 어떻게 구현되어 있는지를 알아보겠다.

클라이언트단은 React.js 서버단은 Node.js를 사용하였다.

 

1. 소켓 생성

: 클라이언트는 socket.io-client에서 server의 IP와 port를 넣고 변수를 생성하면 된다.

클라이언트 - socket()

//./component/socket.js
import React from 'react';
import io from "socket.io-client";
// import { SOCKET_URL } from "config";

export const socket = io('localhost:5000');
export const SocketContext = React.createContext();

: 서버는 socket.io에서 http server를 인식시키고, port를 선택하여, 요청을 listen한다. 

서버 - socket(), bind(), listen()

// server.js

const server = require('http').createServer(app);
const io = require('socket.io')(server, {cors : {origin : "*"}});
const port = process.env.PORT || 5000;

io.listen(port, () => {
  console.log('Server listening at port %d', port);
});

 

2. 연결, 연결 해제 및  데이터 전송

클라이언트 : emit(), on()

서버 : emit(), on()

 

1. client로 부터 username과 함께, 'add user'로 접근한다.

//client

useEffect(async() => {
    socket.emit('add user', nickname);
    
    ...
});

2. io.on('connection', (socket) => { ... }) 에서 연결을 처리하며, numUsers와 socket의 임의의 parameter인 username을 붙여서 관리한다.

//server.js

io.on('connection', (socket) => {
  let addedUser = false;

  // when the client emits 'add user', this listens and executes
  socket.on('add user', (username) => {
    if (addedUser) return;

    // we store the username in the socket session for this client
    socket.username = username;
    ++numUsers;
    console.log("connected : "+socket.id+" num : "+numUsers);
    addedUser = true;
    socket.emit('login', {
      numUsers: numUsers
    });
    // echo globally (all clients) that a person has connected
    socket.broadcast.emit('user joined', {
      username: socket.username,
      numUsers: numUsers
    });
  });

3. 요청받은 request를 처리하고, emit을 통해서, response 하면, client에서는 on을 통하여, 그에 해당하는 동작들을 실행한다.

//client 

useEffect(async() => {
      
    socket.emit('add user', nickname);
    
    socket.on('login', (data) => {
      setIsConnected(true);    
      addChatMessage(data);
    });
    socket.on('user joined', (data) =>{
      setchats(chats.concat(`${data.username} joined`));
    })
});

4. client에서 새로운 Msg를 'new message'로 전달하면, broadcast 명령을 통해서, msg를 전달한 client를 제외한 나머지 socket에 msg를 emit 해준다.

//client

const sendMessage = () => {
    console.log(Msg);
    setchats(chats.concat(`${nickname} : ${Msg}`));
    socket.emit('new message', Msg);
    setMessage('');
}

useEffect(async() => {
      
    socket.on('new message', (data) => {
      setchats(chats.concat(`${data.username} : ${data.message}`));
    });
    return () => {
      socket.off('login');
      socket.off('disconnect');
      socket.off('new message');
    };
});
io.on('connection', (socket) => {
 

  // when the client emits 'new message', this listens and executes
  socket.on('new message', (data) => {
    console.log(`${socket.username} : ${data}`);
    // we tell the client to execute 'new message'
    socket.broadcast.emit('new message', {
      username: socket.username,
      message: data
    });
  });
  
  ...
}

최종 코드

클라이언트

const Home = () => {
  const location = useLocation();
  const nickname = location.state.nickname;
  const [chats, setchats] = useState([]);
  const [isConnected, setIsConnected] = useState(socket.connected);
  const [Msg, setMessage] = useState(null);

  const addChatMessage = (data) => {
    let message = '';
    if (data.numUsers === 1) {
      message += `there's 1 participant`;
    } else {
      message += `there are ${data.numUsers} participants`;
    }
    setchats(chats.concat(message));
  }

  useEffect(async() => {
      
    socket.emit('add user', nickname);
    
    socket.on('login', (data) => {
      setIsConnected(true);    
      addChatMessage(data);
    });
    socket.on('user joined', (data) =>{
      setchats(chats.concat(`${data.username} joined`));
    })
    socket.on('user left', (data) => {
      setchats(chats.concat(`${data.username} left`));
    });
    socket.on('disconnect', () => {
      setIsConnected(false);
    });
    socket.on('new message', (data) => {
      setchats(chats.concat(`${data.username} : ${data.message}`));
    });
    return () => {
      socket.off('login');
      socket.off('disconnect');
      socket.off('new message');
    };
  });

  const sendMessage = () => {
    console.log(Msg);
    setchats(chats.concat(`${nickname} : ${Msg}`));
    socket.emit('new message', Msg);
    setMessage('');
  }

  const onChange = (e) =>{
    setMessage(e.target.value);
  }

  return (
    <div className="App">
      <header className="App-header">
        <p>Connected: { '' + isConnected }</p>
        <p>socket ID: {nickname+`(${socket.id})` }</p>
        <div className="scrollBlind">
          <ul class ="message">
            {chats.map((val, index) => {
              return (<li key={index}>{val}</li>);
            })}
          </ul>
        </div>
        <div>
          <input 
            onChange={onChange} value={Msg} class="inputMessage" 
            placeholder="Type here..." 
            onKeyPress={(e)=>{
              if (e.key === 'Enter')
                sendMessage();
            }}/>
          <button onClick={sendMessage} >Send</button>
        </div>
      </header>
    </div>
  );
};

export default Home;

 

서버

// Routing
app.use(express.static(path.join(__dirname, 'public')));

// Chatroom
let rooms = ["room1", "room2", "room3"];
let numUsers = 0;

io.on('connection', (socket) => {
  let addedUser = false;

  // when the client emits 'new message', this listens and executes
  socket.on('new message', (data) => {
    console.log(`${socket.username} : ${data}`);
    // we tell the client to execute 'new message'
    socket.broadcast.emit('new message', {
      username: socket.username,
      message: data
    });
  });

  // when the client emits 'add user', this listens and executes
  socket.on('add user', (username) => {
    if (addedUser) return;

    // we store the username in the socket session for this client
    socket.username = username;
    ++numUsers;
    console.log("connected : "+socket.id+" num : "+numUsers);
    addedUser = true;
    socket.emit('login', {
      numUsers: numUsers
    });
    // echo globally (all clients) that a person has connected
    socket.broadcast.emit('user joined', {
      username: socket.username,
      numUsers: numUsers
    });
  });

  // when the client emits 'typing', we broadcast it to others
  socket.on('typing', () => {
    socket.broadcast.emit('typing', {
      username: socket.username
    });
  });

  // when the client emits 'stop typing', we broadcast it to others
  socket.on('stop typing', () => {
    socket.broadcast.emit('stop typing', {
      username: socket.username
    });
  });

  // when the user disconnects.. perform this
  socket.on('disconnect', () => {
    
    if (addedUser) {
      --numUsers;
      console.log("disconnected : "+socket.id+" num : "+numUsers);
      // echo globally that this client has left
      socket.broadcast.emit('user left', {
        username: socket.username,
        numUsers: numUsers
      });
    }
  });
});

 

실행화면

 

 

소스코드 

https://github.com/geun9716/node-multiroom-chat

 

geun9716/node-multiroom-chat

Multi rooms chatting system demo by Using socket.io in node.js - geun9716/node-multiroom-chat

github.com