인프런 커뮤니티 질문&답변

bin님의 프로필 이미지
bin

작성한 질문수

socket.io 실행

작성

·

622

·

수정됨

0

안녕하세요

채팅을 구현하기위해 socket.io를 썼는데 통신이 안되는 것같습니다 io에 주소를 제대로 넣었고 서버 on 마다 클라이언트에서 emit으로 작성했는데 작동하지 않습니다 이유가 무엇일까요?

 

// server 파일의 코드입니다

 

require('dotenv').config();

const { createApp } = require('./app');
const { appDataSource } = require('./models/index');

const startServer = async () => {
 const app = createApp();
const PORT = process.env.PORT;
 await appDataSource
 .initialize()
 .then(() => {
  const server = app.listen(PORT, () => {
   console.log(`🟢server is listening on ${PORT}🟢`);
    });
const io = require('socket.io')(server, {
cors: {
 origin: true,
 credentials: true,
},
  });
 const { socketMessage } = require('./middlewares/socket.io');
 socketMessage(io);
    })
.catch((err) => {
console.log(`❌Failed server connect❌`);
appDataSource.destroy();
});
};
startServer();

 

// server의 socket 파일의 코드입니다

 

const jwt = require('jsonwebtoken');
const chatDao = require('../models/chatDao');

const { catchAsync } = require('../utils/error');

const socketMessage = (io) => {
io.use((socket, next) => {

    const token = socket.handshake.headers.authorization;

    if (!token) {

      return next(new Error('Authentication error'));

    }

    jwt.verify(token, process.env.SECRET_KEY, async (err, decoded) => {

      if (err) {

        return next(new Error('Authentication error'));

      }
      userId = decoded.userId;

      next();

    });

  });

  io.on('connection', (socket) => {

    console.log('A User Connected.');

    socket.on(

      'create_room',

      catchAsync(async (postId, callback) => {

        const room = await chatDao.createRoom(userId, postId);

        socket.join(room.raw.insertId);

        callback(room.raw.insertId);

      })

    );
 socket.on(

      'enter_room',

      catchAsync(async (roomId, callback) => {

        socket.join(roomId);

        callback(roomId);

      })

    );


    socket.on('new_text', async (content, roomId, callback) => {

      await chatDao.createChat(userId, content, roomId);

      socket.to(roomId).emit('new_text', content);

      callback(content);

    });


    socket.on('disconnect', () => {

      console.log('접속이 해제되었습니다', socket.id);

      clearInterval(socket.interval);

    });


    socket.on('error', (error) => {

      console.error(error);

    });
​

    socket.on('send', (data) => {

      console.log(data);

      socket.emit('reply', {

        data,

      });

    });


    socket.interval = setInterval(() => {

      socket.emit('news', 'Hello Socket.IO');

    }, process.env.SOCKET_INTERVAL || 1000);

  });

};


module.exports = { socketMessage };
​

 

// client 코드입니다

import React, { useState, useContext } from 'react'; import io from 'socket.io-client';
import './chat.css'; import { MenuContext } from '../../components/Nav/MenuProvider'; 

const Token = localStorage.getItem('accessToken');

const socket = io.connect('http://192.168.0.194:4000', { 
withCredentials: true,
extraHeaders: {Authorization: `Bearer ${Token}`
}
appDataSource.destroy();
}),
});

socket.on('connection', () => { console.log('Connected to server'); 
});

const Chat = () => {   const [roomId, setRoomId] = useState([]);

const [searchData, setSearchData] = useContext(MenuContext);

const handleCreateRoom = event => { 
event.preventDefault(); 
socket.emit('create_room', searchData, ({ searchData, roomId }) => { 
console.log(`Joined room ${roomId}`);
setRoomId(roomId);
});
};

const handleJoinRoom = roomId => { socket.emit('enter_room', roomId, roomId => { console.log(`Joined room ${roomId}`);
setRoomId(roomId);

});

};

const handleNewText = content => { socket.emit('new_text', content, roomId, content => { console.log(`Sent message: ${content}`);

});

};

const handleNewText = content => {

socket.emit('new_text', content, roomId, content => {       console.log(`Sent message: ${content}`);

});

};

const onCheckEnter = e => {if (e.key === 'Enter') {

handleNewText();

}

};

return (

<div className="h-screen pt-36">

<button onClick={handleCreateRoom}>테스트</button>

<button onClick={() => handleJoinRoom(roomId)}>테스트2</button>

<input id="input-text" type="text" onKeyDown={onCheckEnter} />

<button onClick={handleCreateRoom}>제출</button>

</div>

);

};

export default Chat; 

답변 1

0

안녕하세요, 인프런 AI 인턴이에요.

코드를 보니 서버와 클라이언트 모두 문제가 없어보입니다. 가능성이 있다면 .env 파일에서 PORT와 SECRET_KEY의 값을 정확하게 설정했는지 확인해보시는 것이 좋을 것 같아요.

그리고 client 코드에서 아래 코드 중 appDataSource.destroy() 코드는 삭제하셔야 합니다. 여기는 server에서 initialize하고 실행하는 부분이기 때문에, client에서는 필요하지 않습니다.

const socket = io.connect(’http://192.168.0.194:4000’, {
withCredentials: true,
extraHeaders: {Authorization: Bearer ${Token}}
// appDataSource.destroy(), // 삭제!
});

일단 처음 추측하는 문제는 서버와 클라이언트 사이에 CORS (Cross-Origin Resource Sharing) 문제가 있을 수 있다는 것입니다. 서버 코드 중, socket.io 서버를 생성할 때 cors 옵션을 사용하고 있지만, client에서는 cors 설정을 하지 않았습니다. 따라서, client 코드에 cors 옵션을 추가하시면 더 매끄러운 연결이 가능할 것 같습니다.

const socket = io.connect(’http://192.168.0.194:4000’, {
withCredentials: true,
extraHeaders: {Authorization: Bearer ${Token}},
cors: {
origin: true,
credentials: true,
},
});

위와 같이 client 코드에 cors 옵션을 추가해보시기 바랍니다. 혹시 해결이 안된다면 error 이벤트를 잡아서 해당 이벤트에서 어떤 에러가 발생하는지 확인해보시는 것도 좋을 것 같습니다.

그리고 서버 실행 후 바로 client에서 접속하시면 제대로 타이밍이 안맞을 수 있어요. 만약 socket.io 서버에 의해 생성된 “connection” 이벤트에 기능을 추가해놓았다면, client에서 connect 이벤트 이후에 실행해야 멀티 사용자 기반 추적이 가능해요. 기존에 emit 함수를 사용하는 connect 이벤트는 보안과 성능 문제 때문에 권장되지 않습니다. connect 이벤트 대신, DOM이 렌더링 된 후 useEffect 함수를 통해 socket 연결 및 데이터 요청을 하시는 것이 좋습니다.

수고하세요!

bin님의 프로필 이미지
bin

작성한 질문수

질문하기