


서로다른 컴포넌트에서는 랜더링한화면은 동기화가 안되기때문에 상태를 관리해주는 훅이필요함
(+ 리엑트에서 지원하는 '훅'은 컴포넌트의 상태를 관리해주는 함수입니다.)





useEffect(() => {
if (!userEmail) return;
const socket = new SockJS("http://13.213.242.176:8081/chat/ws");
stompClient.current = new Client({
webSocketFactory: () => socket,
reconnectDelay: 5000,
onConnect: () => {
console.log("✅ WebSocket 연결 성공!");
stompClient.current?.subscribe(`/chat/sub/${userEmail}`, (response) => {
let chatMessage;
try {
chatMessage = JSON.parse(response.body);
} catch (error) {
console.warn("JSON 파싱 실패, form-data 형식으로 처리:", response.body);
chatMessage = parseFormDataMessage(response.body);
}
if (!chatMessage.sEmail || chatMessage.sEmail === "알 수 없음") return;
console.log(`📩 새 메시지 도착 - 송신자: ${chatMessage.sEmail}, 메시지: ${chatMessage.content}`);
// 📌 받은 메시지 리스트 업데이트
setUnreadList((prevList) => {
const existingSender = prevList.find((item) => item.sender === chatMessage.sEmail);
if (existingSender) {
return prevList.map((item) =>
item.sender === chatMessage.sEmail ? { ...item, count: item.count + 1 } : item
);
} else {
return [...prevList, { sender: chatMessage.sEmail, count: 1 }];
}
});
// 📌 메시지 목록 업데이트
setMessages((prevMessages) => [...prevMessages, chatMessage]);
});
},
});
stompClient.current.activate();
return () => {
stompClient.current?.deactivate();
};
}, [userEmail]);
<button onClick={() => setIsUnreadListOpen((prev) => !prev)}> 📩 받은 메시지 목록 {unreadList.length > 0 && "💡💡💡"} </button>
{isUnreadListOpen && ( <div> {unreadList.length === 0 ? ( <p>새 메시지가 없습니다.</p> ) : ( <ul> {unreadList.map((item) => ( <li key={item.sender}> <button onClick={() => handleOpenChat(item.sender)}> {item.sender}님 ({item.count}개) </button> </li> ))} </ul> )} </div> )}
const handleOpenChat = async (sender: string) => {
setLoading(true);
try {
console.log(`📥 ${sender}와의 채팅 기록 불러오기...`);
const response = await axios.get(`/chat/history?sEmail=${userEmail}&rEmail=${sender}`);
setMessages(response.data);
// ✅ 해당 발신자의 안 읽은 메시지 개수 삭제
setUnreadList((prevList) => prevList.filter((item) => item.sender !== sender));
} catch (error) {
console.error("🚨 채팅 기록 불러오기 실패:", error);
} finally {
setLoading(false);
setIsChatOpen(true);
}
};
const sendMessage = () => {
if (!stompClient.current || !message.trim()) return;
const chatMessage = {
sEmail: userEmail,
rEmail: messages.length > 0 ? messages[0].sEmail : "",
content: message,
};
stompClient.current.publish({
destination: "/chat/pub/send",
body: JSON.stringify(chatMessage),
});
setMessages((prevMessages) => [...prevMessages, chatMessage]);
setMessage("");
};
{isChatOpen && (
<div className="fixed inset-0 flex items-start justify-center bg-black bg-opacity-50 z-50">
<div className="chat-popup">
<h2 className="text-xl font-semibold text-gray-800 mb-4">채팅창</h2>
{loading ? (
<p>로딩 중...</p>
) : (
<div className="max-h-64 overflow-y-auto p-2 border rounded-lg bg-gray-100 mb-4">
{messages.map((msg, index) => (
<div
key={index}
className={`p-2 my-1 rounded-lg ${
msg.sEmail === userEmail ? "bg-blue-200 text-right" : "bg-gray-200 text-left"
}`}
>
<p className="text-sm">{msg.content}</p>
</div>
))}
</div>
)}
<div className="chat-messageinput">
<input
type="text"
placeholder="메시지를 입력하세요..."
value={message}
onChange={(e) => setMessage(e.target.value)}
/>
<button onClick={sendMessage}>전송</button>
</div>
<button onClick={() => setIsChatOpen(false)}>✖</button>
</div>
</div>
)}
z 요소를 추가해서 상단에 위치하도록하고

댓글 알림버튼구현 완료

내부적인 로직은 메시지와 동일
