안녕하세요

댓글 기능구현(2): 수정, 삭제 권한 분기(유저/게스트/관리자) 본문

Next13 블로그 프로젝트

댓글 기능구현(2): 수정, 삭제 권한 분기(유저/게스트/관리자)

sakuraop 2023. 9. 24. 17:48

전체 기능 설계

목차 

댓글 기능 구현(1): https://sakuraop.tistory.com/601

1. 전체 기능 설계

2. 유저/게스트에 따라 Form 다르게 하기

3. 댓글 작성기능 구현

4. 댓글 리스트 구현

 

댓글 기능 구현(2)

5. 댓글 수정/삭제 구현 (유저/게스트/관리자에 따라 수정/삭제 권한 다르게 하기)

 


5. 댓글 수정/삭제 구현 (유저/게스트에 따라 수정 권한 다르게 하기)

구현 방법 간단 요약

  • 관리자는 모든 버튼 가능
  • 유저는 자신 댓글 버튼, 게스트 댓글 버튼 가능
  • 게스트는 게스트 댓글만 가능

 

분기는 게스트, 유저, 관리자 세가지 입니다.

수정/삭제 버튼 visible 여부

게스트: 유저 댓글에 대해서 수정/삭제 버튼 hide

유저: 자신의 댓글에 대해서만 수정/삭제 버튼 visible

관리자: 모든 댓글에 대해서 수정/삭제 버튼 visible

 

수정/삭제 권한 여부

게스트: ID와 Password 입력으로 댓글을 작성/삭제는 할 수 있으나 댓글 수정은 불가능합니다.

유저: 로그인 한 유저는 "자신의 글" 작성/수정/삭제"Password 입력으로 게스트 댓글" 삭제가 가능합니다.

관리자: 모든 작업이 Password 입력 없이 가능합니다.

 

관리자 로그인
유저 로그인
게스트

 

조건별로 의미를 알기 쉽도록 변수명을 작명합니다.

// 댓글 수정 및 삭제 권한이 있는지 여부에 따라 삭제 버튼이 나타나도록 합니다.
const isSameCommenter: boolean = isLoggedIn && userEmail === author; // 동일한 댓글 작성자
const isBlogAdmin: boolean = checkBlogAdmin(userEmail); // 블로그 관리자
const canEdit: boolean = isSameCommenter || !isLoggedIn || isBlogAdmin; // 수정 권한
const isVisibleConfirmDeletePassword =
deletingCommentId === commentId && checkingGuestPassword && !isLoggedIn; // 게스트 댓글 삭제버튼 visible 여부

 

로그인 유저자신의 댓글에 수정 버튼이 보여야 한다 -> isSameCommenter

관리자 모든 권한이 있어야 한다 -> isBlogAdmin

수정권한은 자신의 댓글(isSameCommenter) 또는 관리자 (isBlogAdmin) 또는 게스트 댓글 나타나야 한다 -> canEdit

현재 선택한 댓글 삭제 비밀번호 입력창은 관리자에게는 나타나지 않아야 한다 -> isVisibleConfirmDeletePassword

관리자는 비밀번호 입력 창 없이 바로 댓글을 삭제할 수 있어야 한다.

 

 

프로필 이미지 여부

로그인 유저라면 유저의 고유 thumbnail을 나타나도록 하고,

게스트라면 게스트 전용 thummbnail 을 나타나도록 한다.

            <div className={styles.thumbnail}>{isLoggedIn ? "✅" : "😀"}</div>

 

수정/삭제 버튼 여부

수정권한이 있다면 ( isSameCommenter || !isLoggedIn || isBlogAdmin ) 나타나도록 한다.

게스트 댓글은 수정 버튼이 나타나지 않도록 한다. ( `${!isLoggedIn && "hide"}` )

                {canEdit && (
                  <div>
                    <buttonclassName={`${styles.editButton} ${!isLoggedIn && "hide"}`}>수정</button>
                    <buttonclassName={styles.deleteButton}>삭제</button>
                  </div>
                )}

 

