구 React-Query 현 TanStack- Query 이용하여 비동기 로직 처리하기

chaaerim·2022년 12월 13일
0

한 마디로 비동기 로직을 섞어쓰지 말자.!.!.!!!.!!!!!!!!!!!

SLASH21 ‘프론트엔드 웹 서비스에서 우아하게 비동기 처리하기’에서 비동기 프로그래밍은 순서가 보장되지 않는 경우를 의미한다고 설명했다. 또한 어려운 코드의 특징 중 하나가 실패, 성공의 경우가 서로 섞여 처리되는 것이라 했다.

즉 컴포넌트에서 로딩과 에러 처리를 동시에 수행하는 경우에 성공하는 경우와 실패하는 경우가 섞여서 처리가 되고, 실패하는 경우에 대한 처리를 외부에 위임하기가 어려워진다. 그런데 내가 딱 코드를 이렇게 작성하고 있었고^^.... 이것을 수정하는 과정을 기록해보려 한다.

React Query는 Server State를 관리하는 라이브러리로 React 프로젝트에서 Server와 Client 사이 비동기 로직들을 손쉽게 다루게 해주는 도구이다.

React Query는 Server State를 다룬다고 위에서 설명했는데 여기서 Server State란 공식 홈페이지에서 아래와 같이 설명하고 있다.

  • Client에서 제어하거나 소유되지 않은 원격의 공간에서 관리되고 유지됨
  • Fetching이나 Updating에 비동기 API가 필요함
  • 다른 사람들과 공유되는 것으로 사용자가 모르는 사이에 변경될 수 있음
  • 신경 쓰지 않는다면 잠재적으로 "out of date"가 될 가능성을 지님


위와 같이 서버 데이터와 클라이언트 데이터를 완전하게 분리하여 비동기 로직을 처리하기 위해서 똑스에서는 비동기 작업을 할 때 react-query를 사용하고 있다.

const onSubmit = handleSubmit(data => {
    nicknameMutation.mutate(data.nickName);

    //닉네임 중복처리
    if ((nicknameMutation.error?.response?.data as CustomAxiosError)?.code === -20011) {
      setError(
        'nickName',
        { message: '이미 존재하는 닉네임입니다.' }, // 에러 메세지
        { shouldFocus: true }
      );
    }
  });

닉네임 생성 페이지에서 완료 버튼을 누르면 서비스 정책을 따라 닉네임이 중복된 경우 에러 메세지를 보여주어야 하는 상황이었다.

따라서 위와 같이 코드를 작성하였고 처음에 409에러가 떨어져도 (백과 닉네임 중복 에러코드를 -20011로 맞춰놓음) input 밑에 ‘이미 존재하는 닉네임입니다.’라는 메세지가 뜨지 않았다.

즉, 409에러가 두 번은 떨어져야 위와 같이 에러메세지가 나타나는 현상이 발생했다.

위와 같은 현상이 발생했던 이유는 비동기 로직을 섞어 썼기 때문 ^^…..!!!!!!!!!!

완료 버튼을 눌렀을 때 mutate을 이용해 닉네임을 보내고 있다. 근데 밑에서 또 mutate으로 에러를 체크하고 있다. 비동기 작업은 순서가 보장되어있지 않기 때문에 onSubmit을 눌렀을 때 mutate이 다 돌아가지 않았음에도 또 다른 mutate을 이용해 에러를 잡으려 하니 위와 같이 409에러가 떨어져도 바로 중복 에러 메세지를 처리하지 못한 것이다.

자 이제 이유를 알았으면 고쳐야한다……….!!!! ..!!!

1. react-query hook으로 분리하기

useSetNickname.ts

import { UseMutationOptions, useMutation } from 'react-query';

import { CustomAxiosError, SetNickname } from 'interfaces/user';
import { patchNickname } from 'pages/MyName/remote/nickName';

export const useSetNickname = (options?: UseMutationOptions<SetNickname, CustomAxiosError, SetNickname>) => {
  return useMutation<SetNickname, CustomAxiosError, SetNickname>(patchNickname, options);
};

useUserInfo.ts

import { UseQueryOptions, useQuery } from 'react-query';

import { REACT_QUERY_KEY } from 'constants/query';
import { GetUserResponse } from 'interfaces/user';
import { getUserinfo } from 'pages/MyName/remote/nickName';

export const useUserInfo = (options?: UseQueryOptions<GetUserResponse, Error>) => {
  return useQuery<GetUserResponse, Error>([REACT_QUERY_KEY.NICKNAME], getUserinfo, options);
};

추후에 서비스가 확장이 되면서 같은 쿼리문을 불러올 경우가 생길 것을 대비해 react-query를 사용하여 api를 fetching하는 코드를 위와 같이 hook으로 분리하였다. 쿼리문 같은 경우에는 코드가 변동될 가능성이 매우 낮기 때문에 이렇게 hook으로 분리해 놓으면 재사용성이 높아진다는 장점이 있다.

2. useMutation 100% 활용하기

const { mutate: nicknameMutation } = useSetNickname({
    onSuccess: () => {
      console.log('닉네임이 변경되었습니다. ');
    },
    onError: error => {
      if ((error?.response?.data as CustomAxiosError)?.code === '-20011') {
        setError('nickName', {
          message: '이미 존재하는 닉네임입니다.', // 에러 메세지
        });
      }
    },
  });

const onSubmit = handleSubmit(data => {
    nicknameMutation(data.nickName);
  });

useMutation은 react-query를 이용해서 서버에 데이터 변경 작업을 요청할 때 사용한다.

앞선 에러가 발생한 코드와는 다르게 onSubmit에서는 data를 mutate에 넘기는 역할만을 하고 있다.

이렇게 넘긴 data는 useMutation에게 패스되어 mutationFn 즉 patchNickname이라는 nickname을 patch하는 함수에 인자로 들어가게 된다.

그리고 나머지의 에러 확인과 같은 분기 처리는 useMutation에 다 위임했다.

onSuccess 같은 경우 mutation이 성공적일 때 실행되고 mutation의 성공된 결과값을 넘겨 받는다.

onError와 같은 경우 mutation이 에러를 만났을 때 실행되고 에러를 넘겨 받는다.

따라서 onError에서 error가 발생했을 때 error코드가 중복에러라면 ‘이미 존재하는 닉네임입니다’를 보여주도록 처리했다.

이렇게 처리하게 된다면 onSubmit에서는 input의 닉네임을 mutate로 보내는 역할만 나머지 비동기 처리에 대한 판단은 useMutation의 onSuccess와 onError에서 하게 된다.

비동기 처리의 순서에 대해 고민할 필요가 사라진 것이다 !!!!

react-query 좋은 줄은 알았는데 생각보다 훨씬 훨씬 쓸만한 도구인 것 같다. 앞으로도 react-query를 100% 활용해서 비동기 로직 처리를 해야겠다 🤗



참고 자료

https://techblog.woowahan.com/6339/

https://tech.osci.kr/2022/07/13/react-query/

https://tanstack.com/query/v4/docs/reference/useMutation

0개의 댓글