[RAPID] feat: 메신저 기능 구현 (Socket.IO 실시간 채팅)

- DB: messenger_rooms/participants/messages/reactions/files 테이블 생성
- Backend: REST API 9개 엔드포인트 + Socket.IO 실시간 핸들러
- Frontend: Gmail 스타일 FAB + 모달, 채팅방 목록, 채팅 패널
- 기능: DM/그룹/채널, 파일 첨부, 이모지 리액션, 멘션, 스레드
- 알림: 토스트 on/off 토글, FAB 읽지 않은 배지

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-30 18:05:54 +09:00
parent e763249342
commit 40aeb3079a
25 changed files with 2444 additions and 3 deletions

View File

@@ -0,0 +1,76 @@
"use client";
import React, { createContext, useContext, useState, useCallback, useEffect } from "react";
interface MessengerContextValue {
isOpen: boolean;
selectedRoomId: string | null;
unreadCount: number;
notificationEnabled: boolean;
openMessenger: (roomId?: string) => void;
closeMessenger: () => void;
selectRoom: (roomId: string) => void;
setUnreadCount: (count: number) => void;
toggleNotification: () => void;
}
const MessengerContext = createContext<MessengerContextValue | undefined>(undefined);
export function MessengerProvider({ children }: { children: React.ReactNode }) {
const [isOpen, setIsOpen] = useState(false);
const [selectedRoomId, setSelectedRoomId] = useState<string | null>(null);
const [unreadCount, setUnreadCount] = useState(0);
const [notificationEnabled, setNotificationEnabled] = useState(true);
useEffect(() => {
const stored = localStorage.getItem("messenger_notification");
if (stored !== null) {
setNotificationEnabled(stored === "true");
}
}, []);
const openMessenger = useCallback((roomId?: string) => {
setIsOpen(true);
if (roomId) setSelectedRoomId(roomId);
}, []);
const closeMessenger = useCallback(() => {
setIsOpen(false);
}, []);
const selectRoom = useCallback((roomId: string) => {
setSelectedRoomId(roomId);
}, []);
const toggleNotification = useCallback(() => {
setNotificationEnabled((prev) => {
const next = !prev;
localStorage.setItem("messenger_notification", String(next));
return next;
});
}, []);
return (
<MessengerContext.Provider
value={{
isOpen,
selectedRoomId,
unreadCount,
notificationEnabled,
openMessenger,
closeMessenger,
selectRoom,
setUnreadCount,
toggleNotification,
}}
>
{children}
</MessengerContext.Provider>
);
}
export function useMessengerContext() {
const ctx = useContext(MessengerContext);
if (!ctx) throw new Error("useMessengerContext must be used within MessengerProvider");
return ctx;
}