Rookies-2025.2.26

이주원·2025년 2월 27일

sk쉴더스 루키즈

목록 보기
22/36

오늘 미니프로젝트 끝낼생각입니다

1. 메시지전송알림

2. 게시판댓글알림

이거두개만하면될것같아요

1. 메시지전송알림은 구현도중 문제가발생했습니다

하고자했던것

1-1 메시지알림컴포넌트를만든다

1-2 헤더밑에 항상뜨도록한다

여기까지는 성공함

문제는 헤더밑에 생긴 채팅창과

마이페이지에서 생긴 채팅창간의 소통이안됨

서로다른 컴포넌트에서 렌더링한화면은 서로 동기화가 안된다는 문제가 있다네요

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

는 그냥 form-data로 전송하는데 json으로 받아서 문제였다.. 해결


누구한태온건지 리스트로 나열됬으면 좋겠음

리스트만들어서 펼쳐지도록

메시지 컴포넌트 정리

🔹 1) WebSocket 연결 및 메시지 수신 (useEffect)

  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]);
  • 특정 사용자의 메시지 수신을 구독
  • 메시지가 도착하면 JSON으로 변환 (실패하면 form-data로 파싱)
  • 새로운 메시지가 오면 unreadList (읽지 않은 메시지 리스트) 업데이트
  • 구독함수가 실행됬다는말은 메시지를 수신받았다는 의미입니다.

🔹 2) 받은 메시지 목록 버튼

  <button onClick={() => setIsUnreadListOpen((prev) => !prev)}>
    📩 받은 메시지 목록 {unreadList.length > 0 && "💡💡💡"}
  </button>
  • 버튼을 클릭하면 isUnreadListOpen 상태를 변경하여 리스트를 열거나 닫음
  • unreadList.length > 0 → 새 메시지가 있다면 알림 아이콘 (💡💡💡) 추가

🔹 3) 받은 메시지 리스트 (새 메시지 발신자 표시)

{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>
)}
  • isUnreadListOpen이 true일 때, 받은 메시지 리스트를 보여줌
  • unreadList.map((item) => ...)
  • item.sender: 메시지를 보낸 사람
  • item.count: 안 읽은 메시지 개수 특정 사용자를 클릭하면 해당 유저와의 채팅창이 열림 (handleOpenChat 실행)

🔹 4) 특정 상대방의 채팅 기록 불러오기

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);
  }
};
  • 서버에서 sEmail=userEmail, rEmail=sender로 채팅 기록 가져오기
  • 채팅을 열면 해당 유저의 알림 (unreadList) 제거
  • setIsChatOpen(true) → 채팅창을 표시

🔹 5) 메시지 전송하기

  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("");
  };
  • 메시지를 JSON.stringify(chatMessage) 형태로 WebSocket으로 전송
  • 메시지를 전송하면 화면에서도 즉시 반영
  • messages.length > 0이면, 즉 대화 중인 메시지가 하나 이상 존재한다는 의미입니다.
  • messages[0]은 현재 열려 있는 채팅에서 가장 첫 번째 메시지를 의미합니다. messages[0].sEmail은 그 메시지를 보낸 사람의 이메일입니다. 즉, 대화를 시작한 상대방의 이메일을 rEmail(받는 사람)으로 설정합니다.

🔹 6) 채팅창 UI

  {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>
  )}
  • 채팅창 UI 표시 (메시지 목록 및 입력창)
  • isChatOpen === false가 되면 닫힘
  • key는 인덱스고 해당 인덱스를이용해서 리스트를 각각 조회하기위해 사용

이제는 댓글 알림구현하면 될듯합니다

z 요소를 추가해서 상단에 위치하도록하고

댓글 알림버튼구현 완료

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

profile
뭐가될지 모름

0개의 댓글