그룹채팅 - Part6 방장의 권한(3) - 공지
ㆍProject Diary/Next.js + Prisma + MariaDB (KiloFlow)
이번 포스팅에서는 방장이 채팅방에서 공지사항을 작성하고 표시하는 기능을 구현하는 방법에 대해 설명합니다. 또한 공지사항의 접기와 "오늘 하루 보지 않기" 상태를 저장하여 상태를 관리하는 방법도 다룹니다.
데이터베이스 스키마
notice 테이블
model notices {
id Int @id @default(autoincrement())
chatroom_id Int
title String
content String
created_at DateTime @default(now())
chatroom chatrooms @relation(fields: [chatroom_id], references: [id], onDelete: Cascade)
@@index([chatroom_id])
}
- chatroom_id Int
- 공지사항이 속한 채팅방을 식별하는 ID
- 각 공지사항은 특정 채팅방과 연관되어 있어야 합니다. - chatroom chatrooms @relation(fields: [chatroom_id], references: [id], onDelete: Cascade)
- chatroom_id 필드는 chatrooms 모델의 id 필드를 참조
- onDelete: Cascade를 통해 해당 채팅방이 삭제될 때 연관된 공지사항도 함께 삭제됩니다.
프론트엔드
1. 공지사항 작성
const { id: roomId } = router.query; // 현재 채팅방의 룸 ID를 가져옴
const handleSubmit = async (event: FormEvent) => {
event.preventDefault();
const notice = { title, content, chatroom_id: roomId }; // 공지사항 데이터 생성
const res = await fetch(`/api/community/notice`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(notice), // 공지사항 데이터 JSON으로 변환하여 요청 본문에 포함
});
if (res.ok) {
window.alert("공지사항이 작성되었습니다."); // 공지사항 작성 성공 알림
router.push(`/community/chat/${roomId}`); // 작성 후 채팅방 페이지로 이동
} else {
const error = await res.json();
console.error("공지사항 작성 실패:", error); // 공지사항 작성 실패 시 에러 로그 출력
}
};
2. 최신 공지사항 가져오기
useEffect(() => {
fetchLatestNotice(); // 최신 공지사항을 가져오는 함수 호출
}, [roomId]);
const fetchLatestNotice = async () => {
const res = await fetch(`/api/community/latest-notice?roomId=${roomId}`);
const data = await res.json();
if (data) {
setLatestNotice(data); // 공지사항 데이터를 상태에 저장
} else {
setLatestNotice(null); // 공지사항이 없는 경우 상태 초기화
}
};
3. 공지사항 컴포넌트
공지사항을 표시하고 상태를 관리
const [lastCreatedAt, setLastCreatedAt] = useState(initialState.lastCreatedAt); // 가장 최신의 공지를 관리하는 상태
useEffect(() => {
if (lastCreatedAt === createdAt) {
localStorage.setItem(
storageKey,
JSON.stringify({
open: isOpen,
visible: isVisible,
lastCreatedAt: createdAt,
})
); // 최신 공지가 업데이트될 때마다 상태를 로컬 스토리지에 저장
}
}, [isOpen, isVisible, storageKey, createdAt, lastCreatedAt]);
useEffect(() => {
const savedState = localStorage.getItem(storageKey);
if (savedState) {
const { open, visible, lastCreatedAt } = JSON.parse(savedState);
if (lastCreatedAt === createdAt) {
setIsOpen(open);
setIsVisible(visible);
} else {
setIsOpen(true);
setIsVisible(true);
setLastCreatedAt(createdAt); // 최신 공지가 변경된 경우 상태 초기화
}
} else {
localStorage.setItem(
storageKey,
JSON.stringify({ open: true, visible: true, lastCreatedAt: createdAt })
);
}
}, [id, createdAt, storageKey]);
return (
<NoticeContainer ref={noticeRef} onClick={() => setIsOpen(!isOpen)}>
<div className="subject">
<TbSpeakerphone />
<h4>{title}</h4>
</div>
{isOpen && <p className="content">{content}</p>}
{isOpen && (
<div className="actions">
<button
onClick={(e) => {
e.stopPropagation();
setIsOpen(false); // 공지 닫기
}}
>
공지 닫기
</button>
<p>|</p>
<button
onClick={(e) => {
e.stopPropagation();
setIsVisible(false); // 다시 보지 않기 상태 저장
}}
>
다시 보지 않기
</button>
</div>
)}
</NoticeContainer>
);
공지사항의 상태 관리
- 'isOpen'과' isVisible'이 모두 true일 때
공지 제목과 내용이 모두 보임 - 'isOpen'만 true일 때
공지 제목만 보이고 내용은 숨겨짐 - 'isVisible'이 false일 때
제목과 내용 모두 숨겨짐 - 지사항의 상태(접기, 다시 보지 않기)를 로컬 스토리지에 저장하여, 페이지를 새로고침하거나 다시 방문해도 공지사항의 상태가 유지되도록 합니다.
백엔드
1. 공지사항 작성 API 엔드포인트
import { NextApiRequest, NextApiResponse } from "next";
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method === "POST") {
const { title, content, chatroom_id } = req.body; // 요청 본문에서 공지사항 제목, 내용, 채팅방 ID를 추출
try {
const notice = await prisma.notices.create({
data: {
title, // 공지사항 제목 설정
content, // 공지사항 내용 설정
chatroom_id: Number(chatroom_id), // 채팅방 ID 설정
},
});
res.status(201).json(notice); // 공지사항 생성 성공 시, 201 상태 코드와 함께 생성된 공지사항 반환
} catch (error) {
res.status(500).json({ error: "공지사항 작성 실패" }); // 공지사항 생성 실패 시, 500 상태 코드와 에러 메시지 반환
}
} else {
res.setHeader("Allow", ["POST"]); // 지원하지 않는 HTTP 메서드에 대해 허용된 메서드 설정
res.status(405).end(`Method ${req.method} Not Allowed`); // 지원하지 않는 HTTP 메서드 요청 시, 405 상태 코드와 에러 메시지 반환
}
}
2. 최신 공지사항 가져오기 API 엔드포인트
import { NextApiRequest, NextApiResponse } from "next";
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const { roomId } = req.query; // 쿼리 파라미터에서 채팅방 ID를 추출
if (!roomId) {
return res.status(400).json({ error: "Missing roomId parameter" }); // roomId가 없는 경우, 400 상태 코드와 에러 메시지 반환
}
if (req.method === "GET") {
try {
const notice = await prisma.notices.findFirst({
where: { chatroom_id: Number(roomId) }, // 특정 채팅방의 최신 공지사항을 찾음
orderBy: { created_at: "desc" }, // 생성일 기준 내림차순으로 정렬하여 가장 최근 공지사항을 가져옴
});
if (!notice) {
return res.status(404).json(null); // 공지사항이 없는 경우, 404 상태 코드와 null 반환
}
res.status(200).json(notice); // 공지사항이 있는 경우, 200 상태 코드와 함께 공지사항 반환
} catch (error) {
res.status(500).json({ error: "Error fetching notice" }); // 공지사항 가져오기 실패 시, 500 상태 코드와 에러 메시지 반환
}
} else {
res.setHeader("Allow", ["GET"]); // 지원하지 않는 HTTP 메서드에 대해 허용된 메서드 설정
res.status(405).end(`Method ${req.method} Not Allowed`); // 지원하지 않는 HTTP 메서드 요청 시, 405 상태 코드와 에러 메시지 반환
}
}