게스트 댓글은 비밀번호 확인을 거쳐야 합니다.

따라서 삭제 요청 api도 달라야 합니다.

                {canEdit && (
                  <div>
                    <buttonclassName={`${styles.editButton} ${!isLoggedIn && "hide"}`}>수정</button>
                    <button
                      className={styles.deleteButton}
                      onClick={
                        isSameCommenter || isBlogAdmin
                          ? () => handleClickDeleteButton(commentId)
                          : () => handleClickGuestDeleteButton(commentId)
                      }
                    >
                      삭제
                    </button>
                    <divclassName={`${styles.guestConfirm} ${isVisibleConfirmDeletePassword && "visible"}`}>비밀번호 확인</div>
                  </div>
                )}

 

자신의 댓글 또는 관리자라면( isSameCommenter || isBlogAdmin )

비밀번호 확인을 거치지 않고 삭제하는 api 를 요청합니다.

 

자신의 댓글이 아니거나 관리자가 아닌 요청의 경우엔 삭제할 수 없도록 합니다.

 

    if ( !isBlogAdmin && !isSameAuthor ) {
      return NextResponse.json({ message: "댓글 삭제: 수정 권한이 없습니다." }, { status: 400 });
    }

  // 삭제 버튼 클릭 이벤트
  const handleClickDeleteButton = async (_id: string) => {
    try {
      // DELETE 요청을 보냅니다.
      const res = await deleteCommentApi(postId, _id);

      // 삭제한 댓글을 제외한 결과를 state에 저장합니다.
      const afterDeleteComments: Comment[] = commentList.filter((comment: Comment) => String(comment._id) !== _id);
      setCommentList(afterDeleteComments);
    } catch (err) {
      console.error(err);
    }
  };
  
  
  // 로그인 댓글 삭제 API
export const DELETE = async (req: NextRequest) => {
    // 로그인 유저일 경우 블로그 관리자가 아니거나, 동일한 작성자가 아닐 경우 400 응답
  if (token) {
    const isBlogAdmin = checkBlogAdmin(token.email as string);
    const isSameAuthor = token.email === foundResult.author;
    if (!isBlogAdmin && !isSameAuthor) {
      return NextResponse.json({ message: "댓글 삭제: 수정 권한이 없습니다." }, { status: 400 });
    }
  }
};

 

게스트 댓글이라면

비밀번호 확인을 거치고 삭제하는 api 를 요청합니다.

 

비밀번호가 다른 경우에는 삭제할 수 없도록 합니다.

 

  const isValidPassword = await compare(password, foundComment.password); // 댓글 비밀번호 검사


  // 입력한 비밀번호가 다른 경우 삭제할 수 없도록
  if (isValidPassword) {
    return NextResponse.json({ message: "게스트 댓글 삭제: 비밀번호가 다릅니다." }, { status: 400 });
  } 

  // 게스트 비밀번호 입력 후 삭제확인 버튼 클릭 이벤트
  const handleClickConfirmGuestPassword = async (_id: string) => {

    try {
      // POST로 비밀번호 확인삭제 요청을 보냅니다.
      const res = await postGuestCommentDeletionApi(postId, _id, deletePassword);

      // 댓글 삭제 비밀번호가 다른 경우
      if (!res) {
        window.alert("비밀번호가 다릅니다.");
        return;
      }

      // 삭제된 댓글 리스트 렌더링
      const deletedCommentList = [...commentList].filter((comment: Comment) => String(comment._id) != _id);
      setCommentList(deletedCommentList);
    } catch (err) {
      console.error(err);
    }
  };
  
  // 게스트 댓글 삭제 API
export const POST = async (req: NextRequest) => {
  const isValidPassword = await compare(password, foundComment.password); // 댓글 비밀번호 검사

  // 입력한 비밀번호가 다른 경우
  if (isValidPassword) {
    return NextResponse.json({ message: "게스트 댓글 삭제: 비밀번호가 다릅니다." }, { status: 400 });
  } 
  //...
};