diff --git a/app.js b/app.js
index 0bbd702a7db8f09349bd753fe2d2b361e64d3068..c01d3400fd1c770ad31aca180ffd61d614f7891b 100644
--- a/app.js
+++ b/app.js
@@ -16,13 +16,14 @@ const app = express();
 
 
 app.use(morgan('dev'));  //濡쒓퉭��
-// CORS �ㅼ젙
+
+// CORS �ㅼ젙 (濡쒖뺄 �섍꼍��)
 app.use(
   cors({
     origin:[ process.env.FROENT_URL,'https://yanawa.shop'],
     methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
     allowedHeaders: ['Content-Type', 'Authorization'],
-    credentials: true,
+    credentials: true, 
   })
 );
 // 
@@ -59,10 +60,10 @@ console.log('MongoDB URI:', process.env.MONGO_URI);
 const authRoutes = require('./routes/auth');
 app.use('/api/auth', authRoutes);
 
-const scheduleRoutes = require('./routes/schedule');
+const scheduleRoutes = require('./routes/scheduleRoute');
 app.use('/api/schedule', scheduleRoutes);
 
-const friendRoutes = require('./routes/friend');
+const friendRoutes = require('./routes/friendRoute');
 app.use('/api/friend', friendRoutes);
 
 const meetingRoutes = require('./routes/meetingRoute');
@@ -74,7 +75,7 @@ app.use('/api/chat', chatRoutes);
 const memberRoutes = require('./routes/memberRoute');
 app.use('/api/member', memberRoutes);
 
-const sessionRouter = require('./routes/session');
+const sessionRouter = require('./routes/sessionRoute');
 app.use('/api/session', sessionRouter);
 
 // �ㅼ�以� �대━�� 珥덇린��
diff --git a/controllers/chatController.js b/controllers/chatController.js
index 41a2d3c6f05d4f021964785d98414452322050ec..bfcca38df3a7ab0b513d3c4bee2c46533226b09b 100644
--- a/controllers/chatController.js
+++ b/controllers/chatController.js
@@ -91,4 +91,67 @@ exports.updateStatusAndLogId = async (req, res) => {
     console.error('Error updating user status and lastReadLogId:', err);
     res.status(500).json({ error: 'Failed to update user status and lastReadLogId' });
   }
+};
+
+// 怨듭� �깅줉
+exports.addNotice = async (req, res) => {
+  const { chatRoomId } = req.params;
+  const { sender, message } = req.body;
+
+  try {
+    const notice = await chatService.addNotice(chatRoomId, sender, message);
+    res.status(200).json(notice);
+  } catch (error) {
+    console.error('Error adding notice:', error.message);
+    res.status(500).json({ error: 'Failed to add notice' });
+  }
+};
+
+// 理쒖떊 怨듭� 議고쉶
+exports.getLatestNotice = async (req, res) => {
+  const { chatRoomId } = req.params;
+
+  try {
+    const latestNotice = await chatService.getLatestNotice(chatRoomId);
+    if (latestNotice) {
+      res.status(200).json(latestNotice);
+    } else {
+      res.status(404).json({ message: 'No notices found' });
+    }
+  } catch (error) {
+    console.error('Error fetching latest notice:', error.message);
+    res.status(500).json({ error: 'Failed to fetch latest notice' });
+  }
+};
+
+// 怨듭� �꾩껜 議고쉶
+exports.getAllNotices = async (req, res) => {
+  const { chatRoomId } = req.params;
+
+  try {
+    const notices = await chatService.getAllNotices(chatRoomId);
+    console.log(`[getAllNotices] Notices for chatRoomId ${chatRoomId}:`, notices); // 濡쒓렇 異붽�
+    res.status(200).json(notices);
+  } catch (error) {
+    console.error('Error fetching all notices:', error.message);
+    res.status(500).json({ error: 'Failed to fetch all notices' });
+  }
+};
+
+// 怨듭��ы빆 �곸꽭 議고쉶
+exports.getNoticeById = async (req, res) => {
+  const { chatRoomId, noticeId } = req.params;
+
+  try {
+    const notice = await chatService.getNoticeById(chatRoomId, noticeId);
+
+    if (!notice) {
+      return res.status(404).json({ error: 'Notice not found' });
+    }
+
+    res.status(200).json(notice);
+  } catch (error) {
+    console.error('Error fetching notice by ID:', error.message);
+    res.status(500).json({ error: 'Failed to fetch notice by ID' });
+  }
 };
\ No newline at end of file
diff --git a/middlewares/auth.js b/middlewares/auth.js
index 52eb397476e16fa8c98e7d2e29a15973d76215bc..10118d7670cc4e393aa23600d4d659668bd8c606 100644
--- a/middlewares/auth.js
+++ b/middlewares/auth.js
@@ -1,4 +1,5 @@
 // middlewares/auth.js
