useMutation 훅으로 분리하기

3
post-thumbnail

참고로 FSD architecture로 파일 분리가 되어있음.

🎀 훅으로 분리하기 전에 준비

📌 dto 파일 생성해서 타입 모으기

src/shared/api/authentication-api/dto.ts

// login api 파라미터 타입
export interface PostLoginParam {
  email: string;
  password: string;
}

// 성공 응답에 response 타입
export interface LoginSuccessResponse {
  data: {
    accessToken: string;
    refreshToken: string;
  };
}

📌 axios api 파일 생성

src/shared/api/authentication-api/post-login.ts

import { axiosInstance } from '@shared/api/instance';
import { REQUEST_URLS } from '@shared/constants/api';

import { LoginSuccessResponse } from './dto';

const postLogIn = async (email: string, password: string) => {
  const response = await axiosInstance.post<LoginSuccessResponse>(REQUEST_URLS.postSignIn, {
    email,
    password,
  });
  return response.data;
};

export { postLogIn };

login api를 axios를 이용해서 post해주는 코드를 만들었다.

🎀 useMutation hook 생성

src/features/authentication/hooks/use-post-login-mutation.tsx

import { useMutation } from '@tanstack/react-query';

import { LoginSuccessResponse, PostLoginParam, postLogIn } from '@shared/api/authentication-api';

export const usePostLoginMutation = () => {
  return useMutation<LoginSuccessResponse, Error, PostLoginParam>({
    mutationFn: ({ email, password }) => postLogIn(email, password),
  });
};

useMutation에 대한 타입 지정이 중요하다.
처음에 타입 지정해주지 않았더니, 파라미터로 넘겨줄 값들을 일일이 지정해주어야 했다.

declare function useMutation<TData = unknown, TError = DefaultError, TVariables = void, ... > ...;

useMutation에 대한 타입은 위와 같다고 한다.
TData TError TVariables 3개 까지만 타입 지정을 해주었다.

TData : 반환되는 값에 대한 타입
TError : 에러
TVariables : 파라미터 타입

🎀 생성한 hook 사용법

src/features/authentication/ui/login-form.tsx

const { mutate: postLoginMutate } = usePostLoginMutation();

hook으로 생성해둔 usePostLoginMutation에서 postLoginMutate(mutate)를 빼온다.

const handleSubmitLogin: SubmitHandler<PostLoginParam> = async (data) => {
    postLoginMutate({ email: data.email, password: data.password });
};

로그인 버튼을 눌렀을 때 handleSubmitLogin 함수가 실행되게 만들고,
postLoginMutate 에 email과 password 인자 값을 넘겨준다!!

그럼 로그인 잘 된다!!

🎀 성공 / 에러 처리

이렇게만 끝내면 갱.장.히. 아쉽다.
로그인은 성공할 수도 있고 실패할 수도 있다.
해당 부분에 대한 처리는 useMutation에서도 해줄 수 있고, mutate에서도 해줄 수 있다.

그런데 아래 주의사항을 꼭 읽어 보자!

useMutation()에 등록된 콜백 함수들은 컴포넌트가 언마운트되더라도 실행이 되지만, mutate()의 콜백 함수들은 만약 뮤테이션이 끝나기 전에 해당 컴포넌트가 언마운트되면 실행되지 않는 특징을 가지고 있다고 한다.

그래서 다른 페이지로 리다이렉트한다든가, 혹은 결과를 토스트로 띄워주는 것과 같은 로직은 mutate()를 통해 등록해 주어야 한다. 출처

그래서 나는 mutate에서 작업해주었다.

src/features/authentication/ui/login-form.tsx


const handleSubmitLogin: SubmitHandler<PostLoginParam> = async (value) => {
  postLoginMutate(
    { email: value.email, password: value.password },
    {
      onSuccess: () => { // 성공했을 때
        return toast.success('로그인 성공!');
      },
      onError: (error) => {
        // 실패했을 때
        if (error instanceof AxiosError) {
          // axios에서 보낸 에러가 맞다면 true 값 반환
          if (error.response?.status === 400) {
              // error.response?.status 에 number 체크해서 아래 값 반환
              return toast.error('이메일과 비밀번호를 다시 확인해 주세요.');
        }
      }
      return toast.error('로그인 실패.');
	  },
    },
  );
};

🎀 마치며...

  • tanstack query보다 타입스크립트가 더 어렵다
  • 타입 오류 때문에 많이 허덕였다,,, 팀원 H님의 설명을 들으며 반성 하게 되었다. 강의 하나 더 구독해볼까,,,

profile
일단 해. 그리고 잘 되면 잘 된 거, 잘 못되면 그냥 해본 거!

4개의 댓글

comment-user-thumbnail
6일 전

안녕하세요 글을 읽어보니까
굳이
postLoginMutate(
{ email: value.email, password: value.password },
{
onSuccess: () => { // 성공했을 때
return toast.success('로그인 성공!');
},
onError: (error) => {
// 실패했을 때
if (error instanceof AxiosError) {
// axios에서 보낸 에러가 맞다면 true 값 반환
if (error.response?.status === 400) {
// error.response?.status 에 number 체크해서 아래 값 반환
return toast.error('이메일과 비밀번호를 다시 확인해 주세요.');
}
}
return toast.error('로그인 실패.');
},
},

이렇게 Submit함수에 작성할 필요는없을거같다는 의견입니다.
뮤테이트를 훅으로 따로 빼셨으면 그 훅 안에서 onSuccess와 onError에 따라서 toast를 보여줘도
괜찮지않나싶어서요! 그 편이 Submit함수가 더 깔끔해지지 않나.. 싶은 개인적인 의견입니다.

만약 뮤테이션이 성공하고 페이지 이동이 이루어져야하는데 말씀하신 컴포넌트 언마운트시 mutat가 사라져서 페이지 이동이 이루어지지 않으면 어떡하지? 라는 걱정이있다면

뮤테이션 훅의 onSuccess에서 return {success:true}같은걸로 리턴시켜주고
Submit함수에서 뮤테이션으로부터 리턴을받아서
if(result.success) router.push('/') 해줘도 괜찮지 않나싶습니다.

if문에 걸릴때라면 이미 뮤테이션 성공콜백함수가 동작한 뒤니까 토스트도 띄워질테니깐요!

혹시 이에대해서 의견이있으시다면 편하게 대댓달아주신다면 좋을거같습니다!

1개의 답글