π 곡λΆνλ μ§μ§μνμΉ΄λ μ²μμ΄μ§?
[Node js Project - 1] Realtime Chat With Users & Rooms λ³Έλ¬Έ
π©π» λ°±μλ(Back-End)/Node js
[Node js Project - 1] Realtime Chat With Users & Rooms
μ§μ§μνμΉ΄ 2023. 6. 16. 01:30728x90
λ°μν
<λ³Έ λΈλ‘κ·Έλ Traversy Media μ μ νλΈλ₯Ό μ°Έκ³ ν΄μ 곡λΆνλ©° μμ±νμμ΅λλ€ :-)>
=> Realtime Chat With Users & Rooms - Socket.io, Node & Express
π 1) Front-End μ½λ νμΌ λ³΅λΆνκΈ°
https://github.com/bradtraversy/chatcord/tree/master/public
π 2) νκ²½μ€μ
npm init
npm install express socket.io moment
npm install -D nodemon
π package.json
{
"name": "realtimechat",
"version": "1.0.0",
"description": "realtime chat app with rooms",
"main": "index.js",
"scripts": {
"start": "node server",
"dev": "nodemon server"
},
"author": "gani",
"license": "MIT",
"dependencies": {
"express": "^4.18.2",
"moment": "^2.29.4",
"socket.io": "^4.6.2"
},
"devDependencies": {
"nodemon": "^2.0.22"
}
}
π server.js
const express = require("express");
const app = express();
const path = require("path");
const PORT = 3000 || process.env.PORT;
// Set static folder
app.use(express.static(path.join(__dirname, "public")));
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
π 4) μ€ννκΈ°
npm run dev
π 5) μ±ν μ°½ κΈ°λ₯
Room Name λ μκ³ , Users μ μ§μ ν μ΄λ¦μ΄ λ€μ΄κ°κ³
λ€μ΄μ€κ±°λ λκ° λ chatbot μ΄ μλ΄ν΄μ€λΉ
π μ΅μ’ μ½λ
π server.js
const express = require("express");
const http = require("http");
const path = require("path");
const PORT = 3000 || process.env.PORT;
const socketio = require("socket.io");
const formatMessage = require("./utils/messages");
const {
userJoin,
getCurrentUser,
userLeave,
getRoomUsers,
} = require("./utils/users");
const app = express();
const server = http.createServer(app);
const io = socketio(server);
// Set static folder
app.use(express.static(path.join(__dirname, "public")));
const botName = "πΎ Chat Bot";
// Run when client connects
io.on("connection", (socket) => {
socket.on("joinRoom", ({ username, room }) => {
const user = userJoin(socket.id, username, room);
socket.join(user.room);
// Welcome current user
socket.emit("message", formatMessage(botName, "Welcome to ChatCord!"));
// Broadcast when a user connects
socket.broadcast
.to(user.room)
.emit(
"message",
formatMessage(botName, `A ${user.username} has joined the chat`)
);
// Send users and room info
io.to(user.room).emit("roomUsers", {
room: user.room,
users: getRoomUsers(user.room),
});
});
// Listen for chatMessage
socket.on("chatMessage", (msg) => {
const user = getCurrentUser(socket.id);
io.to(user.room).emit("message", formatMessage(user.username, msg));
});
// Runs when client disconnects
socket.on("disconnect", () => {
const user = userLeave(socket.id);
if (user) {
io.to(user.room).emit(
"message",
formatMessage(botName, `${user.username} has left the chat`)
);
}
});
});
server.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
π utils/messages.js
// μκ° λ° λ μ§ μ€λΈμ νΈμΈ Date
const moment = require("moment");
function formatMessage(username, text) {
return {
username,
text,
time: moment().format("h:mm a"),
};
}
module.exports = formatMessage;
π utils/users.js
const users = [];
// Join user to chat
function userJoin(id, username, room) {
const user = { id, username, room };
users.push(user);
return user;
}
// Get current user
function getCurrentUser(id) {
return users.find((user) => user.id === id);
}
// User leaves chat
function userLeave(id) {
const index = users.findIndex((user) => user.id === id);
if (index !== -1) {
return users.splice(index, 1)[0];
}
}
// Get room users
function getRoomUsers(room) {
return users.filter((user) => user.room === room);
}
module.exports = {
userJoin,
getCurrentUser,
userLeave,
getRoomUsers,
};
π public/css/style.css
@import url('https://fonts.googleapis.com/css?family=Roboto&display=swap');
:root {
--dark-color-a: #667aff;
--dark-color-b: #7386ff;
--light-color: #e6e9ff;
--success-color: #5cb85c;
--error-color: #d9534f;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Roboto', sans-serif;
font-size: 16px;
background: var(--light-color);
margin: 20px;
}
ul {
list-style: none;
}
a {
text-decoration: none;
}
.btn {
cursor: pointer;
padding: 5px 15px;
background: var(--light-color);
color: var(--dark-color-a);
border: 0;
font-size: 17px;
}
/* Chat Page */
.chat-container {
max-width: 1100px;
background: #fff;
margin: 30px auto;
overflow: hidden;
}
.chat-header {
background: var(--dark-color-a);
color: #fff;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
padding: 15px;
display: flex;
align-items: center;
justify-content: space-between;
}
.chat-main {
display: grid;
grid-template-columns: 1fr 3fr;
}
.chat-sidebar {
background: var(--dark-color-b);
color: #fff;
padding: 20px 20px 60px;
overflow-y: scroll;
}
.chat-sidebar h2 {
font-size: 20px;
background: rgba(0, 0, 0, 0.1);
padding: 10px;
margin-bottom: 20px;
}
.chat-sidebar h3 {
margin-bottom: 15px;
}
.chat-sidebar ul li {
padding: 10px 0;
}
.chat-messages {
padding: 30px;
max-height: 500px;
overflow-y: scroll;
}
.chat-messages .message {
padding: 10px;
margin-bottom: 15px;
background-color: var(--light-color);
border-radius: 5px;
overflow-wrap: break-word;
}
.chat-messages .message .meta {
font-size: 15px;
font-weight: bold;
color: var(--dark-color-b);
opacity: 0.7;
margin-bottom: 7px;
}
.chat-messages .message .meta span {
color: #777;
}
.chat-form-container {
padding: 20px 30px;
background-color: var(--dark-color-a);
}
.chat-form-container form {
display: flex;
}
.chat-form-container input[type='text'] {
font-size: 16px;
padding: 5px;
height: 40px;
flex: 1;
}
/* Join Page */
.join-container {
max-width: 500px;
margin: 80px auto;
color: #fff;
}
.join-header {
text-align: center;
padding: 20px;
background: var(--dark-color-a);
border-top-left-radius: 5px;
border-top-right-radius: 5px;
}
.join-main {
padding: 30px 40px;
background: var(--dark-color-b);
}
.join-main p {
margin-bottom: 20px;
}
.join-main .form-control {
margin-bottom: 20px;
}
.join-main label {
display: block;
margin-bottom: 5px;
}
.join-main input[type='text'] {
font-size: 16px;
padding: 5px;
height: 40px;
width: 100%;
}
.join-main select {
font-size: 16px;
padding: 5px;
height: 40px;
width: 100%;
}
.join-main .btn {
margin-top: 20px;
width: 100%;
}
@media (max-width: 700px) {
.chat-main {
display: block;
}
.chat-sidebar {
display: none;
}
}
π public/js/main.js
const chatForm = document.getElementById("chat-form");
const chatMessage = document.querySelector(".chat-messages");
const roomName = document.getElementById("room-name");
const userList = document.getElementById("users");
// Get username and room from URL
const { username, room } = Qs.parse(location.search, {
ignoreQueryPrefix: true,
});
const socket = io();
// Join chatroom
socket.emit("joinRoom", { username, room });
// Get room and users
socket.on("roomUsers", ({ room, users }) => {
outputRoomName(room);
outputUsers(users);
});
// Message from server
socket.on("message", (message) => {
console.log(message);
outputMessage(message);
// Scroll down
chatMessage.scrollTop = chatMessage.scrollHeight;
});
// Message submit
chatForm.addEventListener("submit", (e) => {
e.preventDefault();
// Get message text
const msg = e.target.elements.msg.value;
// Emit message to server
socket.emit("chatMessage", msg);
// Clear input
e.target.elements.msg.value = "";
e.target.elements.msg.focus();
});
// Output message to DOM
function outputMessage(message) {
const div = document.createElement("div");
div.classList.add("message");
div.innerHTML = `<p class="meta">${message.username}<span>${message.time}</span></p>
<p class="text">${message.text}</p>`;
document.querySelector(".chat-messages").appendChild(div);
}
// Add room name to DOM
function outputRoomName(room) {
roomName.innerHTML = room;
}
// Add users to DOM
function outputUsers(users) {
userList.innerHTML = `
${users.map((user) => `<li>${user.username}</li>`).join("")}
`;
}
π public/chat.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.12.1/css/all.min.css" integrity="sha256-mmgLkCYLUQbXn0B1SRqzHar6dCnv9oZFPEC1g1cwlkk=" crossorigin="anonymous" />
<link rel="stylesheet" href="css/style.css">
<title>ChatCord App</title>
</head>
<body>
<div class="chat-container">
<header class="chat-header">
<h1><i class="fas fa-smile"></i> ChatCord</h1>
<a href="index.html" class="btn">Leave Room</a>
</header>
<main class="chat-main">
<div class="chat-sidebar">
<h3><i class="fas fa-comments"></i> Room Name:</h3>
<h2 id="room-name"></h2>
<h3><i class="fas fa-users"></i> Users</h3>
<ul id="users">
</ul>
</div>
<div class="chat-messages">
</div>
</main>
<div class="chat-form-container">
<form id="chat-form">
<input
id="msg"
type="text"
placeholder="Enter Message"
required
autocomplete="off"
/>
<button class="btn"><i class="fas fa-paper-plane"></i> Send</button>
</form>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/qs/6.11.2/qs.min.js" integrity="sha512-vCegEXqPUYpZsTGz2lk0jaQ1psxtFeniVJACAXhMVxuoYa/N4nZkjoVFOxLwP7uGeQOoemiz7DQrIpRTj4IBPw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="/socket.io/socket.io.js"></script>
<script src="js/main.js"></script>
</body>
</html>
π public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.12.1/css/all.min.css"
integrity="sha256-mmgLkCYLUQbXn0B1SRqzHar6dCnv9oZFPEC1g1cwlkk="
crossorigin="anonymous"
/>
<link rel="stylesheet" href="css/style.css" />
<title>ChatCord App</title>
</head>
<body>
<div class="join-container">
<header class="join-header">
<h1><i class="fas fa-smile"></i> ChatCord</h1>
</header>
<main class="join-main">
<form action="chat.html">
<div class="form-control">
<label for="username">Username</label>
<input
type="text"
name="username"
id="username"
placeholder="Enter username..."
required
/>
</div>
<div class="form-control">
<label for="room">Room</label>
<select name="room" id="room">
<option value="JavaScript">JavaScript</option>
<option value="Python">Python</option>
<option value="PHP">PHP</option>
<option value="C#">C#</option>
<option value="Ruby">Ruby</option>
<option value="Java">Java</option>
</select>
</div>
<button type="submit" class="btn">Join Chat</button>
</form>
</main>
</div>
</body>
</html>
728x90
λ°μν
'π©βπ» λ°±μλ(Back-End) > Node js' μΉ΄ν κ³ λ¦¬μ λ€λ₯Έ κΈ
[Node js Project - 3] Create a Discord Bot (0) | 2023.06.17 |
---|---|
[Node js Project - 2] Store Locator API (0) | 2023.06.17 |
νλ‘ νΈμμ μλ²μ λ°μ΄ν° μμ²νκΈ° (Reactλ‘ fetch, axios μ¬μ©) (0) | 2023.05.18 |
React (front) μ Node (back) μ°λμν€κΈ° (0) | 2023.05.18 |
[E-Commerce App with Fullstack] (8) Product API (1) | 2023.05.16 |
Comments