이제 grqphql-API 사용에 많이 익숙해진 것 같다.
처음에는 어디서부터 시작해야할 지 막막했는데 시행착오를 반복하다보니 자연스럽게 숙지가 되었다. 오늘은 게시물 댓글 조회(fetchBoardComments)와 삭제(deleteBoardComment)기능을 구현해보았다.
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,
},
});
}
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를 먼저 빠르게 사용자에게 보여준다.
그 다음, 데이터가 도착하면 데이터를 반영해서 페이지를 리렌더링한다.
어쨌든, 페이지를 보여주기는 하니까 데이터가 안떠도 기다릴 힘이 생겼다.
위 그림은 데이터가 도착하기 전, 미리 만들어 둔 페이지를 보여주는 것.
이전에 배운 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코드 마지막 부분에 선언되어 있었다..
다음부터는, 이런 것 하나하나 생각하면서 코드를 작성해야겠다.