과제 트러블슈팅: Next.js와 React Query 통합하기
오늘은 Next.js 프로젝트에 React Query를 설정하며 겪었던 트러블슈팅 과정을 정리해 본다.
React Query는 서버 상태 관리에서 강력한 라이브러리로, 데이터를 페칭하고 캐싱하며, 이를 클라이언트에 효율적으로 전달한다.
그러나 Next.js와 통합하는 과정에서 예상치 못한 오류를 만나게 되었다.
1. React Query 설정
RQProvider 설정
React Query는 QueryClientProvider를 통해 Query Client를 프로젝트의 상위에 제공해야 한다.
Next.js에서는 이를 위해 app/providers.tsx 파일을 생성하고 아래와 같이 설정했다.
"use client";
import {
isServer,
QueryClient,
QueryClientProvider,
} from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
function makeQueryClient() {
return new QueryClient({
defaultOptions: {
queries: {
staleTime: 60 * 1000, // 데이터를 60초 동안 신선한 상태로 유지
},
},
});
}
let browserQueryClient: QueryClient | undefined = undefined;
function getQueryClient() {
if (isServer) {
return makeQueryClient(); // 서버에서 새 클라이언트 생성
} else {
if (!browserQueryClient) browserQueryClient = makeQueryClient(); // 브라우저에서는 기존 클라이언트 재사용
return browserQueryClient;
}
}
export default function Providers({ children }: { children: React.ReactNode }) {
const queryClient = getQueryClient();
return (
<QueryClientProvider client={queryClient}>
<ReactQueryDevtools initialIsOpen={false} />
{children}
</QueryClientProvider>
);
}
이 코드는 React Query를 설정하고, 클라이언트와 서버에서 적절히 동작하도록 처리한다.
"use client"를 설정하여 클라이언트 컴포넌트에서 사용할 수 있게 했다.
레이아웃에 Provider 적용
Next.js는 레이아웃 파일에서 최상위 Provider를 적용할 수 있다.
이를 통해 모든 페이지에서 React Query가 동작하도록 설정했다.
import Providers from "./providers";
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}
2. React Query를 이용한 데이터 페칭
React Query를 사용해 간단한 데이터를 가져오는 코드를 작성했다.
이 예제에서는 useQuery를 사용해 API에서 데이터를 페칭하고, 이를 컴포넌트에 렌더링했다.
import { useQuery } from "@tanstack/react-query";
import { fetchData } from "./api/api";
export default function Home() {
const { data: jsondata } = useQuery({
queryKey: ["datas"],
queryFn: fetchData, // API 호출 함수
});
return (
<div className="flex flex-col items-center justify-center">
{jsondata?.map((data) => {
return (
<div key={data.id}>
<p>{data.title}</p>
<p>{data.author}</p>
</div>
);
})}
</div>
);
}
3. 트러블슈팅: TypeError 발생
문제 상황
코드를 실행하자 다음과 같은 오류가 발생했다.
TypeError: (0 , marked__WEBPACK_IMPORTED_MODULE_7__.default) is not a function
페이지가 제대로 로드되지 않았고, React Query가 예상대로 동작하지 않았다.
문제 원인
에러를 분석한 결과, useQuery와 관련된 문제는 "use client" 지시어가 누락되어 발생했다.
Next.js에서 useQuery는 클라이언트 컴포넌트에서만 작동한다.
서버 컴포넌트에서 useQuery를 사용하려고 하면 위와 같은 오류가 발생한다.
해결 방법
"use client" 지시어 추가
useQuery를 사용하는 파일의 최상단에 "use client" 지시어를 추가하여, 해당 파일을 클라이언트 컴포넌트로 설정했다.
수정된 Home 컴포넌트
"use client";
import { useQuery } from "@tanstack/react-query";
import { fetchData } from "./api/api";
export default function Home() {
const { data: jsondata } = useQuery({
queryKey: ["datas"],
queryFn: fetchData,
});
return (
<div className="flex flex-col items-center justify-center">
{jsondata?.map((data) => {
return (
<div key={data.id}>
<p>{data.title}</p>
<p>{data.author}</p>
</div>
);
})}
</div>
);
}
4. 교훈과 배운 점
- 클라이언트와 서버의 구분
- Next.js는 서버와 클라이언트 컴포넌트를 명확히 구분한다.
- React Query와 같은 클라이언트 측 상태 관리 라이브러리는 반드시 클라이언트 컴포넌트에서 사용해야 한다.
- "use client" 지시어의 중요성
- "use client" 지시어를 누락하면 서버 컴포넌트로 인식되어 클라이언트 측 훅이 정상적으로 동작하지 않는다.
- 레이아웃에서 Provider 설정
- QueryClientProvider를 layout.tsx에서 설정하면 프로젝트 전반에서 React Query를 쉽게 사용할 수 있다.
- 디버깅 과정
- TypeError 발생 시, 에러 메시지와 함께 해당 컴포넌트가 서버/클라이언트 어디에서 실행되는지 확인해야 한다.