
프로젝트 리팩토링 중 게시판의 댓글을 작성하는 부분에서 버그를 발견했다. 버그는 다음과 같았다.
댓글을 작성하고 등록 버튼을 클릭하면 등록 성공과 함께 입력란이 공란으로 비워져야 하는데 비워지지 않는다. 분명히 react hook form 에 'comment'로 등록된 methods.reset() 을 호출했는데도 정상적으로 입력란 초기화 동작이 수행되지 않은 이유는 왜일까?
// 댓글 작성
const handleCommentSubmit: SubmitHandler<INewCommentValues> = ({ comment }) => {
createComment(comment);
if (isSuccess) {
methods.reset();
}
};
사실, 이유는 간단했다. 비동기 처리와 관련한 문제였다. mutate 동작을 수행하는createComment() 는 useMutation 동작을 수행하는 useCreateComment 라는 함수에서 반환하는 값이다. useMutation 내부적으로는 서버에 댓글 등록 요청을 보내는 비동기 작업이 진행된다. 따라서 동기적으로 코드를 수행하는 mutate 함수를 사용하는 것은 잘못된 방법이었다. 동기적으로 진행된 코드는 아직 false 값인 isSuccess 를 만나고는 입력란 초기화 작업을 수행하는 코드를 수행하지 못하였다. 따라서, 비동기 결과 처리를 위해서는 mutate 가 아닌 mutateAsync 를 사용하여 비동기적인 처리를 해주어야 한다.
공식문서에도 나와있듯이, mutateAsync 는 void를 반환하는 mutate 와 다르게 Promise 객체를 반환한다. 따라서 async & await 문법을 통해서 반환값을 받은 후, 상태에 따른 조건 분기를 해준다.
const { mutateAsync: createComment, ... } = useCreatePhotoAlbumComment();
// 댓글 작성
const handleCommentSubmit: SubmitHandler<INewCommentValues> = async ({ comment }) => {
const res = await createComment(comment);
// 분기 처리 예시
if (res.status === 200) {
methods.reset(); // form 리셋
}
};
사실, 이후에 알았지만 리액트 쿼리는 위와 같은 비동기 작업의 결과를 콜백함수인 onSuccess, onError, onSettled 를 사용해서 처리할 수 있었다. 즉, mutateAsync 없이 mutate 만으로도 비동기 처리가 가능했다.
useMutation을 선언한 useCreateComment 훅에서 onSuccess 에 대한 처리가 되어있다. 하지만, 해당 함수는 react hook form의 useForm 을 사용하는 컴포넌트와 다른 파일에 있었다. 레이어를 나누기 위해 react hook form의 reset 을 어떻게 넘겨줄지 고민했다. 이때, mutate() 의 두번째 인자로 결과 처리에 대한 콜백을 넘길 수 있었고, 핸들러 함수 내에서 폼 초기화 작업을 진행했다.
// 댓글 작성
const handleCommentSubmit: SubmitHandler<INewCommentValues> = ({ comment }) => {
createComment(comment, {
onSuccess: () => {
methods.reset(); // form 리셋
},
onError: () => {
console.log('댓글 작성 실패');
},
});
};