[]
이 튜토리얼은 인증 및 권한 관리를 구현하기 위해 서버와 클라이언트 코드를 수정하는 과정을 개발자에게 안내합니다. 다음을 수행하게 됩니다.
사용자 이름과 비밀번호로 로그인하여 토큰을 획득
토큰을 사용해 채팅방에 참여
역할 권한(admin, member, guest)에 따라 메시지 전송
실시간 채팅방 만들기 완료
추가 패키지 설치:
npm install jsonwebtoken로그인 라우트와 인증 미들웨어를 서버에 추가하여, 인증된 사용자만 채팅방에 참여할 수 있도록 하고 역할 권한에 따라 메시지 전송을 제어합니다.
server.js 업데이트
server.js의 기존 내용을 다음 코드로 교체합니다.
import express from "express";
import { createServer } from "http";
import { Server } from "@mescius/js-collaboration";
import jwt from "jsonwebtoken";
const app = express();
const httpServer = createServer(app);
const server = new Server({ httpServer });
app.use(express.static("public"));
app.use(express.json());
// JWT secret key
const JWT_SECRET = "your-secret-key-here";
// Simulated user database
const users = new Map([
["admin1", { username: "admin1", password: "admin123", role: "admin" }],
["member1", { username: "member1", password: "member123", role: "member" }],
["guest1", { username: "guest1", password: "guest123", role: "guest" }],
]);
// Login route
app.post("/login", (req, res) => {
const { username, password } = req.body;
const user = users.get(username);
if (!user || user.password !== password) {
return res.status(401).json({ success: false, message: "Invaild username or passward." });
}
const token = jwt.sign({ username: user.username, role: user.role }, JWT_SECRET);
res.json({ success: true, token, user: { username: user.username, role: user.role } });
});
// Authentication middleware
server.use("connect", async (context, next) => {
const token = context.connection.auth?.token;
if (!token) return await next("No token provided");
try {
const decoded = jwt.verify(token, JWT_SECRET);
context.connection.tags.set("user", { username: decoded.username, role: decoded.role });
await next();
} catch {
await next("Invalid token");
}
});
// Message permission validation middleware
server.use("message", async ({ connection }, next) => {
const user = connection.tags.get("user");
if (user.role === "admin" || user.role === "member") await next();
else await next("No permission to send messages");
});
// Event handlers
server.on("connect", ({ connection }) => {
const user = connection.tags.get("user");
console.log(`${user.username} joined the room`);
connection.broadcast(`${user.username} joined the chat room`, "", true);
});
server.on("message", ({ connection, data }) => {
const user = connection.tags.get("user");
console.log(`Received message from ${user.username}: ${data}`);
connection.broadcast(`${user.username}: ${data}`, "", true);
});
server.on("disconnect", ({ connection }) => {
const user = connection.tags.get("user");
console.log(`${user.username} left the room`);
connection.broadcast(`${user.username} left the chat room`, "", true);
});
httpServer.listen(8080, () => {
console.log("Server running at http://localhost:8080");
});코드 설명
역할 정의:
admin: 최고 권한, 메시지 전송 및 채팅방 관리 가능
member: 일반 사용자, 메시지 송수신 가능
guest: 메시지 수신만 가능
인증: /login 라우트를 통해 토큰을 발급받고, connect 미들웨어에서 토큰을 검증하여 사용자 정보를 저장합니다.
권한: message 미들웨어를 통해 guest 사용자의 메시지 전송을 제한합니다.
클라이언트에 로그인 인터페이스를 추가하고, 로그인 후 토큰을 사용해 채팅방에 연결합니다.
public/index.html 업데이트
로그인 폼을 추가하기 위해 내용을 다음 코드로 교체합니다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Real-Time Chat Room (with Authentication)</title>
<link rel="stylesheet" href="./styles.css">
<script type="module" src="./client.bundle.js" defer></script>
</head>
<body>
<div class="container">
<h1>Real-Time Chat Room (with Authentication)</h1>
<div id="login">
<input type="text" id="username" placeholder="Username">
<input type="password" id="password" placeholder="Password">
<button onclick="login()">Login</button>
</div>
<div id="chat" style="display: none;"></div>
<div class="input-area" style="display: none;">
<input type="text" id="message" placeholder="Enter message...">
<button onclick="sendMessage()">Send</button>
</div>
</div>
</body>
</html>public/client.js 업데이트
로그인 및 인증 로직을 추가하기 위해 내용을 다음 코드로 교체합니다.
import { Client } from "@mescius/js-collaboration-client";
let connection = null;
window.login = async function () {
const username = document.getElementById("username").value;
const password = document.getElementById("password").value;
try {
const response = await fetch("http://localhost:8080/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username, password }),
});
const result = await response.json();
if (result.success) {
connectToChat(result.token);
document.getElementById("login").style.display = "none";
document.getElementById("chat").style.display = "block";
document.querySelector(".input-area").style.display = "flex";
} else {
alert(result.message);
}
} catch (error) {
alert("Login failed: " + error.message);
}
};
function connectToChat(token) {
const client = new Client();
connection = client.connect("chatroom", { auth: { token } });
connection.on("message", (data) => {
const chatDiv = document.getElementById("chat");
const message = document.createElement("p");
message.textContent = data;
chatDiv.appendChild(message);
chatDiv.scrollTop = chatDiv.scrollHeight;
});
connection.on("error", (error) => {
alert("Error: " + error.message);
});
}
window.sendMessage = function () {
const input = document.getElementById("message");
const message = input.value.trim();
if (message && connection) {
connection.send(message);
input.value = "";
}
};코드 설명
로그인: /login 엔드포인트를 통해 토큰을 발급받고, 로그인 성공 시 채팅방에 연결합니다.
인터페이스: 로그인 성공 시 로그인 폼을 숨기고 채팅 영역을 표시합니다.
메시지: admin과 member 사용자만 서버에서 메시지가 브로드캐스트됩니다.
public/styles.css 업데이트
내용을 다음 코드로 교체합니다.
html, body {
height: 100%;
margin: 0;
font-family: Arial, sans-serif;
background: #f0f2f5;
}
.container {
height: 100vh;
display: flex;
flex-direction: column;
background: #fff;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
h1 {
padding: 15px;
margin: 0;
font-size: 24px;
color: #333;
text-align: center;
border-bottom: 1px solid #ddd;
}
#login {
padding: 20px;
text-align: center;
}
#login input {
padding: 10px;
margin: 5px;
border: 1px solid #ccc;
border-radius: 5px;
font-size: 14px;
}
#chat {
flex: 1;
overflow-y: auto;
padding: 15px;
background: #fafafa;
}
#chat p {
margin: 10px 0;
padding: 10px;
background: #e9ecef;
border-radius: 5px;
word-wrap: break-word;
}
.input-area {
display: flex;
padding: 15px;
border-top: 1px solid #ddd;
background: #f9f9f9;
}
#message {
flex: 1;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
font-size: 14px;
margin-right: 10px;
}
button {
padding: 10px 20px;
background: #007bff;
color: #fff;
border: none;
border-radius: 5px;
cursor: pointer;
}
button:hover {
background: #0056b3;
}클라이언트 코드 번들링
npm run build서버 시작
npm run start기능 테스트
다음 사용자로 로그인:
admin1 / admin123 (메시지 전송 가능)
member1 / member123 (메시지 전송 가능)
guest1 / guest123 (메시지 전송 불가)
여러 사용자가 참여, 메시지 전송 및 퇴장하는 실시간 동작을 확인합니다.