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

챠챠_님의 프로필 이미지

작성한 질문수

Next + React Query로 SNS 서비스 만들기

/message 페이지 수정하기

채팅 구현했는데 소켓 연결이 됐다 안됐다 늦게 됐다 합니다.

해결된 질문

24.07.05 06:22 작성

·

118

0

안녕하세요 선생님

강의를 보고 참고해서 채팅 기능을 만들었습니다.

그런데 a,b가 채팅화면에 들어왔을때 소켓 연결이 안되거나, 늦게 되거나 하는 경우가 있어서 제가 로직을 잘못짠건지 어딜 확인해야하는건지 조언을 들을수 있을까해서 문의 남깁니다.

먼저 서버에서 이렇게 socket.io를 설정해주고

/app.js

const app = express();
const httpServer = new createServer(app);
const io = new Server(httpServer, {
  cors: {
    origin: 'https://zzimzzim.com',
    credentials: true,
  },
});

/utils/io.js

module.exports = function (io, db) {
  const userSockets = {};
  const nsp = io.of('/messages');
  nsp.on('connection', (socket) => {
    console.log('soket connected');

    socket.on('disconnect', () => {
      console.log('-----------------user disconnected');
    });
    socket.on('login', (user) => {
      userSockets[user.id] = socket.id;
      console.log(userSockets, '-------------------------------userSockets');
      console.log(`User ${user.id} connected with socket id ${socket.id} --------- 유저입장`);
    })

    socket.on('sendMessage', async (data) => {
      try {
        const roomId = data.room.split('-').sort().join('-');
        console.log(roomId, '---------------------------------roomId');
        console.log(data, '---------------------------------data');
        console.log(userSockets, '---------------------------------userSockets');

        let room = await db.Room.findOne({
          where: { room: roomId },
          include: [
            {
              model: db.User,
              as: 'RoomSender',
              attributes: ['id', 'email', 'gender', 'mbti'],
              include: [{model: db.Image}],
            },
            {
              model: db.User,
              as: 'RoomReceiver',
              attributes: ['id', 'email', 'gender', 'mbti'],
              include: [{model: db.Image}]
            },
          ]
        });

        if (room) {
          room = await room.update({ content: data.content });
        } else { 
          const roomCreated = await db.Room.create({
            SenderId: data.senderId,
            ReceiverId: data.receiverId,
            content: data.content,
            room: roomId,
          })
          console.log(roomCreated, '------------------------------roomCreated')
          room = await db.Room.findOne({
            where: { room: roomId },
            include: [
              {
                model: db.User,
                as: 'RoomSender',
                attributes: ['id', 'email', 'gender', 'mbti'],
                include: [{model: db.Image}],
              },
              {
                model: db.User,
                as: 'RoomReceiver',
                attributes: ['id', 'email', 'gender', 'mbti'],
                include: [{model: db.Image}]
              },
            ]
          })
        }

        const savedMessage = await db.Message.create({
          SenderId: data.senderId,
          ReceiverId: data.receiverId,
          content: data.content,
          room: roomId,
        });

        
        // console.log(savedMessage, '------------------------Message saved');
        console.log(room, '------------------------room saved');
        const receiverSocketId = userSockets[data.receiverId];
        if (receiverSocketId) {
          socket.to(receiverSocketId).emit('receiveMessage', savedMessage);
          socket.to(receiverSocketId).emit('receiveRoom', room);
        } else {
          console.log(`User ${data.receiverId} is not connected`);
        }
      } catch (error) {
        console.error('Error saving message:', error);
      }
    });
  })
}

클라이언트에서는

선생님이 useSocket만든것과는 다르게
useContext를 이용해서
/src/app/(afterLogin)/messages/_component/SocketProvider.tsx

'use client';

import { useSession } from 'next-auth/react';
import { ReactNode, createContext, useCallback, useEffect, useMemo, useState } from 'react';
import { io, Socket } from 'socket.io-client';

type Props = { children: ReactNode };
type SocketContextType = {
  socket: Socket | null;
  isConnected: boolean;
  disconnect: Function;
  goDown: boolean;
  setGoDown: Function;
}

export const SocketContext = createContext<SocketContextType>({
  socket: null,
  isConnected: false,
  disconnect: () => { },
  goDown: false,
  setGoDown: () => {},
})

export default function SocketProvider({ children }: Props) {
  const { data: session } = useSession();
  const [socket, setSocket] = useState<any | null>(null);
  const [isConnected, setIsConnected] = useState(false);
  const [goDown, setGoDown] = useState(false);
  const disconnect = useCallback(() => {
    socket?.disconnect();
    setSocket(null);
  }, [socket]);

  const value = useMemo(() => {
    return { socket, isConnected, disconnect, goDown, setGoDown }
  }, [socket, isConnected, disconnect, goDown]);

  useEffect(() => {
    console.log(socket, '-----------------------------------------socket???');
    if (!socket) {
      const socketInstance = io(`${process.env.NEXT_PUBLIC_BASE_URL}/messages`, {
        withCredentials: true,
      });
  
      socketInstance.on('connect', async () => {
        setIsConnected(true);
        console.log("소켓연결 성공!!!", socketInstance.id);
        // console.log(socketInstance, '--------------------socketInstance');
      })
  
      setSocket(socketInstance);
    }
  }, [socket]);

  useEffect(() => {
    if (socket?.connected && session?.user?.id) {
      console.log('--------------------------------------------socket emit login')
      socket?.emit('login', { id: session?.user?.id });
    }
  }, [session, socket]);

  return (
    <SocketContext.Provider value={value}>
      {children}
    </SocketContext.Provider>
  )
}

이런식으로 만들고
/src/app/(afterLogin)/messages/layout.tsx

import { ReactNode } from 'react';
import SocketProvider from './_component/SocketProvider';

type Props = { children: ReactNode };

export default function Layout({ children }: Props) {
  return (
    <SocketProvider>
      {children}
    </SocketProvider>
  )
}

레이에다가 적용해주었습니다.

이런식으로 해주면 소켓연결을 한번만 하고 원할때 메세지를 주고 받을 수 있을거라고 생각해서 적용한건데

혹시 소켓 연결 관련해서 더 확인해야하거나 수정할 부분이 있을지 궁금합니다.

감사합니다!

답변 1

0

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

2024. 07. 05. 10:27

연결이 되었는지 안 되었는지부터 확인하셔야 되고, 모든 것은 다 개발자도구 네트워크 탭에 나옵니다. websocket이 있는지와 있다면 연결된걸로 나오는지 보시면 됩니다.

챠챠_님의 프로필 이미지
챠챠_
질문자

2024. 07. 05. 18:19

감사합니다.

선생님 조언덕에 해결할 수 있었습니다.

챠챠_님의 프로필 이미지

작성한 질문수

질문하기