๐Ÿ› ๏ธ ๋ธ”๋กœ๊ทธ ๊ฐœ๋ฐœํ•ด๋ณด๊ธฐ - 7 (Entry - part2) ๐Ÿ› ๏ธ

์˜ค์œ ์ง„ยท2023๋…„ 3์›” 27์ผ
0
post-thumbnail

Entry Footer ๊ตฌ์ƒ ๐Ÿค”

  • Entry Footer ์—์„  ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€๋“ค๊ณผ ๋Œ“๊ธ€๋“ค์„ ๋ณด์—ฌ์ค„๊ฒƒ์ด๋‹ค.

  • Comment Input ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค์–ด ๋Œ“๊ธ€์„ ์ž…๋ ฅํ•œ๋‹ค.

  • Entry page์—์„œ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” docId ๊ฐ’์œผ๋กœ ๊ธ€์— ๋งž๋Š” ๋Œ“๊ธ€๋“ค๋งŒ ๋ถˆ๋Ÿฌ์˜ฌ๊ฒƒ์ด๋‹ค.

  • ๋Œ“๊ธ€ ์ˆ˜์ •๊ณผ ๋‹ต๊ธ€ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ• ๊ฒƒ์ด๋‹ค.

Comments Database

์ต๋ช… ์ž‘์„ฑ์„ ์œ„ํ•ด ์ž‘์„ฑ์ž ์ด๋ฆ„๊ณผ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅ๋ฐ›์Œ.
๊ธ€์˜ ์•„์ด๋””์— ๋งž๋Š” ๋Œ“๊ธ€๋“ค์„ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ ์œ„ํ•ด docId ๋ฅผ ์ €์žฅ.

  • nickname(์ž‘์„ฑ์ž ์ด๋ฆ„)
  • password(์ˆ˜์ •/์‚ญ์ œ์šฉ ๋น„๋ฐ€๋ฒˆํ˜ธ)
  • comment(๋Œ“๊ธ€)
  • docId(๊ธ€ id)
  • edited(์ˆ˜์ •๋จ ํ‘œ์‹œ ์—ฌ๋ถ€)
  • avatar(๋Œ“๊ธ€ ์•„๋ฐ”ํƒ€)

์นดํ…Œ๊ณ ๋ฆฌ ๋‹ค๋ฅธ๊ธ€

//EntryFooter.tsx
const getNotes = async (category: string) => {
  try {
    const q = query(
      collection(dbService, "notes"),
      where("category", "==", category as string),
      orderBy("createdAt", "desc"),
      limit(3)
    );
    onSnapshot(q, (snapshot) => {
      const notesArr: any = snapshot.docs.map((note) => ({
        id: note.id + "",
        ...note.data(),
      }));
      setNotes(notesArr);
    });
  } catch (error: any) {}
};
  • Entry Page์—์„œ ๊ฐ€์ง€๊ณ ์žˆ๋Š” ์นดํ…Œ๊ณ ๋ฆฌ๋ฅผ ์ธ์ž๋กœ ๋ฐ›์€ ๋’ค, ํ•ด๋‹น ์นดํ…Œ๊ณ ๋ฆฌ์—๋งž๋Š” ๊ธ€์„ 3๊ฐœ๋งŒ ๋ถˆ๋Ÿฌ์˜จ๋‹ค.
  • ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ๊ธ€์„ ๋” ๋ณด๊ณ ์‹ถ๋‹ค๋ฉด notes๋กœ ์ธ๋„ํ•œ๋’ค, ์„ ํƒ๋œ ์นดํ…Œ๊ณ ๋ฆฌ๋ฅผ ๋ฐ”๋กœ ๋ณด์—ฌ์ค€๋‹ค.
  • ๋‹ค๋ฅธ ํŽ˜์ด์ง€๋ฅผ ๊ฐ€์„œ๋„ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•ด์•ผ ํ•˜๊ธฐ๋•Œ๋ฌธ์— , ์ „์—ญ์ƒํƒœ๊ด€๋ฆฌ์ธ Recoil ์ด์šฉ.
const [selectedCategory, setSelectedCategory] =
      useRecoilState<string>(selectedCategoryAtom);
const onOtherNotesClicked = () => {
  setSelectedCategory(category);
  navigation(`/notes/${encodeURIComponent(selectedCategory).toLowerCase()}`);
};

