그룹채팅 - Part7 일반 참여자의 권한 - 채팅방 나가기

Project Diary/Next.js + Prisma + MariaDB (KiloFlow)

이번 글에서는 일반 참여자가 채팅방을 나가는 기능을 구현하는 방법에 대해 설명합니다. 사용자가 채팅방을 나가면, 데이터베이스에서 해당 사용자의 정보를 삭제합니다.


 

프론트엔드

 

1. 채팅방 나가기 버튼

{isOwner ? (
  <div className="admin__actions">
    <button onClick={handleSettingsClick}>
      <IoSettingsOutline />
    </button>
    <button onClick={handleNoticeClick}>
      <TbSpeakerphone />
    </button>
  </div>
) : (
  <div className="admin__actions">
    <button onClick={handleLeaveRoom}>
      <RxExit />
    </button>
  </div>
)}

일반 참여자는 채팅방 나가기 버튼이 보이며, 이 버튼을 클릭하면 handleLeaveRoom 함수가 호출됩니다.

 

2. handleLeaveRoom 함수

const handleLeaveRoom = async () => {
  if (!currentUser) {
    alert("유효하지 않은 사용자입니다."); // 사용자가 유효하지 않을 경우 경고 메시지
    return;
  }

  if (confirm("채팅방을 나가시겠습니까?")) {
    const res = await fetch(`/api/community/join`, {
      method: "DELETE",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        chatroom_id: roomId, // 채팅방 ID
        user_id: currentUser.user_id, // 현재 사용자 ID
      }),
    });

    if (res.ok) {
      fetchParticipatingUsers(); // 참여자 목록 업데이트
      socket.emit("leave_room", {
        roomId,
        user: currentUser,
      });
      router.push("/community/list"); // 채팅방 목록 페이지로 이동
    }
  }
};
  • fetch('/api/community/join', {...})
    DELETE 메서드를 사용하여 현재 사용자를 채팅방에서 제거합니다.
  • fetchParticipatingUsers()
    참여자 목록을 업데이트합니다.
  • socket.emit("leave_room", {...})
    서버에 채팅방을 떠난 사실을 알립니다.
  • router.push("/community/list")
    채팅방 목록 페이지로 이동합니다.

3. 지난 대화의 프로필 이미지와 닉네임 처리

const ChatMessage: React.FC<ChatMessageProps> = ({
  message,
  isCurrentUser,
  isSystemMessage,
  messageUser,
  formatTime,
}) => {
  const [imagePath, setImagePath] = useState<string | null>(null);

  useEffect(() => {
    if (message.image_id) {
      fetch(`/api/community/upload?id=${message.image_id}`)
        .then((res) => res.json())
        .then((data) => {
          if (data.image) {
            setImagePath(data.image.path);
          }
        })
        .catch((error) => {
          console.error("Error fetching image:", error);
        });
    }
  }, [message.image_id]);

  const profileImage = messageUser ? messageUser.profile_image : unknownUser;
  const nickname = messageUser ? messageUser.nickname : "알 수 없는 사용자";

  return (
    <MessageContainer
      isCurrentUser={isCurrentUser}
      isSystemMessage={isSystemMessage}
    >
      {!isCurrentUser && !isSystemMessage && (
        <Image
          src={profileImage}
          alt="프로필"
          className="profile__image"
          width={40}
          height={40}
        />
      )}
      <div className="message__content">
        {!isCurrentUser && !isSystemMessage && (
          <div className="nickname">
            {nickname}
          </div>
        )}
        {message.message && <div>{message.message}</div>}
        {imagePath && (
          <div className="image__content">
            <Image
              src={imagePath}
              alt="Uploaded file"
              width={100}
              height={100}
            />
          </div>
        )}
        {!isSystemMessage && (
          <div className="message__time">{formatTime(message.created_at)}</div>
        )}
      </div>
    </MessageContainer>
  );
};

export default ChatMessage;
  • 사용자가 chatroom_members에서 삭제되면
    데이터 테이블 참조(user users @relation(fields: [user_id], references: [id], onDelete: Cascade)를 통해
    사용자의 정보(프로필 이미지, 닉네임)이 모두 사라지기 때문에 처리해야 합니다.
  • const profileImage = messageUser ? messageUser.profile_image : unknownUser;
    메시지를 보낸 사용자의 프로필 이미지를 가져오고, 사용자가 존재하지 않을 경우 기본 프로필 이미지를 사용합니다.
  • const nickname = messageUser ? messageUser.nickname : "알 수 없는 사용자";
    메시지를 보낸 사용자의 닉네임을 가져오고, 사용자가 존재하지 않을 경우 "알 수 없는 사용자"로 표시합니다.

 

백엔드

 

1. 채팅방 나가기 API 엔드포인트

import { NextApiRequest, NextApiResponse } from "next";
import prisma from "../../../lib/prisma";

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  if (req.method === "DELETE") {
    const { chatroom_id, user_id } = req.body; // 요청 본문에서 채팅방 ID와 사용자 ID를 추출

    try {
      const chatroomMember = await prisma.chatroom_members.deleteMany({
        where: {
          chatroom_id: Number(chatroom_id), // 채팅방 ID 조건
          user_id: Number(user_id), // 사용자 ID 조건
        },
      });
      res.status(200).json(chatroomMember); // 삭제 성공 시, 200 상태 코드와 함께 삭제된 회원 정보 반환
    } catch (error) {
      res.status(500).json({ error: "채팅방 나가기 중 에러 발생" }); // 삭제 실패 시, 500 상태 코드와 에러 메시지 반환
    }
  } else {
    res.setHeader("Allow", ["DELETE"]); // 지원하지 않는 HTTP 메서드에 대해 허용된 메서드 설정
    res.status(405).end(`Method ${req.method} Not Allowed`); // 지원하지 않는 HTTP 메서드 요청 시, 405 상태 코드와 에러 메시지 반환
  }
}
  • await prisma.chatroom_members.deleteMany({...})
    채팅방 ID와 사용자 ID에 해당하는 회원 정보를 삭제합니다.