개발자

react query 내에서 과연 react hook을 사용해도 괜찮을까요? 에러 내용을 어떻게 뽑아내면 좋을까요?

2023년 12월 21일조회 523

안녕하세요. 이번에 로그인 시 에러가 날 때, 서버에서 보내주는 각각의 데이터값이 있는데 그것을 추출해서 활용하려고 합니다. 우선 아래 코드와 같이 useMutation (v5) 를 사용했구요. onError 에서 그 값을 추출해서 리턴하고 싶어했습니다. (error 의 타입은 Error 나 AxiosError 하면 해결되는 줄 알았는데, message 값까지 들어가면 타입 정의가 되지 않더라구요.. any를 바꿀 팁도 알려주시면 감사하겠습니다 ㅎ..) (* mutation 의 error의 return 값은 null 로 찍혔습니다..! *) 여튼, 콘솔로 값은 읽을 수 있으나 isError 값이나, error 객체도 원하는 결과값이 나오지 않고, 외부 리턴값으로 활용하는 방법이 떠오르지 않아, 일단 2가지 방법으로 추출을 시도했었습니다. - let 변수를 활용해서 상단에 선언 후에, error 시 재선언하여 값을 입력 - state를 정의하여 setState값으로 입히기 첫번째 방법은 항상 undefined로 읽혀지고, 두번째 방법은 서버 응답 속도에 따라서 값이 받아질 때가 있고, 아닌 경우는 기본 defaultState 값으로 읽혀집니다. 또한, 하나의 hook 내에서 다른 hook 을 선언해버리면 리액트의 훅 규칙을 어기는 것으로 알고 있습니다. 그러기에 위 방법은 옳지 않은 방법인 것 같아서, 옳은 에러 데이터 추출방법을 알고 싶습니다. 감사합니다 :)

1export const usePostLogin = () => {
2  const [errorMessage, setErrorMessage] = useState("");
3  let message;
4  const {
5    mutate: loginMutate,
6    isError,
7    error,
8  } = useMutation({
9    mutationFn: async (payload: LoginPostPayload) => {
10      const response = await instanceWithToken.post(`auth/sign-in`, payload);
11      return response.data;
12    },
13    onSuccess: (data) => {
14      localStorage.setItem("accessToken", data?.data?.accessToken);
15      localStorage.setItem("refreshToken", data?.data?.refreshToken);
16      console.log("로그인 성공: ", data, data?.data?.accessToken);
17      window.location.replace("/");
18    },
19    onError: (error: any) => {
20      message = error?.response?.data?.message
21      setErrorMessage(error?.response?.data?.message);
22      console.log("쌩: ", error?.response?.data?.message);
23      console.log("로그인 실패: ", errorMessage);
24    },
25  });
26
27  return { loginMutate, isError, errorMessage };
28};
이 질문이 도움이 되었나요?
'추천해요' 버튼을 누르면 좋은 질문이 더 많은 사람에게 노출될 수 있어요. '보충이 필요해요' 버튼을 누르면 질문자에게 질문 내용 보충을 요청하는 알림이 가요.

답변 1

인기 답변

김석현님의 프로필 사진

안녕하세요. 결론: useMutation이 반환하는 error 객체를 사용하셔라. (첨부 코드 참고) 공식 문서 https://tanstack.com/query/latest/docs/react/reference/useMutation 참고하시면 되는데, error 객체에 들어있는 정보는 onError에서 첫 번째 인자로 받은 error 정보와 동일한 것 같군요. 결론과는 별개로 굳이 만약에 react-query에서 error 객체에 접근할 수 있는 인터페이스를 제공하지 않았다고 한다면, state를 정의해서 사용했을 것 같습니다. 관련해서 질문자님께서 "하나의 hook 내에서 다른 hook 을 선언해버리면 리액트의 훅 규칙을 어기는 것으로 알고 있습니다."라고 말씀하셨는데, 이 부분은 다시 한번 생각해 보시면 좋을 것 같아요. usePostLogin라는 커스텀 훅 내에서 const [errorMessage, setErrorMessage] = useState("");를 정의한 게 과연 리액트의 훅 규칙을 어긴 것인가?에 대해서 말입니다.

1import { useMutation } from '@tanstack/react-query';
2import axios from 'axios';
3
4function App() {
5  const { mutate: loginMutate, error } = useMutation({
6    mutationFn: async () => {
7      const response = await axios.post('/api/test');
8      const { data } = response;
9
10      return data;
11    },
12    onSuccess: (data) => {
13      console.log(data);
14    },
15    onError: (error) => {
16      // 여기서 받은 error랑 useMutation이 내뱉는 error랑 같음
17      console.log(error);
18    },
19  });
20
21  return (
22    <div>
23      <button onClick={() => loginMutate()}>로그인</button>
24      <p>{error?.message}</p>
25    </div>
26  );
27}
28
29export default App;
김석현님의 프로필 사진

김석현

QANDA Frontend Engineer2023년 12월 21일

근데 본문 다시 보니 error 객체를 뽑아내셨는데 원하는 값이 없었다고 하셨네요?? 좀 잘 이해가 안 되는데, 그럼 어떤 값이 들어있었나요?

이상원님의 프로필 사진

이상원

작성자

Frontend Dev2023년 12월 21일