Comment Input ์ปดํฌ๋„ŒํŠธ

//CommentInput.tsx
const [nickname, setNickname] = useState<string>("");
const [password, setPassword] = useState<string>("");
const [comment, setComment] = useState<string>("");

Input ์œผ๋กœ ๋‹‰๋„ค์ž„,๋น„๋ฐ€๋ฒˆํ˜ธ,๋Œ“๊ธ€์„ ์ž…๋ ฅ๋ฐ›๋Š”๋‹ค.

//CommentInput.tsx
const onAddButtonClicked = async () => {
  if (nickname === "" || password === "" || comment === "") {
    toast({
      title: "๋นˆ์นธ์ด ์žˆ์Šต๋‹ˆ๋‹ค.",
      position: "top",
      status: "error",
      isClosable: true,
    });
    return;
  }
  if (nickname.length > 15) {
    toast({
      title: `๋‹‰๋„ค์ž„์ด ๋„ˆ๋ฌด ๊น๋‹ˆ๋‹ค..( ${nickname.length} / 15 )`,
      position: "top",
      status: "error",
      isClosable: true,
    });
    return;
  }
  if (comment.length > 500) {
    toast({
      title: `๋Œ“๊ธ€์ด ๋„ˆ๋ฌด ๊น๋‹ˆ๋‹ค..( ${comment.length} / 500 )`,
      position: "top",
      status: "error",
      isClosable: true,
    });
    return;
  }
  await addDoc(collection(dbService, "comments"), {
    docId: docId,
    avatar: userIcon.string,
    nickname: nickname,
    password: password,
    comment: comment,
    createdAt: Date.now(),
    edited: false,
  });
  toast({
    title: "๋Œ“๊ธ€์ž‘์„ฑ ์™„๋ฃŒ!",
    position: "top",
    isClosable: true,
  });
};

๋Œ“๊ธ€ ์ž‘์„ฑ ๋ฒ„ํŠผ์„ ํด๋ฆญํ–ˆ์„๋•Œ, db ์šฉ๋Ÿ‰์„ ์œ„ํ•ด ๋‹‰๋„ค์ž„๊ณผ ๋Œ“๊ธ€ ๊ธ€์ž์ˆ˜์— ์ œํ•œ์„ ๋‘”๋‹ค. ์กฐ๊ฑด์ด ๋งž์œผ๋ฉด addDoc ๋ฉ”์†Œ๋“œ๋กœ comments ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ถ”๊ฐ€ํ•ด์ค€๋‹ค.

Comments ๋ถˆ๋Ÿฌ์˜ค๊ธฐ

//Comments.tsx
export default function Comments({ docId }: ICommentsProps) {
  const getComments = async (docId: string) => {
    try {
      const q = query(
        collection(dbService, "comments"),
        where("docId", "==", docId),
        orderBy("createdAt", "desc")
      );
      onSnapshot(q, (snapshot) => {
        const commentsArr: any = snapshot.docs.map((comment) => ({
          id: comment.id + "",
          ...comment.data(),
        }));
        setComments(commentsArr);
      });
    } catch (error: any) {}
  };

  useEffect(() => {
    getComments(docId);
  }, [docId]);
  ...
    return (
    <>
      <Center w="full" h={"auto"} flexDir={"column"} gap={4} bgColor={bgColor}>
        {comments?.map((comment) => (
          <Comment
            key={comment.id}
            commentId={comment.id}
            nickname={comment.nickname}
            password={comment.password}
            avatar={comment.avatar}
            comment={comment.comment}
            createdAt={comment.createdAt}
            edited={comment.edited}
          />
        ))}
      </Center>
    </>
  );
}

docId๋ฅผ props๋กœ ๋ฐ›์•„ docId๊ฐ€ ์ผ์น˜ํ•˜๋Š” ๋Œ“๊ธ€๋“ค๋งŒ ๋ถˆ๋Ÿฌ์˜จ๋’ค , comment ์ปดํฌ๋„ŒํŠธ์— ๋ณด๋‚ด์ค€๋‹ค.

Comment ์ปดํฌ๋„ŒํŠธ

