React Query와 서버 액션의 충돌 문제, API 경로로 해결한 사례 분석
2025-03-31 19:17:28React Query와 서버 액션의 충돌 문제
Next.js의 App Router 프로젝트에서 React Query를 사용하여 데이터를 가져오는 기능을 개발하던 중, 일부 쿼리가 무한 로딩 상태에 머무르는 문제를 발견했습니다. 이 문제의 원인은 서버 액션을 클라이언트에서 직접 호출할 수 없는 Next.js의 동작 방식에 있다는 것을 알아냈습니다. 이 글에서는 이러한 문제를 해결하기 위해 사용한 Next.js API Routes의 활용 방법에 대해 설명하겠습니다.
문제 분석
무한 로딩의 원인
우리는 클라이언트 컴포넌트에서 React Query의 useQuery를 사용하여 데이터를 가져오고 있었습니다.
export const useGetCommentsQuery = (interviewId: string) =>
useQuery({
queryKey: ["interviewComments", interviewId],
queryFn: () => getInterviewComments(interviewId), // ❌ 서버 액션
enabled: !!interviewId,
});
getInterviewComments 함수는 서버 액션으로, 서버에서만 실행되어 데이터베이스에 접근할 수 있습니다. 하지만 이 코드가 클라이언트 컴포넌트 내에서 실행되고 있으며, useQuery는 브라우저에서 실행되므로 서버 전용 함수를 클라이언트에서 호출하려고 했던 것입니다.
문제의 증상
- 훅이 로딩 상태에서 멈춰 있습니다.
- 데이터는 반환되지 않았습니다.
- 에러가 발생하지 않았습니다.
- 서버 액션 내 콘솔 로그가 트리거되지 않았습니다.
Next.js의 컨텍스트에서 원인 분석
Next.js에서 서버 액션은 서버(예: Server Component 나 폼 제출)에서만 호출될 수 있습니다. 브라우저에서 호출을 시도하면 실행되지 않으며, 따라서 Promise는 절대 해결되지 않습니다. 이는 React Query의 문제가 아니라 서버 전용 코드가 실행될 수 없는 곳에서 실행하려 했던 문제입니다.
API Routes로 문제 해결
API 경로 생성
문제를 해결하기 위해, Next.js API Routes를 사용하여 클라이언트가 안전하게 사용할 수 있는 서버 측 로직을 노출했습니다.
// app/api/interviews/[id]/comments/route.ts
import { NextResponse } from "next/server";
import { getInterviewComments } from "@/app/(admin)/exams/_actions/comments/get-interview-comments";
export async function GET(
req: Request,
{ params }: { params: { id: string } }
) {
try {
const comments = await getInterviewComments(params.id);
return NextResponse.json(comments);
} catch (error) {
console.error("Failed to fetch comments:", error);
return new NextResponse("Failed to fetch comments", { status: 500 });
}
}
위의 코드는 서버로부터 안전하게 데이터를 가져오는 API 경로를 정의합니다.
React Query 훅 업데이트
API 경로를 생성한 후, React Query 훅을 업데이트하여 fetch를 사용하도록 했습니다.
export const useGetCommentsQuery = (interviewId: string) =>
useQuery({
queryKey: ["interviewComments", interviewId],
queryFn: async () => {
const res = await fetch(`/api/interviews/${interviewId}/comments`);
if (!res.ok) throw new Error("Failed to fetch comments");
return res.json();
},
enabled: !!interviewId,
});
이제 올바른 아키텍처가 마련되었습니다.
성공적인 결과
useQuery훅이 제대로 해결됩니다.- 로딩 상태가 정상적으로 동작합니다.
- 서버 전용 로직은 서버에 남습니다.
- API 경로가 깔끔하고 안전한 중간 계층 역할을 합니다.
핵심 포인트
- Next.js App Router에서 서버 액션은 클라이언트에서 직접 호출될 수 없습니다.
- React Query는 브라우저에서 작동하는
queryFn을 요구합니다 — 서버 액션은 이 요구 조건을 충족하지 않습니다. - API 경로를 사용하여 클라이언트 컴포넌트가 안전하게 호출할 수 있는 서버 로직을 노출해야 합니다.
React Query를 사용하면서 로딩 상태가 계속 멈춰있다면, queryFn에서 서버 전용 함수를 직접 호출하고 있는지 확인하세요. 그렇다면 적절한 API 경로를 만들어야 합니다.
참고하면 도움이 될 만한 자료: