Mi Lugarcito
Node.js - Twitter Clone Coding // Real time messages(Socket.IO) 본문
Express.js
Node.js - Twitter Clone Coding // Real time messages(Socket.IO)
selene park 2021. 4. 5. 13:10medium.com/@vdongbin/socket-io-%EB%A7%9B%EB%B3%B4%EA%B8%B0-aa9fbf1a2eb7
https://cdn.socket.io/3.1.3/socket.io.min.js" integrity="sha384-cPwlPLvBTa3sKAgddT6krw0cJat7egBga3DJepJyrLl4Q9/5WLra3rrnMcyTyOnh" crossorigin="anonymous
npm install socket.io // 설치하기
socket.io/docs/v3/client-installation/index.html
https://cdn.socket.io/3.1.3/socket.io.min.js
크롬 접속시
사파리 접속시
app.js
//server part here
const express = require('express');
const app = express();
const port = 3003;
const middleware = require('./middleware')
const path = require('path')
const bodyParser = require("body-parser")
const mongoose = require("./database");
const session = require("express-session");
const server = app.listen(port, () => console.log("Server listening on port " + port));
//socket io server build...
const io = require("socket.io")(server, { pingTimeout: 60000 });
app.set("view engine", "pug");
app.set("views", "views");
app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, "public")));
app.use(session({
secret: "bbq chips",
resave: true,
saveUninitialized: false
}))
// Routes
const loginRoute = require('./routes/loginRoutes');
const registerRoute = require('./routes/registerRoutes');
const postRoute = require('./routes/postRoutes');
const profileRoute = require('./routes/profileRoutes');
const uploadRoute = require('./routes/uploadRoutes');
const searchRoute = require('./routes/searchRoutes');
const messagesRoute = require('./routes/messagesRoutes');
const logoutRoute = require('./routes/logout');
// Api routes
const postsApiRoute = require('./routes/api/posts');
const usersApiRoute = require('./routes/api/users');
const chatsApiRoute = require('./routes/api/chats');
const messagesApiRoute = require('./routes/api/messages');
app.use("/login", loginRoute);
app.use("/register", registerRoute);
app.use("/posts", middleware.requireLogin, postRoute);
app.use("/profile", middleware.requireLogin, profileRoute);
app.use("/uploads", uploadRoute);
app.use("/search", middleware.requireLogin, searchRoute);
app.use("/messages", middleware.requireLogin, messagesRoute);
app.use("/api/posts", postsApiRoute);
app.use("/api/users", usersApiRoute);
app.use("/api/chats", chatsApiRoute);
app.use("/api/messages", messagesApiRoute);
app.use("/logout", logoutRoute);
app.get("/", middleware.requireLogin, (req, res, next) => {
var payload = {
pageTitle: "Home",
userLoggedIn: req.session.user,
userLoggedInJs: JSON.stringify(req.session.user),
}
res.status(200).render("home", payload);
})
//socket io - server part
//이렇게 세팅해도 실행안됨. 클라이언트에서도 세팅을 해줘야한다.
io.on("connection", socket => {//socket 의미 : client 를 뜻함
//console.log("connected to socket io");
//event listener register 하기
//callback function = arrow function..?
//this part is event and "setup" is just calling name of event call..
//3. 서버쪽에서 client가 넘겨준 userLoggedIn를 받아서 userData 로 넘겨준다.
socket.on("setup", userData => {
//console.log(userData.firstName)
socket.join(userData._id);//-4, room 만들고
socket.emit("connected");//-5, connected 한다. 그리고 나서 클라이언트가 이 이벤트를 받으면
})
//클라이언트가 던졌으니 서버에서 받아야함
socket.on("join room", room => socket.join(room));//room은 chatId 를 뜻함
socket.on("typing", room => socket.in(room).emit("typing"));//-2, in means inside
})
chatPage.js
//client part!!!
//const { Socket } = require("socket.io"); //이제 더이상 클라이언트가 접속해도 여기 콘솔에 접속했다는 메시지 안뜬다!
$(document).ready(() => {
//join the room
socket.emit("join room", chatId);//server 에게 join room 라고 알려주고 chatId 넘겨주면서 서버에게 받을 준비하라고 알려주기
socket.on("typing", () => console.log("user is typing...."));
$.get(`/api/chats/${chatId}`, (data) => $("#chatName").text(getChatName(data)))
//loading 새로 했을때 창에 보이도록 하기
$.get(`/api/chats/${chatId}/messages`, (data) => {
//console.log(data);
var messages = [];
//상대방 대화 끝나고 프로필 사진 보여주기
var lastSenderId = "";
data.forEach((message, index)=>{//index를 한 덩이로 0,1,2,3...이렇게 숫자로 구분//---1
var html = createMessageHtml(message, data[index+1], lastSenderId);//data는 무조건 배열 [] 로 표기해줘야함!!
messages.push(html);//여기서 messages는 array 이다!
lastSenderId = message.sender._id;
})
var messagesHtml = messages.join("");//messagesHtml 는 huge array, one big string
addMessagesHtmlToPage(messagesHtml);
scrollToBottom(false);
$(".loadingSpinnerContainer").remove();
$(".chatContainer").css("visibility", "visible");
})
})
$("#chatNameButton").click(() => {
var name = $("#chatNameTextbox").val().trim();
$.ajax({
url: "/api/chats/" + chatId,
type: "PUT",
data: { chatName: name },
success: (data, status, xhr) => {
if(xhr.status != 204) {
alert("could not update");
}
else {
location.reload();
}
}
})
})
$(".sendMessageButton").click(() => {
messageSubmitted();
})
//send the notification to the socket을 위해서 코드 추가
$(".inputTextbox").keydown((event) => {
updateTyping();
if(event.which === 13) {
messageSubmitted();
return false;
}
})
//send the notification to the socket을 위해서 코드 추가
function updateTyping(){
socket.emit("typing", chatId);//-1
}
//채팅 메시지 화면에 뿌리기
function addMessagesHtmlToPage(html){
$(".chatMessages").append(html);
//TODO : SCROLL TO BOTTOM
}
function messageSubmitted() {
var content = $(".inputTextbox").val().trim();
if(content != "") {
sendMessage(content);
$(".inputTextbox").val("");
}
}
function sendMessage(content) {
$.post("/api/messages", { content: content, chatId: chatId }, (data, status, xhr) => {
if(xhr.status !=201){
alert("Could not send message");
$(".inputTextbox").val(content);
return;
}
//console.log(data);
addChatMessageHtml(data);
})
}
function addChatMessageHtml(message){
if(!message || !message._id){
alert("Message is not valid");
return;
}
var messageDiv = createMessageHtml(message, null, "");//3
//$(".chatMessages").append(messageDiv);
addMessagesHtmlToPage(messageDiv);
}
function createMessageHtml(message, nextMessage, lastSenderId){//2
var sender = message.sender;
var senderName = sender.firstName + " " + sender.lastName;
var currentSenderId = sender._id;
var nextSenderId = nextMessage != null ? nextMessage.sender._id : "";
//중요 2줄!!
var isFirst = lastSenderId !==currentSenderId;//즉 본인을 뜻
var isLast = nextSenderId !==currentSenderId;
var isMine = message.sender._id === userLoggedIn._id;
var liClassName = isMine ? "mine" : "theirs";
var nameElement = "";
//이 아래 2개의 로직은 메시지 1개만 보냈을때 first 이자 last 일 수 있다.
if(isFirst){
liClassName += " first";
if(!isMine){
nameElement = `<span class='senderName'>${senderName}</span>`;
}
}
var profileImage = "";
if(isLast){
liClassName += " last";
profileImage = ` <img src='${sender.profilePic}'>`;
}
var imageContainer = "";
if(!isMine){
imageContainer = `<div class='imageContainer'>
${profileImage}
</div>`;
}
return `<li class='message ${liClassName}'>
${imageContainer}
<div class='messageContainer'>
${nameElement}
<span class ='messageBody'>
${message.content}
</span>
</div>
</li>`;
}
function scrollToBottom(animated){
var container = $(".chatMessages");
var scrollHeight = container[0].scrollHeight;
if(animated){
container.animate({scrollTop : scrollHeight}, "slow");
}else{
container.scrollTop(scrollHeight);
}
}
clientSocket.js
//클라이언트 소켓 -- client part
//socket instance 만들려면 socket io 를 만들어야 한다
var connected = false;
//1. 클라이언트가 soket io 접속
var socket = io("http://localhost:3003");
//2. client 가 "setup" 이벤트 리스너를 받아서 userLoggedIn 을 서버로 보내주면
socket.emit("setup", userLoggedIn);
//6. 클라이언트가 connected 이 이벤트를 받으면 우리는 소켓연결 성공했다는것을 알 수 있고 connected = true라고 할 수 있다.
socket.on("connected", () => connected = true)
'Express.js' 카테고리의 다른 글
Node.js - Twitter Clone Coding // Displaying notifications (0) | 2021.04.05 |
---|---|
Node.js - Twitter Clone Coding // Sending notifications (0) | 2021.04.05 |
Node.js - Twitter Clone Coding // Outputting messages (0) | 2021.04.05 |
Node.js - Twitter Clone Coding // Sending messages (0) | 2021.04.04 |
Node.js - Twitter Clone Coding // Changing the chat name (0) | 2021.04.04 |