+exports.isLoggedIn = (req, res, next) => { // 濡쒓렇�몃맂 �ъ슜�먮쭔 �묎렐 �덉슜
 exports.isLoggedIn = (req, res, next) => { // 濡쒓렇�몃맂 �ъ슜�먮쭔 �묎렐 �덉슜
   if (req.isAuthenticated()) {
     return next();
diff --git a/models/index.js b/models/index.js
index e76f6335b2a0bccafc4faf8546995a203e7de6ee..7caa67277dc86e3f1dcc043a15e8897cbc7ee641 100644
--- a/models/index.js
+++ b/models/index.js
@@ -36,7 +36,7 @@ User.hasMany(Friend, {
 Meeting.belongsTo(User, {
   foreignKey: 'created_by',
   as: 'creator',
-  onDelete: 'SET NULL', // Meetings might persist even if the creator is deleted
+  onDelete: 'SET NULL', 
 });
 User.hasMany(Meeting, {
   foreignKey: 'created_by',
diff --git a/passport/googleStrategy.js b/passport/googleStrategy.js
index 6926deeb8ef2f6809d99e1f11b264a2ba08d9959..f6698b36fab68e39e1d5b078802a20e197594797 100644
--- a/passport/googleStrategy.js
+++ b/passport/googleStrategy.js
@@ -1,12 +1,13 @@
 // passport/googleStrategy.js
 const { Strategy: GoogleStrategy } = require('passport-google-oauth20');
-const User = require('../models/user'); // �ъ슜�� 紐⑤뜽�� 媛��몄샃�덈떎.
+const User = require('../models/user'); 
 
 module.exports = new GoogleStrategy(
   {
     clientID: process.env.GOOGLE_CLIENT_ID,
     clientSecret: process.env.GOOGLE_CLIENT_SECRET,
     callbackURL: process.env.CALLBACK_URL,
+    passReqToCallback: true, // req 媛앹껜瑜� 肄쒕갚�� �꾨떖
   },
   async (req, accessToken, refreshToken, profile, done) => {
     try {
diff --git a/routes/authRoute.js b/routes/authRoute.js
new file mode 100644
index 0000000000000000000000000000000000000000..e6f2d553e56f361aec5c9f6ca82d664c1b5034ff
--- /dev/null
+++ b/routes/authRoute.js
@@ -0,0 +1,84 @@
+  const express = require('express');
+  const passport = require('passport');
+
+  const router = express.Router();
+
+  // Google OAuth 濡쒓렇�� �쇱슦��
+  router.get(
+    '/login',
+    passport.authenticate('google', {
+      scope: ['profile', 'email'], // �ъ슜�� �뺣낫 �붿껌�� �꾪븳 scope 
+      failureRedirect: `${process.env.FRONT_URL}/login`
+    })
+  );
+
+  router.get(
+    '/google/callback',
+    passport.authenticate('google', {
+      failureRedirect: `${process.env.FRONT_URL}/login` 
+    }),
+    (req, res) => {
+      const redirectUrl = process.env.FRONT_URL;
+      req.session.save((err) => {
+        if (err) {
+          console.error('�몄뀡 ���� �ㅻ쪟:', err);
+          return res.status(500).json({ error: '�쒕쾭 �ㅻ쪟' });
+        }
+        res.redirect(redirectUrl);
+      });
+    }
+  );
+
+  // 濡쒓렇�꾩썐 �쇱슦��
+  router.get('/logout', (req, res) => {
+    if (req.session) {
+      req.session.destroy((err) => {
+        if (err) {
+          console.error('�몄뀡 ��젣 �ㅻ쪟:', err);
+          return res.status(500).json({ error: '�쒕쾭 �ㅻ쪟' });
+        }
+        const redirectUrl = process.env.FRONT_URL;
+        res.redirect(redirectUrl);
+      });
+    } else {
+      // �몄뀡�� �녿뒗 寃쎌슦�먮룄 由щ떎�대젆��
+      const redirectUrl = process.env.FRONT_URL;
+      res.redirect(redirectUrl);
+    }
+  });
+  
+  // �ъ슜�� ��젣 �쇱슦��
+router.delete('/leave', async (req, res) => {
+  try {
+    // �몄쬆�� �ъ슜�� �뺤씤
+    if (!req.user) {
+      return res.status(401).json({ error: '�몄쬆�섏� �딆� �ъ슜�먯엯�덈떎.' });
+    }
+
+    const userId = req.user.id;
+
+    // �ъ슜�� ��젣
+    const deleted = await User.destroy({
+      where: { id: userId }
+    });
+
+    if (!deleted) {
+      return res.status(404).json({ error: '�ъ슜�먮� 李얠쓣 �� �놁뒿�덈떎.' });
+    }
+
+    // �몄뀡 ��젣
+    req.session.destroy((err) => {
+      if (err) {
+        console.error('�몄뀡 ��젣 �ㅻ쪟:', err);
+        return res.status(500).json({ error: '�쒕쾭 �ㅻ쪟' });
+      }
+      // �깃났 硫붿떆吏� 諛섑솚 (由щ떎�대젆�� ���� JSON �묐떟)
+      res.status(200).json({ message: '�ъ슜�� 怨꾩젙�� �깃났�곸쑝濡� ��젣�섏뿀�듬땲��.' });
+    });
+  } catch (error) {
+    console.error('�ъ슜�� ��젣 �ㅻ쪟:', error);
+    res.status(500).json({ error: '�쒕쾭 �ㅻ쪟' });
+  }
+});
+
+  module.exports = router;
\ No newline at end of file
diff --git a/routes/chatRoute.js b/routes/chatRoute.js
index 6ae03f13f5e429e9c245c289d859400f99ac799a..e7c2769ca7ec5a4cf449694190885d2f19333046 100644
--- a/routes/chatRoute.js
+++ b/routes/chatRoute.js
@@ -10,5 +10,9 @@ router.get('/unread-messages/:nickname', chatController.getUnreadMessages);
 router.get('/unread-count/:chatRoomId', chatController.getUnreadCount);
 router.post('/update-status-and-logid', chatController.updateStatusAndLogId);
 router.post('/update-read-log-id', chatController.updateReadLogId);
+router.post('/:chatRoomId/notices', chatController.addNotice); 
+router.get('/:chatRoomId/notices/latest', chatController.getLatestNotice); 
+router.get('/:chatRoomId/notices', chatController.getAllNotices);
+router.get('/:chatRoomId/notices/:noticeId', chatController.getNoticeById);
 
 module.exports = router;
diff --git a/routes/friend.js b/routes/friendRoute.js
similarity index 100%
rename from routes/friend.js
rename to routes/friendRoute.js
diff --git a/routes/inviteRoutes.js b/routes/inviteRoute.js
similarity index 100%
rename from routes/inviteRoutes.js
rename to routes/inviteRoute.js
diff --git a/routes/schedule.js b/routes/scheduleRoute.js
similarity index 100%
rename from routes/schedule.js
rename to routes/scheduleRoute.js
diff --git a/routes/sessionRoute.js b/routes/sessionRoute.js
new file mode 100644
index 0000000000000000000000000000000000000000..77a3b118a8050b3167a10494921698230699b5b6
--- /dev/null
+++ b/routes/sessionRoute.js
@@ -0,0 +1,26 @@
+const express = require('express');
+const router = express.Router();
+
+// GET /api/session/info
+router.get('/info', (req, res) => {
+  if (req.user) {
+    const { email, name } = req.user;
+  // 罹먯떛 鍮꾪솢�깊솕
+    res.set('Cache-Control', 'no-store');
+    res.set('Pragma', 'no-cache');        
+    return res.status(200).json({
+      user: {
+        email,
+        name,
+      },
+    });
+  }
+  // �몄뀡�� 留뚮즺�섏뿀嫄곕굹 �ъ슜�� �뺣낫媛� �녿뒗 寃쎌슦
+  res.set('Cache-Control', 'no-store');
+  res.set('Pragma', 'no-cache');
+  res.status(401).json({
+    message: '�몄뀡�� 留뚮즺�섏뿀嫄곕굹 �ъ슜�� �뺣낫媛� �놁뒿�덈떎.',
+  });
+});
+
+module.exports = router;
\ No newline at end of file
diff --git a/schemas/chatRooms.js b/schemas/chatRooms.js
index 323bb94c06f5487534c8b422990759dca9b752e0..beaba10e774b5036b2206fc0a4152b5fc105f9a6 100644
--- a/schemas/chatRooms.js
+++ b/schemas/chatRooms.js
@@ -1,7 +1,5 @@
-//schemas/chatRooms.js
 const mongoose = require('mongoose');
 
-// MongoDB 梨꾪똿諛� �ㅽ궎留� �섏젙 (FCM �좏겙�� 諛곗뿴濡� 愿�由�)
 const chatRoomsSchema = new mongoose.Schema({
   chatRoomId: { type: String, required: true, unique: true },
   chatRoomName: { type: String, required: true },
@@ -18,6 +16,11 @@ const chatRoomsSchema = new mongoose.Schema({
   lastReadAt: { type: Map, of: Date },
   lastReadLogId: { type: Map, of: String },
   isOnline: { type: Map, of: Boolean },
+  notices: [{ 
+    sender: { type: String },
+    message: { type: String },
+    timestamp: { type: Date, default: Date.now }, 
+  }]
 }, { collection: 'chatrooms' });
 
 const ChatRooms = mongoose.models.ChatRooms || mongoose.model('ChatRooms', chatRoomsSchema);
diff --git a/services/chatService.js b/services/chatService.js
index 0d462a5da57a7d0ce940cbafa7dbe7eee295828b..f67ee428d4efc761df9dd467600ec25eb93e4cbc 100644
--- a/services/chatService.js
+++ b/services/chatService.js
@@ -209,6 +209,87 @@ class ChatService {
     }
   }
 
+  // 怨듭��ы빆 異붽�
+  async addNotice(chatRoomId, sender, message) {
+    try {
+      const newNotice = {
+        sender,
+        message,
+        timestamp: new Date(),
+      };
+
+      const updatedChatRoom = await ChatRooms.findOneAndUpdate(
+        { chatRoomId },
+        { $push: { notices: newNotice } }, // 怨듭��ы빆 諛곗뿴�� 異붽�
+        { new: true }
+      );
+
+      if (!updatedChatRoom) {
+        throw new Error('Chat room not found');
+      }
+
+      return newNotice;
+    } catch (error) {
+      console.error('Error adding notice:', error.message);
+      throw new Error('Failed to add notice');
+    }
+  }
+
+  // 理쒖떊 怨듭��ы빆 議고쉶
+  async getLatestNotice(chatRoomId) {
+    try {
+      const chatRoom = await ChatRooms.findOne(
+        { chatRoomId },
+        { notices: { $slice: -1 } } // 理쒖떊 怨듭� 1媛쒕쭔 媛��몄삤湲�
+      );
+
+      if (!chatRoom || chatRoom.notices.length === 0) {
+        return null;
+      }
+
+      return chatRoom.notices[0];
+    } catch (error) {
+      console.error('Error fetching latest notice:', error.message);
+      throw new Error('Failed to fetch latest notice');
+    }
+  }
+
+  // 怨듭��ы빆 �꾩껜 議고쉶
+  async getAllNotices(chatRoomId) {
+    try {
+      const chatRoom = await ChatRooms.findOne({ chatRoomId }, { notices: 1 });
+
+      if (!chatRoom) {
+        throw new Error('Chat room not found');
+      }
+
+      return chatRoom.notices;
+    } catch (error) {
+      console.error('Error fetching all notices:', error.message);
+      throw new Error('Failed to fetch all notices');
+    }
+  }
+
+  // 怨듭��ы빆 �곸꽭 議고쉶
+  async getNoticeById(chatRoomId, noticeId) {
+    try {
+      const chatRoom = await ChatRooms.findOne({ chatRoomId });
+      if (!chatRoom) {
+        throw new Error('Chat room not found');
+      }
+
+      const notice = chatRoom.notices.find(notice => notice._id.toString() === noticeId);
+      if (!notice) {
+        throw new Error('Notice not found');
+      }
+
+      return notice;
+    } catch (error) {
+      console.error('Error in getNoticeById:', error.message);
+      throw error;
+    }
+  }
+
 }
 
 module.exports = new ChatService();
\ No newline at end of file
diff --git a/wsServer.js b/wsServer.js
index 16e8bf651815e0d76f1d433cd89124ca49e3d417..26b8d3e59dd7cc0bdccbad820f826055c2342777 100644
--- a/wsServer.js
+++ b/wsServer.js
@@ -5,22 +5,21 @@ const mongoose = require('mongoose');
 const admin = require('firebase-admin');
 const dotenv = require('dotenv');
 const amqp = require('amqplib'); // RabbitMQ �곌껐
-const ChatRoom = require('./schemas/chatRooms');
+const ChatRoom = require('./schemas/ChatRooms');
 
 // .env �뚯씪 濡쒕뱶
 dotenv.config();
 
-// �쒕퉬�� 怨꾩젙 �� �뚯씪 寃쎈줈瑜� �섍꼍 蹂��섏뿉�� 媛��몄삤湲�
-const serviceAccountPath = process.env.FIREBASE_CREDENTIAL_PATH;
+const HEARTBEAT_TIMEOUT = 10000; // 10珥� �� ���꾩븘��
 
-// Firebase Admin SDK 珥덇린��
-admin.initializeApp({
-  credential: admin.credential.cert(require(serviceAccountPath)),
-});
+// RabbitMQ �곌껐 �� �앹꽦
+let amqpConnection, amqpChannel;
 
 // WebSocket 愿��� �곗씠��
 let clients = [];
-let chatRooms = {};
+
+// �대씪�댁뼵�� �곹깭瑜� ���ν븯�� Map
+const clientHeartbeats = new Map();
 
 // MongoDB �곌껐 �ㅼ젙
 async function connectMongoDB() {
@@ -39,14 +38,35 @@ async function connectMongoDB() {
   }
 }
 
-// RabbitMQ 硫붿떆吏� 諛쒗뻾 �⑥닔
+// // RabbitMQ 硫붿떆吏� 諛쒗뻾 �⑥닔
+// async function publishToQueue(queue, message) {
+//   const connection = await amqp.connect(process.env.RABBITMQ_URL || 'amqp://localhost');
+//   const channel = await connection.createChannel();
+//   await channel.assertQueue(queue, { durable: true });
+//   channel.sendToQueue(queue, Buffer.from(JSON.stringify(message)));
+//   console.log(`Message sent to queue ${queue}:`, message);
+//   setTimeout(() => connection.close(), 500); // �곌껐 �リ린
+// }
+
+async function setupRabbitMQ() {
+  try {
+    amqpConnection = await amqp.connect(process.env.RABBITMQ_URL || 'amqp://localhost');
+    amqpChannel = await amqpConnection.createChannel();
+    console.log('RabbitMQ connection established');
+  } catch (err) {
+    logError('RabbitMQ Setup', err);
+    process.exit(1);
+  }
+}
+
 async function publishToQueue(queue, message) {
-  const connection = await amqp.connect(process.env.RABBITMQ_URL || 'amqp://localhost');
-  const channel = await connection.createChannel();
-  await channel.assertQueue(queue, { durable: true });
-  channel.sendToQueue(queue, Buffer.from(JSON.stringify(message)));
-  console.log(`Message sent to queue ${queue}:`, message);
-  setTimeout(() => connection.close(), 500); // �곌껐 �リ린
+  try {
+    await amqpChannel.assertQueue(queue, { durable: true });
+    amqpChannel.sendToQueue(queue, Buffer.from(JSON.stringify(message)));
+    console.log(`Message sent to queue ${queue}:`, message);
+  } catch (err) {
+    logError('RabbitMQ Publish', err);
+  }
 }
 
 // RabbitMQ瑜� �듯빐 �몄떆 �뚮┝ �붿껌�� �꾩넚�섎뒗 �⑥닔
@@ -67,237 +87,355 @@ async function getChatHistory(chatRoomId) {
   return chatRoom ? chatRoom.messages : [];
 }
 
-// WebSocket �쒕쾭 �앹꽦 諛� �몃뱶�곗씠�� 泥섎━
 function startWebSocketServer() {
-  const wsServer = http.createServer((req, res) => {
+  const server = http.createServer((req, res) => {
     res.writeHead(200, { 'Content-Type': 'text/plain' });
     res.end('WebSocket server is running');
   });
 
-  wsServer.on('upgrade', (req, socket, head) => {
-    const key = req.headers['sec-websocket-key'];
-    const acceptKey = generateAcceptValue(key);
-    const responseHeaders = [
-      'HTTP/1.1 101 Switching Protocols',
-      'Upgrade: websocket',
-      'Connection: Upgrade',
-      `Sec-WebSocket-Accept: ${acceptKey}`
-    ];
-    socket.write(responseHeaders.join('\r\n') + '\r\n\r\n');
-
-    // �대씪�댁뼵�몃� clients 諛곗뿴�� 異붽�
-    clients.push(socket);
-
-    let chatRoomId = null;
-    let nickname = null;
-
-    socket.on('data', async buffer => {
-      let message;
-      try {
-        message = parseMessage(buffer);
-        const parsedData = JSON.parse(message);
-        const { type, chatRoomId: clientChatRoomId, nickname: clientNickname, text } = parsedData;
-
-        console.log('�쒕쾭�먯꽌 �섏떊�� 硫붿떆吏�:', { type, clientChatRoomId, clientNickname, text });
-
-        if (type === 'join' || type === 'leave') {
-          await ChatRoom.updateOne(
-            { chatRoomId: clientChatRoomId },
-            { $set: { [`isOnline.${clientNickname}`]: type === 'join' } }
-          );
-
-          const statusMessage = {
-            type: 'status',
-            chatRoomId: clientChatRoomId,
-            nickname: clientNickname,
-            isOnline: type === 'join',
-          };
-
-          clients.forEach(client => {
-            client.write(constructReply(JSON.stringify(statusMessage)));
-          });
-        }
-
-        if (type === 'join') {
-          chatRoomId = clientChatRoomId;
-          nickname = clientNickname;
-
-          await ChatRoom.updateOne(
-            { chatRoomId },
-            {
-              $set: {
-                [`isOnline.${nickname}`]: true,
-                [`lastReadLogId.${nickname}`]: null,
-              },
-            }
-          );
-
-          if (!chatRooms[chatRoomId]) {
-            chatRooms[chatRoomId] = [];
-          }
-
-          const chatRoom = await ChatRoom.findOne({ chatRoomId });
-
-          // 李멸��� �뺤씤
-          const participantIndex = chatRoom.participants.findIndex(participant => participant.name === nickname);
-          if (participantIndex !== -1) {
-            const existingParticipant = chatRoom.participants[participantIndex];
-
-            // 李멸��� �곹깭 �낅뜲�댄듃
-            existingParticipant.isOnline = true;
-            existingParticipant.lastReadAt = new Date();
-
-            await chatRoom.save();
-          } else {
-            // �� 李멸��� 異붽�
-            const joinMessage = {
-              message: `${nickname}�섏씠 李멸��덉뒿�덈떎.`,
-              timestamp: new Date(),
-              type: 'join'
-            };
-
-            chatRoom.participants.push({
-              name: nickname,
-              fcmTokens: parsedData.fcmToken ? [parsedData.fcmToken] : [],
-              lastReadAt: new Date(),
-              lastReadLogId: null,
-              isOnline: true,
-            });
-
-            chatRoom.messages.push(joinMessage);
-
-            await chatRoom.save();
-
-            clients.forEach(client => {
-              client.write(constructReply(JSON.stringify(joinMessage)));
-            });
-
-            console.log(`${nickname} �� 李멸��먮줈 異붽�`);
-          }
-
-          try {
-            const previousMessages = await getChatHistory(chatRoomId);
-            if (previousMessages.length > 0) {
-              socket.write(constructReply(JSON.stringify({ type: 'previousMessages', messages: previousMessages })));
-              console.log(`�댁쟾 硫붿떆吏� �꾩넚: ${previousMessages.length}媛�`);
-            } 
-          } catch (err) {
-            console.error('�댁쟾 梨꾪똿 湲곕줉 遺덈윭�ㅺ린 以� �ㅻ쪟 諛쒖깮:', err);
-          }
-
-        } else if (type === 'message') {
-          const chatMessage = {
-            message: text,
-            timestamp: new Date(),
-            type: 'message',
-            sender: nickname
-          };
-
-
-          chatRooms[chatRoomId].push(chatMessage);
-
-          try {
-            // �덈줈�� 硫붿떆吏�瑜� messages 諛곗뿴�� 異붽�
-            const updatedChatRoom = await ChatRoom.findOneAndUpdate(
-              { chatRoomId }, 
-              { $push: { messages: chatMessage } },
-              { new: true, fields: { "messages": { $slice: -1 } } }  // 留덉�留� 異붽��� 硫붿떆吏�留� 媛��몄샂
-            );
-
-            // 留덉�留됱뿉 異붽��� 硫붿떆吏��� _id瑜� 媛��몄삤湲�
-            const savedMessage = updatedChatRoom.messages[updatedChatRoom.messages.length - 1];
-
-            // �덈줈�� 硫붿떆吏� �꾩넚: �대씪�댁뼵�몃줈 硫붿떆吏� 釉뚮줈�쒖틦�ㅽ듃
-            const messageData = {
-              type: 'message',
-              chatRoomId,
-              sender: nickname,
-              message: text,
-              timestamp: chatMessage.timestamp,
-              _id: savedMessage._id  // ���λ맂 硫붿떆吏��� _id �ъ슜
-            };
-
-            clients.forEach(client => {
-              client.write(constructReply(JSON.stringify(messageData)));
-              console.log('梨꾪똿 硫붿떆吏� �꾩넚:', messageData);
-            });
-
-            // �ㅽ봽�쇱씤 �ъ슜�먯뿉寃� FCM �몄떆 �뚮┝ �꾩넚
-            const chatRoom = await ChatRoom.findOne({ chatRoomId });
-            const offlineParticipants = chatRoom.participants.filter(participant => {
-            // isOnline �곹깭瑜� Map�먯꽌 媛��몄삤湲�
-            const isOnline = chatRoom.isOnline.get(participant.name);
-              return isOnline === false; // �뺥솗�� false�� �ъ슜�먮쭔 �꾪꽣留�
-            });
-
-            console.log("offlineParticipants", offlineParticipants);
-
-            // RabbitMQ�� �몄떆 �뚮┝ �붿껌 諛쒗뻾
-            await sendPushNotificationRequest(chatRoom.chatRoomName, clientNickname, text, offlineParticipants, chatRoomId);
-          } catch (err) {
-            console.error('MongoDB 梨꾪똿 硫붿떆吏� ���� �ㅻ쪟:', err);
-          }
-        } else if (type === 'leave') {
-          const leaveMessage = { 
-            message: `${nickname}�섏씠 �댁옣�덉뒿�덈떎.`, 
-            timestamp: new Date(),
-            type: 'leave'
-          };
-          
-          chatRooms[chatRoomId].push(leaveMessage);
-
-          await ChatRoom.updateOne(
-            { chatRoomId },
-            { $set: { [`isOnline.${nickname}`]: false } }
-          );
-
-          await ChatRoom.updateOne({ chatRoomId }, {
-            $push: { messages: leaveMessage },
-            $pull: { participants: nickname }
-          });
-
-          clients.forEach(client => {
-            client.write(constructReply(JSON.stringify(leaveMessage)));
-          });
-
-          clients = clients.filter(client => client !== socket);
-        }
-      } catch (err) {
-        console.error('硫붿떆吏� 泥섎━ 以� �ㅻ쪟 諛쒖깮:', err);
-      }
-    });
+  server.on('upgrade', (req, socket, head) => {
+    handleWebSocketUpgrade(req, socket);
+  });
 
-    socket.on('close', async () => {
-      if (nickname && chatRoomId) {
-        await ChatRoom.updateOne(
-          { chatRoomId },
-          { $set: { [`isOnline.${nickname}`]: false } }
-        );
-
-        const statusMessage = {
-          type: 'status',
-          chatRoomId,
-          nickname,
-          isOnline: false,
-        };
-
-        clients.forEach(client => {
-          client.write(constructReply(JSON.stringify(statusMessage)));
-        });
-      }
+  server.listen(8081, () => {
+    console.log('WebSocket 梨꾪똿 �쒕쾭媛� 8081 �ы듃�먯꽌 �ㅽ뻾 以묒엯�덈떎.');
+  });
+}
+
+function handleWebSocketUpgrade(req, socket) {
+  const key = req.headers['sec-websocket-key'];
+  const acceptKey = generateAcceptValue(key);
+  const responseHeaders = [
+    'HTTP/1.1 101 Switching Protocols',
+    'Upgrade: websocket',
+    'Connection: Upgrade',
+    `Sec-WebSocket-Accept: ${acceptKey}`
+  ];
+
+  socket.write(responseHeaders.join('\r\n') + '\r\n\r\n');
+
+  // �대씪�댁뼵�몃� clients 諛곗뿴�� 異붽�
+  clients.push(socket);
+
+  socket.on('data', async buffer => {
+    try {
+      message = parseMessage(buffer);
+      if (!message) return; // 硫붿떆吏�媛� 鍮꾩뼱 �덈뒗 寃쎌슦 臾댁떆
+
+      const parsedData = JSON.parse(message);
+      const { type, chatRoomId: clientChatRoomId, nickname: clientNickname, text } = parsedData;
+      await handleClientMessage(socket, parsedData);
+    } catch (err) {
+      console.error('Error processing message:', err);
+    }
+  });
+
+  socket.on('close', async () => {
+
+      console.log(`WebSocket �곌껐�� 醫낅즺�섏뿀�듬땲��: ${socket.nickname}, ${socket.chatRoomId}`);
+
+      // �대씪�댁뼵�� Heartbeat 留듭뿉�� �쒓굅
+      clientHeartbeats.delete(socket);
+
+      // �대씪�댁뼵�� 紐⑸줉�먯꽌 �쒓굅
+      clients = clients.filter((client) => client !== socket);
+
+
+      // �뚯폆 醫낅즺 ��, 李� �リ린 or hidden �뚮Ц�� �대� �⑤씪�� �곹깭 false濡� �� (以묐났 濡쒖쭅 二쇱꽍 泥섎━)
+      // if (socket.nickname && socket.chatRoomId) {
+      //   await ChatRoom.updateOne(
+      //     { chatRoomId: socket.chatRoomId },
+      //     { $set: { [`isOnline.${socket.nickname}`]: false } }
+      //   );
+      // }
+  });
+
+  socket.on('error', (err) => {
+    console.error(`WebSocket error: ${err}`);
+    clients = clients.filter((client) => client !== socket);
+  });
+
+}
+
+// 硫붿떆吏� ���� 泥섎━
+async function handleClientMessage(socket, data) {
+  const { type, chatRoomId, nickname, text, fcmToken } = data;
+
+  // ���꾩븘�껊맂 �뚯폆 李⑤떒
+  if (socket.isTimedOut) {
+    console.log(`���꾩븘�껊맂 �대씪�댁뼵�몄쓽 �ъ뿰寃곗쓣 李⑤떒: ${nickname}`);
+    return;
+  }
+
+  switch (type) {
+    case 'heartbeat':
+      // console.log(`Heartbeat received from ${nickname} in room ${chatRoomId}`);
+      clientHeartbeats.set(socket, Date.now());
+      break;
+    case 'join':
+      // WebSocket�� �ъ슜�� �뺣낫 ����
+      // socket.nickname = nickname;
+      // socket.chatRoomId = chatRoomId;
+      await handleJoin(socket, chatRoomId, nickname, fcmToken);
+      break;
+    case 'message':
+      await handleMessage(chatRoomId, nickname, text);
+      break;
+    case 'leave':
+      await handleLeave(chatRoomId, nickname);
+      break;
+    case 'notice':
+      await handleSetNotice(chatRoomId, nickname, text);
+      break;
+    default:
+      console.log(`Unknown message type: ${type}`);
+  }
+}
+
+// join - 李멸� 硫붿떆吏�
+async function handleJoin(socket, chatRoomId, nickname) {
+  if (socket.isTimedOut) {
+    console.log(`���꾩븘�껊맂 �대씪�댁뼵�몄쓽 �ъ갭�щ� 李⑤떒: ${nickname}`);
+    return;
+  }
+
+  // Set client properties
+  socket.chatRoomId = chatRoomId;
+  socket.nickname = nickname;
+
+  console.log(`Client joined room: ${chatRoomId}, nickname: ${nickname}`);
+
+  await ChatRoom.updateOne(
+    { chatRoomId: chatRoomId },
+    { $set: { [`isOnline.${nickname}`]:true } }
+  );
+
+  const statusMessage = {
+    type: 'status',
+    chatRoomId: chatRoomId,
+    nickname: nickname,
+    isOnline: true,
+  };
+
+  broadcastMessage(chatRoomId, statusMessage);
+
+  await ChatRoom.updateOne(
+    { chatRoomId },
+    { $set: {
+      [`isOnline.${nickname}`]: true,
+      [`lastReadLogId.${nickname}`]: null,
+      },
+    }
+  );
+
+  const chatRoom = await ChatRoom.findOne({ chatRoomId });
+
+  // 李멸��� �뺤씤
+  const participantIndex = chatRoom.participants.findIndex(participant => participant.name === nickname);
+  
+  if (participantIndex !== -1) {
+      const existingParticipant = chatRoom.participants[participantIndex];
+
+      // 李멸��� �곹깭 �낅뜲�댄듃
+      existingParticipant.isOnline = true;
+      existingParticipant.lastReadAt = new Date();
+
+      await chatRoom.save();
+    } else {
+      // �� 李멸��� 異붽�
+      const joinMessage = {
+        message: `${nickname}�섏씠 李멸��덉뒿�덈떎.`,
+        timestamp: new Date(),
+        type: 'join'
+      };
+
+      chatRoom.participants.push({
+        name: nickname,
+        fcmTokens: parsedData.fcmToken ? [parsedData.fcmToken] : [],
+        lastReadAt: new Date(),
+        lastReadLogId: null,
+        isOnline: true,
+      });
+
+      chatRoom.messages.push(joinMessage);
+
+      await chatRoom.save();
+
+      broadcastMessage(chatRoomId, joinMessage);
+
+      console.log(`${nickname} �� 李멸��먮줈 異붽�`);
+    }
+
+  const previousMessages = await getChatHistory(chatRoomId);
+  if (previousMessages.length > 0) {
+    socket.write(constructReply(JSON.stringify({ type: 'previousMessages', messages: previousMessages })));
+  }
+}
+
+// meessage - �쇰컲 硫붿떆吏�
+async function handleMessage(chatRoomId, nickname, text) {
+  const chatMessage = { message: text, timestamp: new Date(), type: 'message', sender: nickname };
+
+  try {
+    const updatedChatRoom = await ChatRoom.findOneAndUpdate(
+      { chatRoomId },
+      { $push: { messages: chatMessage } },
+      { new: true, fields: { messages: { $slice: -1 } } }
+    );
+
+    // 留덉�留됱뿉 異붽��� 硫붿떆吏��� _id瑜� 媛��몄삤湲�
+    const savedMessage = updatedChatRoom.messages[updatedChatRoom.messages.length - 1];
+
+    // �덈줈�� 硫붿떆吏� �꾩넚: �대씪�댁뼵�몃줈 硫붿떆吏� 釉뚮줈�쒖틦�ㅽ듃
+    const messageData = {
+      type: 'message',
+      chatRoomId,
+      sender: nickname,
+      message: text,
+      timestamp: chatMessage.timestamp,
+      _id: savedMessage._id  // ���λ맂 硫붿떆吏��� _id �ъ슜
+    };
+
+    console.log('梨꾪똿�먯꽌 Current clients:', clients.map(client => client.chatRoomId));
+
+    // broadcastMessage(chatRoomId, messageData);
+
+    clients.forEach(client => {
+      client.write(constructReply(JSON.stringify(messageData)));
+      console.log('梨꾪똿 硫붿떆吏� �꾩넚:', messageData);
     });
 
-    socket.on('error', (err) => {
-      console.error(`WebSocket error: ${err}`);
-      clients = clients.filter(client => client !== socket);
+    // �ㅽ봽�쇱씤 �ъ슜�먯뿉寃� FCM �몄떆 �뚮┝ �꾩넚
+    const chatRoom = await ChatRoom.findOne({ chatRoomId });
+    const offlineParticipants = chatRoom.participants.filter(participant => {
+      // isOnline �곹깭瑜� Map�먯꽌 媛��몄삤湲�
+      const isOnline = chatRoom.isOnline.get(participant.name);
+      return isOnline === false; // �뺥솗�� false�� �ъ슜�먮쭔 �꾪꽣留�
     });
+
+    console.log("offlineParticipants", offlineParticipants);
+
+    // RabbitMQ�� �몄떆 �뚮┝ �붿껌 諛쒗뻾
+    await sendPushNotificationRequest(chatRoom.chatRoomName, nickname, text, offlineParticipants, chatRoomId);
+    
+  } catch (err) {
+    console.error('Error saving message to MongoDB:', err);
+  }
+}
+
+// leave - �댁옣 硫붿떆吏�
+async function handleLeave(chatRoomId, nickname) {
+  await ChatRoom.updateOne(
+    { chatRoomId: clientChatRoomId },
+    { $set: { [`isOnline.${clientNickname}`]: type === 'leave' } }
+  );
+
+  const statusMessage = {
+    type: 'status',
+    chatRoomId: clientChatRoomId,
+    nickname: clientNickname,
+    isOnline: type === 'leave',
+  };
+
+  clients.forEach(client => {
+    client.write(constructReply(JSON.stringify(statusMessage)));
   });
 
-  wsServer.listen(8081, () => {
-    console.log('WebSocket 梨꾪똿 �쒕쾭媛� 8081 �ы듃�먯꽌 �ㅽ뻾 以묒엯�덈떎.');
+  const leaveMessage = { message: `${nickname} �섏씠 �댁옣�덉뒿�덈떎.`, timestamp: new Date(), type: 'leave' };
+  await ChatRoom.updateOne({ chatRoomId }, { $push: { messages: leaveMessage } });
+  broadcastMessage(chatRoomId, leaveMessage);
+}
+
+async function handleSetNotice(chatRoomId, sender, message) {
+  const notice = {
+    sender,
+    message,
+    timestamp: new Date(),
+  };
+
+  try {
+    // MongoDB�� 理쒖떊 怨듭� ����
+    await ChatRoom.updateOne(
+      { chatRoomId },
+      { $push: { notices: notice } }
+    );
+
+    // 紐⑤뱺 �대씪�댁뼵�몄뿉寃� 怨듭��ы빆 �낅뜲�댄듃 硫붿떆吏� �꾩넚
+    const noticeMessage = {
+      type: 'notice',
+      chatRoomId,
+      sender,
+      message,
+    };
+    
+    clients.forEach(client => {
+      client.write(constructReply(JSON.stringify(noticeMessage)));
+    });
+
+    // broadcastMessage(chatRoomId, noticeMessage);
+
+    console.log('怨듭��ы빆 �낅뜲�댄듃:', noticeMessage);
+  } catch (error) {
+    console.error('怨듭��ы빆 �낅뜲�댄듃 �ㅽ뙣:', error);
+  }
+}
+
+// Broadcast message to clients in the same chat room
+function broadcastMessage(chatRoomId, message) {
+  clients.forEach((client) => {
+    if (client.chatRoomId === chatRoomId) {
+      client.write(constructReply(JSON.stringify(message)));
+    }
   });
 }
 
+// 二쇨린�곸쑝濡� Heartbeat �곹깭 �뺤씤
+setInterval(async () => {
+  const now = Date.now();
+  for (const [socket, lastHeartbeat] of clientHeartbeats.entries()) {
+    if (now - lastHeartbeat > HEARTBEAT_TIMEOUT) {
+      console.log('���꾩븘�� ���� �대씪�댁뼵��:', {
+        nickname: socket.nickname,
+        chatRoomId: socket.chatRoomId,
+        lastHeartbeat: new Date(lastHeartbeat).toISOString(),
+      });
+
+      // Heartbeat 留듭뿉�� �쒓굅
+      clientHeartbeats.delete(socket);
+
+      // �곹깭 �뚮옒洹� �ㅼ젙
+      socket.isTimedOut = true;
+
+       // �뚯폆 �곌껐 醫낅즺
+      socket.end();
+
+      // �대씪�댁뼵�� 紐⑸줉�먯꽌 �쒓굅
+      clients = clients.filter((client) => client !== socket);
+
+      // �대씪�댁뼵�몃� �ㅽ봽�쇱씤�쇰줈 �ㅼ젙
+      console.log("Client timed out �� �ㅽ봽�쇱씤 �ㅼ젙");
+      await ChatRoom.updateOne(
+        { [`isOnline.${socket.nickname}`]: false },
+        { [`lastReadAt.${socket.nickname}`]: new Date() }
+      );
+
+      // �대씪�댁뼵�몄뿉寃� �곌껐 醫낅즺 硫붿떆吏� �꾩넚
+      const timeoutMessage = JSON.stringify({
+        type: 'status',
+        nickname: socket.nickname,
+        chatRoomId: socket.chatRoomId,
+        isOnline: false,
+      });
+      
+      clients.forEach(client => {
+        client.write(constructReply(timeoutMessage));
+      });
+
+      
+    }
+  }
+}, 5000); // 5珥덈쭏�� �곹깭 �뺤씤
+
 // Sec-WebSocket-Accept �ㅻ뜑 媛� �앹꽦 -> env泥섎━
 function generateAcceptValue(key) {
   return crypto.createHash('sha1').update(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', 'binary').digest('base64');
@@ -305,27 +443,37 @@ function generateAcceptValue(key) {
 
 // WebSocket 硫붿떆吏� �뚯떛 �⑥닔
 function parseMessage(buffer) {
-  const byteArray = [...buffer];
-  const secondByte = byteArray[1];
-  let length = secondByte & 127;
-  let maskStart = 2;
-
-  if (length === 126) {
-    length = (byteArray[2] << 8) + byteArray[3];
-    maskStart = 4;
-  } else if (length === 127) {
-    length = 0;
-    for (let i = 0; i < 8; i++) {
-      length = (length << 8) + byteArray[2 + i];
+  try {
+    const byteArray = [...buffer];
+    const secondByte = byteArray[1];
+    let length = secondByte & 127;
+    let maskStart = 2;
+
+    if (length === 126) {
+      length = (byteArray[2] << 8) + byteArray[3];
+      maskStart = 4;
+    } else if (length === 127) {
+      length = 0;
+      for (let i = 0; i < 8; i++) {
+        length = (length << 8) + byteArray[2 + i];
+      }
+      maskStart = 10;
     }
-    maskStart = 10;
-  }
 
-  const dataStart = maskStart + 4;
-  const mask = byteArray.slice(maskStart, dataStart);
-  const data = byteArray.slice(dataStart, dataStart + length).map((byte, i) => byte ^ mask[i % 4]);
+    const dataStart = maskStart + 4;
+    const mask = byteArray.slice(maskStart, dataStart);
+    const data = byteArray.slice(dataStart, dataStart + length).map((byte, i) => byte ^ mask[i % 4]);
+
+    const decodedMessage = new TextDecoder('utf-8').decode(Uint8Array.from(data));
+
+    // JSON �좏슚�� 寃���
+    JSON.parse(decodedMessage);
 
-  return new TextDecoder('utf-8').decode(Uint8Array.from(data));
+    return decodedMessage;
+  } catch (err) {
+    console.error('Error parsing WebSocket message:', err.message);
+    return null; // �좏슚�섏� �딆� 硫붿떆吏��� 臾댁떆
+  }
 }
 
 // �대씪�댁뼵�� 硫붿떆吏� �묐떟 �앹꽦 �⑥닔
@@ -353,5 +501,8 @@ function constructReply(message) {
   return Buffer.concat([Buffer.from(reply), messageBuffer]);
 }
 
+// �쒕쾭 �쒖옉 �� RabbitMQ �ㅼ젙
+setupRabbitMQ();
+
 // MongoDB �곌껐 �� WebSocket �쒕쾭 �쒖옉
 connectMongoDB();