아. 제가 혼동을 드릴 수 있게 본문에 작성을 했었네요.. Mutation 에서 isError와 error를 객체로 return 하고 사용되는 페이지 컴포넌트에서 뽑아서 console을 찍어봤는데, 각각 false와 null 값을 반환을 해서 그렇게 작성한 것 같습니다. 그리고 답변해주신 커스텀 훅 내의 state 선언 부분은 다시 한번 되짚으며 알아가 보겠습니다. 친절하게 답변주셔서 감사해요. 석현님 :)

김석현님의 프로필 사진

김석현

QANDA Frontend Engineer2023년 12월 21일

맞아요! 처음에는 당연히 null로 찍히는 게 맞는데, 에러 응답을 받은 후에도 동일한가요? 테스트를 위해 1. onError에서 받은 첫 번째 인자의 error를 로그로 찍어본다. 2. usePostLogin 함수 내에서 (return 문 바로 위 정도면 좋겠네요) useMutation이 반환한 error를 로그로 찍어본다. 에러 응답을 받은 후, 두 내용이 다른지 확인해 보면 좋을 것 같습니다.

이상원님의 프로필 사진

이상원

작성자

Frontend Dev2023년 12월 21일

return 문 바로 위에서 console 과 onError 내에서 찍어내는 console 모두 error 객체를 받아냅니다. 하지만 해당 hook을 사용하는 컴포넌트에서는 받아내지를 못합니다. state 값도 error 값도 모두 null이라고 나와요. 아마 제 생각에는 사실 해당 hook 이 다른 mutation의 성공 시, 실행되는 hook이라 미리 정의된 값을 그대로 컴포넌트에서 도출해내는 것 같다는 생각이 듭니다. 컴포넌트 단에서 해당 값이 받아지는 것을 감시해서 값이 들어오면 보여주는 형식으로 구현하면 좋을 것 같아서 한번 해답을 찾아보겠습니다. 점심시간임에도 자세히 봐주셔서 감사합니다!

지금 가입하면 모든 질문의 답변을 볼 수 있어요!

현직자들의 명쾌한 답변을 얻을 수 있어요.

또는

이미 회원이신가요?

AI 답변
커리어리 AI 봇 프로필 이미지

커리어리 AI 봇

GPT가 달아주는 답변입니다.

2023년 12월 21일

`react-query`에서 `useMutation` hook의 동작 방식 측면에서, 에러 처리 및 상태 관리에 대한 질문을 주셨군요. 우선, 특별한 기술적인 이유가 없다면, 리액트 훅 안에 다른 훅을 사용하는 것은 괜찮습니다. `useMutation` 내부에서 훅 (예: `useState`)를 사용하는 것은 공식적으로 승인된 패턴이며, 이는 React Hook의 규칙을 어기지 않습니다. 그리고 onError 함수에서 불러올 수 있는 error 값은 자주 사용되어야 하므로 error를 세부적으로 파싱하려면 타입스크립트 엔티티를 결과 요청과 마찬가지로 정의하거나 AxiosError 타입을 확장하여 명시적으로 작성해야 합니다. 아래 코드는 리팩터링된 useMutation 예시입니다. ```typescript export const usePostLogin = () => { const [errorMessage, setErrorMessage] = useState(""); const mutation = useMutation( async (payload: LoginPostPayload) => { const response = await instanceWithToken.post('auth/sign-in', payload); return response.data; }, { onError: (error: any) => { // update your state to reflect the error message console.log("쌩: ", error?.response?.data?.message); console.log("로그인 실패"); setErrorMessage(error?.response?.data?.message); }, onSuccess: (data) => { localStorage.setItem("accessToken", data?.data?.accessToken); localStorage.setItem("refreshToken", data?.data?.refreshToken); console.log("로그인 성공: ", data, data?.data?.accessToken); window.location.replace("/"); // resetting error message upon successful request setErrorMessage(""); } } ); return { loginMutate: mutation.mutate, isError: mutation.isError, errorMessage }; }; ``` 이 방법으로 다시 시도해 보세요. 현재의 코드로는 에러 메세지를 추출하는데 어려움이 있어 보이는데, 이는 리액트 상태 업데이트가 비동기적일 수 있기 때문입니다. 즉 `setErrorMessage`가 호출된 직후에 `errorMessage` 값을 읽으면 아직 업데이트되지 않은 값이 출력될 수 있습니다. 서버에서 오류 메세지를 동기적으로 반환하려면 onError()에서 서버 응답을 분석하고 해당 메세지를 상태 변수에 설정해야 합니다. 그런 다음 해당 상태 변수를 컴포넌트에서 사용하여 필요한 곳에 표시합니다. 참고로 `useMutation()` hooks 내부 로직 중 일부는 잘 이해하지 못 한 부분이 있는 것 같으니, 추가로 아래 번역자 노트 참고 바랍니다. [번역자 노트] - useMutation() 는 mutation 객체를 반환하는데, 그 안에는 위 에러처리를 포함한 여러가지 상태값이 있습니다. - onError 핸들러에서 실패한 mutation 당시의 변수도 받을 수 있습니다. 이 변수를 사용하면 onFailure 시 복잡한 로직 처리가 가능합니다. - onSuccess, onError 콜백에서는 기본적으로 아래 3가지 파라미터를 받게 됩니다: data 또는 error, variables(실패/성공한 mutation의 변수), context(옵션, onMutate에서 반환된 값).

목록으로

지금 가입하면 모든 질문의 답변을 볼 수 있어요!