React

[useMutation] 폴더 구조를 재구성하며 커스텀 훅으로 분리

연신내고독한늑대 2025. 1. 10. 20:00

React Query를 사용할 때, 한 페이지에서 여러 mutate를 사용하다 보면 코드가 복잡해지고 가독성이 떨어질 수 있습니다.
저 역시 page.tsx에서 useMutation을 직접 사용하던 초기 방식에서, 이를 hooks 폴더로 분리하여 관리 효율성을 높였습니다.
이번 글에서는 제가 겪었던 문제와 이를 해결하기 위한 과정을 소개합니다.

 

■ 기존 폴더 구조

처음에는 src/api 폴더에서 API 호출 로직만 분리하고, page.tsx에서 직접 useMutation을 사용했습니다.

src/
├── api/
│ ├── map.ts # 경로 관련 API
│ ├── user.ts # 사용자 관련 API
│ ├── index.ts # API 묶음 관리
├── app/
│ ├── page.tsx # 페이지 컴포넌트

 

초기 코드 (page.tsx)
import { useMutation } from '@tanstack/react-query';
import { route, shareRoute } from '@/api/map';

export default function Page() {
  const { mutate: mutateFindRoute } = useMutation<String, Error, RouteData>({
    mutateFn: route

    onSuccess: (data) => console.log('경로 찾기 성공:', data),
    onError: (error) => console.error('경로 찾기 실패:', error.message),
  });

  const { mutate: mutateShareRoute } = useMutation<String, Error, ShareData>({
    mutateFn: shareRoute,

    onSuccess: (data) => console.log('경로 공유 성공:', data),
    onError: (error) => console.error('경로 공유 실패:', error.message),
  });

  const handleFindRoute = () => {
    mutateFindRoute({ origin: '서울', destination: '부산' });
  };

  const handleShareRoute = () => {
    mutateShareRoute({ routeId: 123 });
  };

  return (
    <div>
      <button onClick={handleFindRoute}>경로 찾기</button>
      <button onClick={handleShareRoute}>경로 공유하기</button>
    </div>
  );
}

문제점

  1. 코드 길이 증가:
    • 페이지 컴포넌트 내부에 API 호출과 useMutation 옵션들이 반복적으로 작성되며 코드가 길어짐.
  2. 가독성 저하:
    • UI 렌더링, 이벤트 처리, API 호출 로직이 한 파일에 섞여 있어 코드의 목적이 명확하지 않음.

 

■ 개선된 폴더 구조

위 문제를 해결하기 위해 src/hooks 폴더를 추가하여 useMutation 관련 로직을 분리했습니다.
이제 API 호출은 여전히 api 폴더에서 관리하며, React Query 훅은 hooks 폴더에서 처리합니다.

수정된 폴더 구조

src/
├── api/
│   ├── map.ts       # 경로 관련 API
│   ├── user.ts      # 사용자 관련 API
│   ├── index.ts     # API 묶음 관리
├── hooks/
│   ├── useFindRouteMutation.ts   # 경로 찾기 훅
│   ├── useShareRouteMutation.ts  # 경로 공유 훅
├── app/
│   ├── page.tsx     # 페이지 컴포넌트
커스텀 훅으로 분리 (useFindRouteMutation.ts)

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

const useFindRouteMutation= () => {
  return useMutation<String, Error, RouteData> ({
    mutateFn: route,
    onSuccess: (data) => console.log('경로 찾기성공:', data),
    onError: (error) => console.error('경로 찾기 실패:', error.message),
  });
};

export default useFindRouteMutation;
커스텀 훅으로 분리 ( useShareRouteMutation .ts)

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

const useFindRouteMutation= () => {
  return useMutation<String, Error, ShareData> ({
    mutateFn: share,
    onSuccess: (data) => console.log('경로 공유 성공:', data),
    onError: (error) => console.error('경로 공유 실패:', error.message),
  });
};

export default useFindRouteMutation;
컴포넌트 코드 수정 (page.tsx)

import useFindRouteMutation from '@/hooks/useFindRouteMutation';
import useShareRouteMutation from '@/hooks/useShareRouteMutation';

export default function Page() {

  const findRouteMutation = useFindRouteMutation();
  const shareRouteMutation = useShareRouteMutation();

  const handleFindRoute = () => {
    findRouteMutation.mutate({ origin: '서울', destination: '부산' });
  };

  const handleShareRoute = () => {
    shareRouteMutation.mutate({ routeId: 123 });
  };

  return (
    <div>
      <button onClick={handleFindRoute}>경로 찾기</button>
      <button onClick={handleShareRoute}>경로 공유하기</button>
    </div>
  );
}

개선된 구조의 장점

1. 유지보수성 향상

  • React Query 관련 로직은 hooks 폴더에서 일괄 관리 가능.

2. 가독성 향상

  • 컴포넌트는 UI와 이벤트 처리에만 집중.

3. 관심사의 분리

  • React Query와 상태 관리: hooks 폴더에서 처리.
  • UI와 사용자 이벤트: 컴포넌트에서 집중.