그룹채팅 - 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에 해당하는 회원 정보를 삭제합니다.