diff --git a/package-lock.json b/package-lock.json
index f1b10a9..cc3fb11 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -23,6 +23,7 @@
"@radix-ui/react-switch": "^1.2.5",
"@radix-ui/react-tabs": "^1.1.12",
"@radix-ui/react-tooltip": "^1.2.7",
+ "@recharts/devtools": "^0.0.7",
"@supabase/auth-helpers-nextjs": "^0.10.0",
"@supabase/ssr": "^0.6.1",
"@supabase/supabase-js": "^2.50.0",
@@ -2115,6 +2116,17 @@
"integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==",
"license": "MIT"
},
+ "node_modules/@recharts/devtools": {
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/@recharts/devtools/-/devtools-0.0.7.tgz",
+ "integrity": "sha512-ud66rUf3FYf1yQLGSCowI50EQyC/rcZblvDgNvfUIVaEXyQtr5K2DFgwegziqbVclsVBQLTxyntVViJN5H4oWQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0",
+ "recharts": "^3.3.0"
+ }
+ },
"node_modules/@reduxjs/toolkit": {
"version": "2.8.2",
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.8.2.tgz",
diff --git a/package.json b/package.json
index 4a683c1..abdbbd7 100644
--- a/package.json
+++ b/package.json
@@ -25,6 +25,7 @@
"@radix-ui/react-switch": "^1.2.5",
"@radix-ui/react-tabs": "^1.1.12",
"@radix-ui/react-tooltip": "^1.2.7",
+ "@recharts/devtools": "^0.0.7",
"@supabase/auth-helpers-nextjs": "^0.10.0",
"@supabase/ssr": "^0.6.1",
"@supabase/supabase-js": "^2.50.0",
diff --git a/src/app/api/v2/quiz/history/route.ts b/src/app/api/v2/quiz/history/route.ts
index fe5f9a5..572eb91 100644
--- a/src/app/api/v2/quiz/history/route.ts
+++ b/src/app/api/v2/quiz/history/route.ts
@@ -22,7 +22,8 @@ export async function GET(request: NextRequest) {
try {
const supabase = await createClient();
const { searchParams } = new URL(request.url);
- const limit = parseInt(searchParams.get('limit') || '3');
+ const limitParam = searchParams.get('limit');
+ const limit = limitParam ? parseInt(limitParam) : null;
const {
data: { user },
error: userError,
@@ -35,7 +36,7 @@ export async function GET(request: NextRequest) {
);
}
- const { data: quizHistoryData, error: quizHistoryError } = await supabase
+ let query = supabase
.from('user_quiz_sessions')
.select(
`
@@ -50,8 +51,13 @@ export async function GET(request: NextRequest) {
)
`
)
- .eq('user_id', user.id)
- .limit(limit);
+ .eq('user_id', user.id);
+
+ if (limit) {
+ query = query.limit(limit);
+ }
+
+ const { data: quizHistoryData, error: quizHistoryError } = await query;
if (quizHistoryError) {
throw quizHistoryError;
diff --git a/src/features/mypage/ClientMyPage.tsx b/src/features/mypage/ClientMyPage.tsx
index a8f0cfd..8582043 100644
--- a/src/features/mypage/ClientMyPage.tsx
+++ b/src/features/mypage/ClientMyPage.tsx
@@ -1,28 +1,51 @@
'use client';
+import { useQuery } from '@tanstack/react-query';
import { toast } from 'sonner';
import { useUser } from '@/hooks/useUser';
+
import RequireLogin from '@/components/RequireLogin';
import Bookmark from './Bookmark';
import LearningProgress from './LearningProgress';
import QuizHistory from './QuizHistory';
-// import { Button } from '@/components/ui/button';
-import {
- Card,
- CardContent,
- // CardDescription,
- // CardHeader,
- // CardTitle,
-} from '@/components/ui/card';
-import {
- // Settings,
- Trophy,
- BookOpen,
- TrendingUp,
- Star,
-} from 'lucide-react';
+import { QuizType } from '@/types/quiz';
+
+import { Card, CardContent } from '@/components/ui/card';
+import { Trophy, BookOpen, TrendingUp, Star } from 'lucide-react';
+import QuizScoreChart from './QuizScoreChart';
+
+async function fetchQuizHistorys() {
+ const response = await fetch(`/api/v2/quiz/history`, {
+ method: 'GET',
+ });
+
+ if (!response.ok) {
+ const errorResponse = await response.json();
+ throw new Error(
+ errorResponse?.error || '퀴즈 내역을 불러오는데 실패했습니다.'
+ );
+ }
+
+ const { quizHistory, totalCount } = await response.json();
+
+ return { quizHistory, totalCount } as {
+ quizHistory: QuizType[];
+ totalCount: number;
+ };
+}
+
export default function ClientMyPage() {
const { data: user, error: getUserError } = useUser();
+ const { data, isLoading, error } = useQuery({
+ queryKey: ['quizHistory'],
+ queryFn: () => fetchQuizHistorys(),
+ staleTime: 1000 * 60,
+ gcTime: 1000 * 60 * 5,
+ retry: 2,
+ refetchInterval: 1000 * 60,
+ });
+
+ const { quizHistory = [], totalCount = 0 } = data || {};
if (!user) {
toast.error('로그인이 필요합니다');
@@ -36,7 +59,15 @@ export default function ClientMyPage() {