게시물 댓글 조회 및 삭제 기능 구현

kwon·2024년 2월 22일
0

FRONTEND STUDY

목록 보기
11/22
post-thumbnail

이제 grqphql-API 사용에 많이 익숙해진 것 같다.
처음에는 어디서부터 시작해야할 지 막막했는데 시행착오를 반복하다보니 자연스럽게 숙지가 되었다. 오늘은 게시물 댓글 조회(fetchBoardComments)와 삭제(deleteBoardComment)기능을 구현해보았다.

Container 코드(댓글 조회)

export default function BoardCommentListContainerPage() {
  const router = useRouter();
  if (!router || typeof router.query.boardId !== "string") {
    return <></>;
  }
  const { data } = useQuery<
    Pick<IQuery, "fetchBoardComments">,
    IQueryFetchBoardCommentsArgs
  >(FETCH_BOARD_COMMENTS, {
      //댓글 조회 API에서는 게시물 ID를 필요로 한다.
    variables: {
      boardId: router.query.boardId,
    },
  });
}

Container 코드(댓글 삭제)

const [deleteBoardComment] = useMutation<Pick<IMutation,"deleteBoardComment">,IMutationDeleteBoardCommentArgs>(DELETE_BOARD_COMMENT);
  const onClickDelete = async (event: MouseEvent<HTMLImageElement>) => {
    //prompt를 사용해 사용자에게 입력받을 수 있다.(그러나, 보안 위험->개선 필요)
    const password = prompt("비밀번호를 입력하세요.");
    try {
      //"만약 Image태그가 아니면 종료할게"라고 타입스크립트에게 약속
      if (!(event.target instanceof HTMLImageElement)) {
        alert("시스템 오류 발생");
        return;
      }
      await deleteBoardComment({
        variables: {
          password: password,
          boardCommentId: event.target.id,
        },
        refetchQueries: [
          {
            query: FETCH_BOARD_COMMENTS,
            variables: {
              boardId: router.query.boardId,     
            },
          },
        ],
      });
    } catch (error) {
      if (error instanceof Error) {
        alert(error.message);
      }
    }
  };

이전까지 다 학습해온 부분이기 때문에 크게 다르지 않다.

그러나, 한 가지 궁금한 것이 생겨서 테스트 해본 것이 있다.
바로 async-await 비동기 방식을 사용안하면 어떨까이다.
데이터의 양이 작아서 체감하기 힘들었다.
그래서 network탭에 들어가서 속도를 slow 3G로 변경 후 테스트 해보았다.

비동기 동작방식을 사용하지 않을 때는, 사용자에게 보여지는 페이지의 등장이 게시물 데이터가 모두 받아오고, 그 데이터들을 반영해서 한방에 보여주는 식이였다.
그렇기에, 상당히 오래 기다리는 느낌이 들었다.

비동기 동작방식을 사용했을 때는, 기존의 페이지 UI를 먼저 빠르게 사용자에게 보여준다.
그 다음, 데이터가 도착하면 데이터를 반영해서 페이지를 리렌더링한다.
어쨌든, 페이지를 보여주기는 하니까 데이터가 안떠도 기다릴 힘이 생겼다.


위 그림은 데이터가 도착하기 전, 미리 만들어 둔 페이지를 보여주는 것.


Presenter (게시물 댓글 리스트 생성)

이전에 배운 map의 특징을 생각해서, 하나의 댓글 양식을 만들어놓고
API에서 받아온 댓글 데이터 전체를 순회해서 보여주는 방식이다.

export default function BoardCommentListUIPage(props: BoardCommentListUIProps) {
  return (
    <div>
    //fetchBoardComments에서 받아온 데이터 객체를 순회한다.
      {props.data?.fetchBoardComments.map((el) => (
        <S.Wrapper>
          <S.MainWrapper>
            <S.Group>
              <S.ProfileIcon src="/images/boardComment/list/profile_icon.png" />
              <S.Group2>
                <S.ContentsGroup>
                  <S.Group3>
                    <S.Writer>{el.writer}</S.Writer>
                    <S.Group4>
  						//해당 댓글의 id를 저장(삭제를 하기 위함)
                      <S.UpdateIcon  id={el._id} src="/images/boardComment/list/update_icon.png"/>
                      <S.DeleteIcon onClick={props.onClickDelete} id={el._id} src="/images/boardComment/list/delete_icon.png"/>
                    </S.Group4>
                  </S.Group3>
                  <S.Contents>{el.contents}</S.Contents>
                </S.ContentsGroup>
                <S.CreateDate>{el.createdAt}</S.CreateDate>
              </S.Group2>
            </S.Group>
          </S.MainWrapper>
          <S.SubLine />
        </S.Wrapper>
      ))}
    </div>
  );
}

오류 발생

처음 코드를 완성했을 때, 위와 같은 오류가 발생했다.
구글링한 결과 오류를 해결하려면 모든 hooks(=use어쩌고)를 함수 구성 요소의 최상위 수준으로 이동하고 조건 내부에서 후크를 사용하지 말라고 한다. 그래서 코드를 확인해보니

  const { data } = useQuery<
    Pick<IQuery, "fetchBoardComments">,
    IQueryFetchBoardCommentsArgs
  >(FETCH_BOARD_COMMENTS, {
    variables: {
      boardId: router.query.boardId,
    },
  });

  return (
  <BoardCommentListUIPage data={data} onClickDelete={onClickDelete} />
  );
}

useQuery 후크가 ts코드 마지막 부분에 선언되어 있었다..
다음부터는, 이런 것 하나하나 생각하면서 코드를 작성해야겠다.

profile
🏃🏻 🏃🏻 🏃🏻 🏃🏻

0개의 댓글

관련 채용 정보