Next.js와 React Query를 활용한 SSR 적용 방법
Next.js와 React Query를 활용한 SSR 적용 방법
Next.js와 React Query를 활용한 SSR 적용 방법
Next.js에서 React Query를 활용하여 서버 사이드 렌더링(SSR)을 적용하는 방법을 설명한다. 이를 통해 SEO 성능을 개선하고, 초기 로딩 속도를 향상시킬 수 있다.
1. SSR에서 React Query를 활용하는 이유
- 서버에서 데이터를 미리 가져와 클라이언트에서 Hydration하여 SEO 최적화 가능
- 초기 렌더링 속도가 빨라져 사용자 경험 개선
- Next.js의
fetch
함수와 React Query의useQuery
를 함께 활용하여 데이터 관리 최적화
2. SSR을 적용한 page.tsx
수정
아래 코드는 홈페이지에서 SSR을 적용하는 코드이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// src/app/(home)/page.tsx
import {
HydrationBoundary,
QueryClient,
dehydrate
} from "@tanstack/react-query";
import { getProducts } from "@/hooks/api/useProducts";
import Container from "@/components/Container";
import Categories from "@/components/Categories";
import Sidebar from "@/components/Sidebar";
import Products from "@/components/Products";
import FloatingButton from "@/components/FloatingButton";
import { FaPlus } from "react-icons/fa";
export default async function Home({
searchParams
}: {
searchParams: Record<string, string>;
}) {
const queryClient = new QueryClient();
// 서버에서 데이터를 미리 가져와 QueryClient에 캐싱
await queryClient.prefetchQuery({
queryKey: ["products", searchParams],
queryFn: () => getProducts(searchParams)
});
return (
<HydrationBoundary state={dehydrate(queryClient)}>
<Container>
<Categories />
<div className="flex flex-col md:flex-row w-full">
{Object.keys(searchParams).length !== 0 && <Sidebar />}
<Products searchParams={searchParams} />
</div>
<FloatingButton href="/products/upload">
<FaPlus />
</FloatingButton>
</Container>
</HydrationBoundary>
);
}
🔹 SSR을 위한 핵심 과정
getProducts
를 통해 서버에서 데이터를 미리 가져옴queryClient.prefetchQuery
로 데이터를 미리 캐싱하여 클라이언트가 동일한 데이터를 다시 요청하지 않도록 함dehydrate(queryClient)
로 데이터를 직렬화하여 클라이언트로 전송- 클라이언트에서
HydrationBoundary
를 통해 데이터를 React Query로 다시 불러옴
3. getProducts
함수 구현
서버에서 데이터를 가져오는 getProducts
함수를 정의한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// src/hooks/api/useProducts.ts
export const getProducts = async (searchParams: Record<string, string>) => {
const response = await fetch(
`${process.env.NEXT_PUBLIC_API_URL}/api/products?${new URLSearchParams(
searchParams
).toString()}`,
{
cache: "no-store" // 최신 데이터를 가져오기 위해 캐싱 방지
}
);
if (!response.ok) throw new Error("네트워크 응답이 올바르지 않습니다");
return response.json();
};
🔹 fetch
옵션 설명
cache: "no-store"
: SSR에서 최신 데이터를 가져오기 위해 캐싱을 방지함new URLSearchParams(searchParams).toString()
: URL에 쿼리스트링을 동적으로 추가하여 API 요청
4. loading.tsx
수정 또는 제거
Next.js에서 loading.tsx
가 존재하면 Suspense로 감싸지기 때문에 SSR이 정상적으로 동작하지 않을 수 있다. 이를 해결하기 위해 loading.tsx
를 다음과 같이 수정하거나 제거한다.
1
2
3
4
// src/app/(home)/loading.tsx
export default function Loading() {
return null; // 최소한의 로딩 UI 또는 제거
}
✅ 해결 방법
loading.tsx
를 제거하면 SSR이 정상적으로 동작- 혹은 Suspense를 특정 컴포넌트에만 적용하도록 변경
5. getCategories
함수 수정
서버에서 getCategories
API를 호출할 때 절대 URL을 사용해야 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// src/hooks/api/useCategories.ts
export const getCategories = async () => {
const baseUrl =
process.env.NODE_ENV === "development"
? "http://localhost:3000"
: process.env.NEXT_PUBLIC_API_URL;
const response = await fetch(`${baseUrl}/api/categories`, {
cache: "no-store"
});
if (!response.ok) {
throw new Error("Failed to fetch categories");
}
return response.json();
};
// 클라이언트에서 사용하는 훅
export const useCategories = () => {
return useQuery({
queryKey: ["categories"],
queryFn: () => axios.get("/api/categories").then((res) => res.data)
});
};
🔹 수정 이유
- 서버 컴포넌트에서 상대 경로(
/api/categories
)를 사용할 경우, SSR 시 문제가 발생 - 절대 URL(
http://localhost:3000
또는NEXT_PUBLIC_API_URL
)을 사용해야 SSR에서 정상적으로 동작
6. SSR과 React Query 적용 결과
✅ 초기 로딩 속도 개선: 데이터가 서버에서 미리 로드되므로 클라이언트에서 추가 요청 없이 바로 렌더링
✅ SEO 향상: SSR을 통해 검색 엔진이 HTML에 포함된 데이터를 바로 크롤링 가능
✅ 클라이언트에서 Hydration: HydrationBoundary
를 활용하여 React Query 캐싱을 유지하면서 동작
✅ Suspense를 활용한 로딩 최적화: SSR을 방해하지 않으면서 특정 컴포넌트에서 로딩 UI 제공
결론
Next.js에서 React Query를 활용하여 SSR을 적용하는 방법을 정리하면 다음과 같다.
- 서버에서 데이터를 미리 가져와 QueryClient에 캐싱
- 클라이언트에서
HydrationBoundary
를 통해 데이터 유지 - 절대 URL을 사용하여 서버에서도 API 요청 가능하도록 설정
- Suspense를 특정 컴포넌트에 적용하여 SSR 방해 최소화
- 캐싱을 위한
cache: "no-store"
설정으로 최신 데이터 유지
이를 통해 빠른 초기 로딩, SEO 최적화, 성능 개선을 달성할 수 있다. 🚀
This post is licensed under CC BY 4.0 by the author.