diff --git a/src/app/(AppBarHeader)/toks-main/_components/QuizListServerComponent.tsx b/src/app/(AppBarHeader)/toks-main/_components/QuizListServerComponent.tsx
new file mode 100644
index 000000000..e98862c18
--- /dev/null
+++ b/src/app/(AppBarHeader)/toks-main/_components/QuizListServerComponent.tsx
@@ -0,0 +1,32 @@
+import {
+ HydrationBoundary,
+ QueryClient,
+ dehydrate,
+} from '@tanstack/react-query';
+import React from 'react';
+
+import { CardList } from './CardList';
+import { getQuizList } from '../_lib/remotes/quiz';
+
+const QuizListServerComponent = async () => {
+ const queryClient = new QueryClient();
+ await queryClient.prefetchInfiniteQuery({
+ queryKey: ['posts', 'recommends'],
+ queryFn: ({ pageParams }) =>
+ getQuizList({
+ page: pageParams,
+ size: 10,
+ categoryIds: [1, 2, 3, 4, 5, 6, 7, 8, 9],
+ }),
+ initialPageParam: 0,
+ });
+ const dehydratedState = dehydrate(queryClient);
+
+ return (
+
+
+
+ );
+};
+
+export default QuizListServerComponent;
diff --git a/src/app/(AppBarHeader)/toks-main/_lib/remotes/quiz.ts b/src/app/(AppBarHeader)/toks-main/_lib/remotes/quiz.ts
new file mode 100644
index 000000000..faab01b86
--- /dev/null
+++ b/src/app/(AppBarHeader)/toks-main/_lib/remotes/quiz.ts
@@ -0,0 +1,21 @@
+type Props = { page: number; size: number; categoryIds: string[] };
+
+export const getQuizList = async ({ page, size, categoryIds }: Props) => {
+ const queryParams: Record = {};
+
+ if (categoryIds.length > 0) {
+ queryParams['categoryIds'] = categoryIds.join(',');
+ }
+
+ queryParams['page'] = String(page);
+ queryParams['size'] = String(size);
+
+ const searchParams = new URLSearchParams(queryParams).toString();
+ const res = await fetch(`api/v1/quizzes?${searchParams}`, {
+ next: {
+ tags: ['quiz-list'],
+ },
+ });
+
+ return res.json();
+};
diff --git a/src/common/utils/isEqual.ts b/src/common/utils/isEqual.ts
new file mode 100644
index 000000000..31a6a4bc0
--- /dev/null
+++ b/src/common/utils/isEqual.ts
@@ -0,0 +1,45 @@
+export const isEqual = (value: unknown, other: unknown): boolean => {
+ if (value === other) {
+ return true;
+ }
+
+ if (typeof value !== typeof other) {
+ return false;
+ }
+
+ if (Array.isArray(value) && Array.isArray(other)) {
+ if (value.length !== other.length) {
+ return false;
+ }
+ for (let i = 0; i < value.length; i++) {
+ if (!isEqual(value[i], other[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ if (
+ typeof value === 'object' &&
+ typeof other === 'object' &&
+ value !== null &&
+ other !== null
+ ) {
+ const valueObj = value as Record;
+ const otherObj = other as Record;
+ const valueKeys = Object.keys(valueObj);
+ const otherKeys = Object.keys(otherObj);
+
+ if (valueKeys.length !== otherKeys.length) {
+ return false;
+ }
+
+ return valueKeys.every(
+ (key) =>
+ Object.prototype.hasOwnProperty.call(otherObj, key) &&
+ isEqual(valueObj[key], otherObj[key])
+ );
+ }
+
+ return value === other;
+};
diff --git a/src/common/utils/reactQuery.ts b/src/common/utils/reactQuery.ts
new file mode 100644
index 000000000..0be1199d4
--- /dev/null
+++ b/src/common/utils/reactQuery.ts
@@ -0,0 +1,55 @@
+import {
+ HydrationBoundary,
+ QueryClient,
+ QueryKey,
+ QueryState,
+ dehydrate,
+} from '@tanstack/react-query';
+import { cache } from 'react';
+
+import { isEqual } from './isEqual';
+
+export const getQueryClient = cache(() => new QueryClient());
+
+type UnwrapPromise = T extends Promise ? U : T;
+
+interface QueryProps {
+ queryKey: QueryKey;
+ queryFn: () => Promise;
+}
+
+interface DehydratedQueryExtended {
+ state: QueryState;
+}
+
+export async function getDehydratedQuery({
+ queryKey,
+ queryFn,
+}: Q) {
+ const queryClient = getQueryClient();
+ await queryClient.prefetchQuery({ queryKey, queryFn });
+
+ const { queries } = dehydrate(queryClient);
+ const [dehydratedQuery] = queries.filter((query) =>
+ isEqual(query.queryKey, queryKey)
+ );
+
+ return dehydratedQuery as DehydratedQueryExtended<
+ UnwrapPromise>
+ >;
+}
+
+export async function getDehydratedQueries(queries: Q) {
+ const queryClient = getQueryClient();
+ await Promise.all(
+ queries.map(({ queryKey, queryFn }) =>
+ queryClient.prefetchQuery({ queryKey, queryFn })
+ )
+ );
+
+ return dehydrate(queryClient).queries as Array<
+ DehydratedQueryExtended>>
+ >;
+}
+
+export const Hydrate = HydrationBoundary;