Express, React, Mongoose 로 웹소켓 채팅앱 만들기. 3
1. chat, user table 완성 후 socket으로 front-back 상호작용 연결하기
2023.10.13 - [개발관련/개인프로젝트] - Express, React, Mongoose 로 웹소켓 채팅앱 만들기. 1
Express, React, Mongoose 로 웹소켓 채팅앱 만들기. 1
쇼핑몰 애플리케이션에서 간단하게 구현해봤던 웹소켓을 이용해서 간단한 채팅앱을 구현해보기로 했다. Express, React, Mongoose 로 웹소켓 채팅앱 만들기. 1 1. 어떻게 만들 것인가? 2. 오늘 겪은 문제
veritas-crystal.tistory.com
2. user login 기능 구현, user 정보 전달/저장
2023.10.19 - [개발관련/개인프로젝트] - Express, React, Mongoose 로 웹소켓 채팅앱 만들기. 2
Express, React, Mongoose 로 웹소켓 채팅앱 만들기. 2
Express, React, Mongoose 로 웹소켓 채팅앱 만들기. 2 1탄에서 socket 으로 프론트/백 연결까지 했으니, 이제는 user login 기능을 구현해야한다. 1. Today I learned 2. 오늘 겪은 문제 위의 목차를 클릭하면 해당
veritas-crystal.tistory.com
오늘은 chat 내용을 db에 전달/저장하는 마지막 단계를 완성할 예정이다.
위의 목차를 클릭하면 해당 글로 자동 이동 합니다.
1. Today I learned
login한 user가 채팅을 위해 text를 입력하면, 그 text는 message list에 저장되어야하고,
채팅방에 있는 모두가 알아야한다. 먼저 front 에서 sendMessage 함수를 통해 text를 전달받고, socket emit을 통해 back으로 전달하는 기능을 구현했다.
import { useEffect, useState } from 'react';
import './App.css';
import socket from './server';
import InputField from './components/InputField/InputField';
import MessageContainer from './components/MessageContainer/MessageContainer';
function App() {
const [user, setUser] = useState(null);
const [message, setMessage] = useState('');
const [messageList, setMessageList] = useState([]);
console.log('messageList', messageList);
useEffect(() => {
askUserName();
socket.on('message', (message) => {
setMessageList((prevState) => prevState.concat(message));
});
}, []);
const askUserName = () => {
const userName = prompt('당신의 이름을 입력하세요');
// console.log('uuu', userName);
socket.emit('login', userName, (res) => {
if (res?.ok) {
setUser(res.data);
}
console.log('Res', res);
});
};
const sendMessage = (event) => {
event.preventDefault();
socket.emit('sendMessage', message, (res) => {
console.log('sendMessage res', res);
});
};
back으로 전달된 메세지는 user아이디를 확인 후 db에 저장되어야 한다.
const chatController = require('../Controllers/chat.controller');
const userController = require('../Controllers/user.controller');
module.exports = function (io) {
// io 관련된 모든 일
io.on('connection', async (Socket) => {
console.log('client connection', Socket.id);
//1. 유저정보
Socket.on('login', async (userName, cb) => {
// 유저 정보를 저장
console.log(`${userName}님이 채팅방에 입장하셨습니다.`);
try {
const user = await userController.saveUser(userName, Socket.id);
const welcome = {
chat: `${user.name}is joined to this room`,
user: { id: null, name: 'system' },
};
io.emit('message', welcome);
cb({ ok: true, data: user });
} catch (error) {
cb({ ok: false, data: error.message });
}
});
Socket.on('sendMessage', async (message, cb) => {
try {
// socket id로 유저를 찾고
const user = await userController.checkUser(Socket.id);
// 메세지 저장(유저)
const newMessage = await chatController.saveChat(message, user);
io.emit('message', newMessage);
cb({ ok: true });
} catch (error) {
cb({ ok: false, data: error.message });
}
});
Socket.on('disconnect', () => {
console.log('user disconnected');
});
});
};
newMessage는 message와 user를 인자로 받는 chatController.saveChat 메서드에서 반환된 값이다.
인자로 받을 user는 checkUser 라는 함수안에서 socket.id 를 통해 확인한다.
확인된 user와 message 는 io.emit를 통해 방에 있는 모두에게 알려진다.
여기서 이용될 chatController.saveChat 함수는, 따로 chatController 파일 안에서 구현된다.
const Chat = require('../Models/chat');
const chatController = {};
chatController.saveChat = async (message, user) => {
const newMessage = new Chat({
chat: message,
user: {
id: user._id,
name: user.name,
},
});
await newMessage.save();
return newMessage;
};
module.exports = chatController;
chatController.saveChat 함수는 chat model의 형태를 가진 chat을 저장하는 역할을 한다.
chat schema는 chat(String), 해당 chat의 _id, user정보(user id, name)으로 이루어져있다.
아래와 같이 닉네임 '짱구맨'으로 로그인 후, 채팅을 쳐보았다.
짱구맨에 해당하는 socket id는 'N3BPzxYkWEXViddoAAAB'
짱구맨의 _id 는 "6540b3f10b771a5cb2314c77" 이다.
만약 코드가 정상적으로 작동한다면 chat db에는 chat schema는 chat(String), 해당 chat의 _id, user정보(user id, name)가 저장될 것이다.
두근두근두근
.
.
.
.
.
짜잔~~!
String 타입의 chat, chat의 _id, 그리고 Object타입의 user 정보가 정상적으로 저장되었다.
user object에는 user의 id(ObjectId : "6540b3f10b771a5cb2314c77")가 저장되었고,
user name도 잘 저장된 것을 볼 수 있다.
이렇게 프론트에서 sendMessage 이벤트를 받으면 백에서 user, message를 확인 저장하고 to.emit을 통해 새 메세지 객체를 모든 연결된 클라이언트에게 브로드캐스팅 하는 것이다. 😚
2. 오늘 겪은 문제
1. user 정보가 제대로 전달되지 않는 문제
front-back 코드를 완성 후 messageContainer css를 통해 메세지를 출력하려 하는데, 오류가 발생했다.
내용을 살펴보면 어떤 객체의 name property를 읽으려고 시도했지만 해당 객체가 정의되지 않았다는 뜻이다.
askUserName을 통해 name을 입력했는데도 name 을 읽을 수 없는 이유가 뭘까?
살펴보니 userController.checkUser(Socket.id)를 사용하여 사용자를 찾는 동안 await가 빠져 발생하는 문제였다.
userController.checkUser(Socket.id)는 비동기 함수이기 때문에 await 를 사용하여 해당 함수가 완료될 때까지 기다려야한다. await가 없으면 해당 함수의 결과를 받아오기 전에 다음 코드로 넘어가기 때문에, user정보가 check되지 않고 넘어가게되므로 메세지가 저장될 수 없는 것이다.
Socket.on('sendMessage', async (message, cb) => {
try {
// socket id로 유저를 찾고
const user = userController.checkUser(Socket.id);
// 메세지 저장(유저)
const newMessage = await chatController.saveChat(message, user);
io.emit('message', newMessage);
cb({ ok: true });
} catch (error) {
cb({ ok: false, data: error.message });
}
});
await 를 추가하면 문제는 해결!
2. text를 입력한 후에 메세지 입력칸을 공백으로 만들기
text 입력 후에도 메세지 입력창에 문장이 그대로 있어서, 실제 app과 유사하게 수정했다!
//inputFeild.jsx
import React from 'react';
import { Input } from '@mui/base/Input';
import { Button } from '@mui/base/Button';
import './InputField.css';
const InputField = ({ message, setMessage, sendMessage }) => {
const handleSendMessage = (event) => {
event.preventDefault();
// 메시지를 보낸 후 메시지 상태 초기화
sendMessage(event);
setMessage(''); // 메시지를 초기화하여 입력창을 비웁니다.
};
이렇게 해서 채팅앱 구현을 해보았고, 추후 채팅방 기능을 구현할 생각이다.
'개발 > 개인, 사이드 프로젝트' 카테고리의 다른 글
Express, React, Mongoose 로 웹소켓 채팅앱 만들기. 4 [채팅방 추가] (0) | 2023.11.08 |
---|---|
Express, React, Mongoose 로 웹소켓 채팅앱 만들기. 2 (1) | 2023.10.19 |
Express, React, Mongoose 로 웹소켓 채팅앱 만들기. 1 (0) | 2023.10.13 |
개인프로젝트 - 01. 프로젝트 시작 (0) | 2023.09.06 |