//Comment.tsx
interface ICommentProps {
  nickname: string;
  password: string;
  avatar: string;
  comment: string;
  createdAt: number;
  commentId: string;
  edited: boolean;
}

comment props๋ฅผ ์„ค์ •ํ•ด์ค€๋‹ค.
commentId๋Š” ๋‹ต๊ธ€์„ ์œ„ํ•ด ๋Œ“๊ธ€์˜ Id๋ฅผ ์ €์žฅํ•ด๋†“๋Š”๋‹ค.

edit,reply,delete ๊ธฐ๋Šฅ์„ ์œ„ํ•ด ๋ฒ„ํŠผ 3๊ฐœ๋ฅผ ๊ตฌํ˜„ํ•œ๋‹ค.

edit/reply

//Comment.tsx
const [isEdit, setIsEdit] = useState(false);
const [isReply, setIsReply] = useState(false);
  • edit๊ธฐ๋Šฅ๊ณผ reply ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•œ๋‹ค.
  • edit์€ Chakra UI์˜ popover ์ปดํฌ๋„ŒํŠธ๋กœ ๊ตฌํ˜„ํ–ˆ๋‹ค.
  • ๋ฒ„ํŠผ์„๋ˆŒ๋Ÿฌ isEdit์ด true๊ฐ€๋˜๋ฉด popover๋ฅผ ๋ณด์—ฌ์ค€๋‹ค.
  • reply๋Š” ๋ฒ„ํŠผ์„๋ˆŒ๋Ÿฌ isReply๊ฐ€ true๊ฐ€ ๋˜๋ฉด replyInput ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ณด์—ฌ์ค€๋‹ค.
  • replyInput ์ปดํฌ๋„ŒํŠธ๋Š” CommentInput ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค์–ด๋†“์•˜๊ธฐ์— ๊ฐ„๋‹จํžˆ ๊ตฌํ˜„ํ• ์ˆ˜์žˆ์—ˆ๋‹ค.

delete

  • ์‚ญ์ œ๋Š” deleteModal์„ ๋งŒ๋“ค์–ด์„œ ์ง„ํ–‰ํ•œ๋‹ค.
  • ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์ผ์น˜ํ•  ๊ฒฝ์šฐ์—๋งŒ ๋Œ“๊ธ€์„ ์‚ญ์ œํ•œ๋‹ค.
  • isReply props๋กœ ๋‹ต๊ธ€์ธ์ง€ ๋Œ“๊ธ€์ธ์ง€ ํŒŒ์•…ํ•ด์ค€๋’ค, deleteDoc ๋ฉ”์†Œ๋“œ๋กœ ๋Œ“๊ธ€์˜ id๋ฅผ ๋ฐ›์•„ ์‚ญ์ œํ•œ๋‹ค.
//CommentDeleteModal.tsx
const onDeleteClick = async () => {
  let commentsRef = null;
  if (isReply) {
    commentsRef = doc(dbService, "replyComments", commentId!);
  } else {
    commentsRef = doc(dbService, "comments", commentId!);
  }
  if (password !== checkPassword) {
    toast({
      title: "๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ํ‹€๋ฆฝ๋‹ˆ๋‹ค",
      position: "top",
      isClosable: true,
      status: "error",
    });
  } else {
    await deleteDoc(commentsRef);
    toast({
      title: "์‚ญ์ œ ์™„๋ฃŒ!",
      position: "top",
      isClosable: true,
    });
    onClose();
  }
};

Comment Reply ์ปดํฌ๋„ŒํŠธ

const getReplyComments = async (commentId: string) => {
  try {
    const q = query(
      collection(dbService, "replyComments"),
      where("commentId", "==", commentId),
      orderBy("createdAt", "asc")
    );
    onSnapshot(q, (snapshot) => {
      const commentsArr: any = snapshot.docs.map((comment) => ({
        id: comment.id + "",
        ...comment.data(),
      }));
      setReplyComments(commentsArr);
    });
  } catch (error: any) {}
};

ํ•ด๋‹น ๋Œ“๊ธ€ id์— ๋งž๋Š” ๋‹ต๊ธ€๋“ค์„ ๋ถˆ๋Ÿฌ์˜จ๋‹ค.

์™„์„ฑ

0๊ฐœ์˜ ๋Œ“๊ธ€