FE/React

Next.js + React Query + Zustand 실무 패턴 정리

yhkim_ 2025. 5. 11. 00:14

 

 

Next.js로 프론트엔드 개발을 하다 보면 API 호출, 상태 관리, 전역 상태 공유 등의 과정에서 코드가 점점 복잡해지고, 프로젝트가 커질수록 유지보수가 힘들어지는 문제에 직면하게 됩니다.

 

특히 Zustand, React Query, Axios를 함께 사용하는 경우, 체계적인 구조와 규칙이 없다면 코드가 뒤죽박죽이 되기 쉽습니다.

이 글에서는 실제 실무에서 사용하는 Next.js + Zustand + React Query + Axios의 조합 패턴을 완벽하게 정리했습니다.

 

1. 폴더 구조

src/
├── apis/                → API 요청 함수
├── hooks/               → React Query + 커스텀 훅
├── stores/              → Zustand 상태관리
├── types/               → 타입 정의
├── lib/axiosInstance.ts → axios 설정

 

 

역할 담당
apis API 요청 (axios)
types 타입 정의
hooks React Query 커스텀 훅
stores Zustand 전역 상태
lib axios 설정

 


 

2. 전체 개발 플로우

2-1. types : API 모델 정의

// src/types/user.type.ts
export interface User {
  id: string;
  name: string;
  email: string;
}

export interface UserResponse {
  user: User;
}

 

 

2-2. apis : Axios 기반 API 함수

// src/apis/userApi.ts
import { apiInstance } from "@/lib/axiosInstance";
import { UserResponse } from "@/types/user.type";

export const fetchUser = async (id: string) => {
  const { data } = await apiInstance.get<UserResponse>(`/users/${id}`);
  return data.user;
};

 

 

2-3. hooks : React Query + 커스텀 훅

// src/hooks/useUserQuery.ts
import { useQuery } from "@tanstack/react-query";
import { fetchUser } from "@/apis/userApi";

export const useUserQuery = (id: string) => {
  return useQuery({
    queryKey: ["user", id],
    queryFn: () => fetchUser(id),
  });
};

 

 

2-4. stores: Zustand로 필요한 전역 상태만 관리

// src/stores/userStore.ts
import { create } from "zustand";
import { User } from "@/types/user.type";

export const useUserStore = create((set) => ({
  currentUser: null,
  setCurrentUser: (user: User) => set({ currentUser: user }),
}));

 

 

2-5. 컴포넌트 : 데이터 fetch + zustand저장 + UI 표시

// components/UserCard.tsx
"use client";
import { useUserQuery } from "@/hooks/useUserQuery";
import { useUserStore } from "@/stores/userStore";
import { useEffect } from "react";

export default function UserCard({ id }: { id: string }) {
  const { data, isLoading } = useUserQuery(id);
  const setCurrentUser = useUserStore((state) => state.setCurrentUser);

  useEffect(() => {
    if (data) setCurrentUser(data);
  }, [data, setCurrentUser]);

  if (isLoading) return <div>Loading...</div>;
  return <div>안녕하세요, {data?.name}</div>;
}

 

 

📌 요약

➡️ [Next.js Page] 

➡️[useUserQuery (React Query)] 

➡️ [fetchUser (apis → axiosInstance)] 

➡️ [백엔드 API 호출 → 응답] ->

➡️ [React Query로 반환 → 필요시 Zustand 저장] 

➡️ [컴포넌트에서 UI 표시]

 

✅ 참고