diff --git a/.pnp.cjs b/.pnp.cjs
index eb78e062..5bf37711 100755
--- a/.pnp.cjs
+++ b/.pnp.cjs
@@ -6842,6 +6842,7 @@ const RAW_RUNTIME_STATE =
["qrcode.react", "virtual:658502eb4296e93abedc18b6aa9b26978f434f08d98e21ebb0e725354b8bb54b62db9c4a1893e460c694ff7500ff5cbafa4457b0dfd26b5838868666c861e990#npm:4.1.0"],\
["react", "npm:18.3.1"],\
["react-dom", "virtual:658502eb4296e93abedc18b6aa9b26978f434f08d98e21ebb0e725354b8bb54b62db9c4a1893e460c694ff7500ff5cbafa4457b0dfd26b5838868666c861e990#npm:18.3.1"],\
+ ["react-error-boundary", "virtual:658502eb4296e93abedc18b6aa9b26978f434f08d98e21ebb0e725354b8bb54b62db9c4a1893e460c694ff7500ff5cbafa4457b0dfd26b5838868666c861e990#npm:4.1.2"],\
["react-router-dom", "virtual:658502eb4296e93abedc18b6aa9b26978f434f08d98e21ebb0e725354b8bb54b62db9c4a1893e460c694ff7500ff5cbafa4457b0dfd26b5838868666c861e990#npm:6.27.0"],\
["recharts", "virtual:658502eb4296e93abedc18b6aa9b26978f434f08d98e21ebb0e725354b8bb54b62db9c4a1893e460c694ff7500ff5cbafa4457b0dfd26b5838868666c861e990#npm:2.13.3"],\
["socket.io-client", "npm:4.8.1"],\
@@ -12324,6 +12325,29 @@ const RAW_RUNTIME_STATE =
"linkType": "HARD"\
}]\
]],\
+ ["react-error-boundary", [\
+ ["npm:4.1.2", {\
+ "packageLocation": "./.yarn/cache/react-error-boundary-npm-4.1.2-7591172537-0737e5259b.zip/node_modules/react-error-boundary/",\
+ "packageDependencies": [\
+ ["react-error-boundary", "npm:4.1.2"]\
+ ],\
+ "linkType": "SOFT"\
+ }],\
+ ["virtual:658502eb4296e93abedc18b6aa9b26978f434f08d98e21ebb0e725354b8bb54b62db9c4a1893e460c694ff7500ff5cbafa4457b0dfd26b5838868666c861e990#npm:4.1.2", {\
+ "packageLocation": "./.yarn/__virtual__/react-error-boundary-virtual-0d90b1d24e/0/cache/react-error-boundary-npm-4.1.2-7591172537-0737e5259b.zip/node_modules/react-error-boundary/",\
+ "packageDependencies": [\
+ ["react-error-boundary", "virtual:658502eb4296e93abedc18b6aa9b26978f434f08d98e21ebb0e725354b8bb54b62db9c4a1893e460c694ff7500ff5cbafa4457b0dfd26b5838868666c861e990#npm:4.1.2"],\
+ ["@babel/runtime", "npm:7.26.0"],\
+ ["@types/react", "npm:18.3.12"],\
+ ["react", "npm:18.3.1"]\
+ ],\
+ "packagePeers": [\
+ "@types/react",\
+ "react"\
+ ],\
+ "linkType": "HARD"\
+ }]\
+ ]],\
["react-is", [\
["npm:16.13.1", {\
"packageLocation": "./.yarn/cache/react-is-npm-16.13.1-a9b9382b4f-33977da7a5.zip/node_modules/react-is/",\
diff --git a/.yarn/cache/react-error-boundary-npm-4.1.2-7591172537-0737e5259b.zip b/.yarn/cache/react-error-boundary-npm-4.1.2-7591172537-0737e5259b.zip
new file mode 100644
index 00000000..155e82dd
Binary files /dev/null and b/.yarn/cache/react-error-boundary-npm-4.1.2-7591172537-0737e5259b.zip differ
diff --git a/packages/client/package.json b/packages/client/package.json
index 82da9a74..2c70fc82 100644
--- a/packages/client/package.json
+++ b/packages/client/package.json
@@ -20,6 +20,7 @@
"qrcode.react": "^4.1.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
+ "react-error-boundary": "^4.1.2",
"react-router-dom": "^6.27.0",
"recharts": "^2.13.3",
"socket.io-client": "^4.8.1"
diff --git a/packages/client/src/pages/quiz-session/index.lazy.tsx b/packages/client/src/pages/quiz-session/index.lazy.tsx
index 12f7f03e..de18c25e 100644
--- a/packages/client/src/pages/quiz-session/index.lazy.tsx
+++ b/packages/client/src/pages/quiz-session/index.lazy.tsx
@@ -21,9 +21,6 @@ export default function QuizSessionLazyPage() {
quizOrder: parseInt(id as string),
});
- console.log(quiz.currentQuizData.timeLimit);
- console.log(quiz.currentQuizData.position);
-
useEffect(() => {
const prevCurrentOrder = localStorage.getItem('currentOrder');
if (prevCurrentOrder !== null && parseInt(prevCurrentOrder) !== quiz.currentQuizData.position) {
@@ -45,21 +42,23 @@ export default function QuizSessionLazyPage() {
return (
<>
{!isQuizEnd && (
-
-
-
+
)}
{isQuizEnd && (
diff --git a/packages/client/src/pages/quiz-session/ui/ProgressBar.tsx b/packages/client/src/pages/quiz-session/ui/ProgressBar.tsx
index 51df29ef..7f57a800 100644
--- a/packages/client/src/pages/quiz-session/ui/ProgressBar.tsx
+++ b/packages/client/src/pages/quiz-session/ui/ProgressBar.tsx
@@ -1,3 +1,5 @@
+import { useEffect, useRef } from 'react';
+
interface ProgressBarProps {
/** 전체 시간 설정 */
time: number;
@@ -10,7 +12,7 @@ interface ProgressBarProps {
/** 애니메이션 종료 시 콜백 함수 */
handleAnimationEnd?: () => void;
/** 현재 진행 시간 */
- currentTime?: number;
+ remainingTime: number;
}
const progressBarColors = {
@@ -22,27 +24,42 @@ const progressBarColors = {
};
const progressBarShapes = {
- rounded: 'rounded-r-base',
+ rounded: 'rounded-base',
square: 'rounded-none',
};
const ProgressBar = ({
- time = 5,
+ time,
type = 'success',
barShape = 'rounded',
pauseOnHover,
handleAnimationEnd,
- currentTime = 0,
+ remainingTime,
}: ProgressBarProps) => {
const progressBarColor = progressBarColors[type];
const progressBarShape = progressBarShapes[barShape];
+ const progressBarRef = useRef
(null);
+
+ useEffect(() => {
+ const startTime = Date.now();
+ const endTime = startTime + remainingTime * 1000;
+
+ const updateWidth = () => {
+ const now = Date.now();
+ const remaining = Math.max(endTime - now, 0);
+ const progress = (remaining / (time * 1000)) * 100;
+
+ if (progressBarRef.current) {
+ progressBarRef.current.style.width = `${progress}%`;
+ }
+
+ if (remaining > 0) {
+ requestAnimationFrame(updateWidth);
+ }
+ };
- // 현재 시간에 따른 진행률 계산
- const progress = Math.min(Math.max((currentTime / time) * 100, 0), 100);
- // 남은 시간 비율 계산
- const remainingTimeRatio = 1 - currentTime / time;
- // 애니메이션 지속 시간 계산 (초 단위)
- const animationDuration = time * remainingTimeRatio;
+ updateWidth();
+ }, [remainingTime, time]);
return (
@@ -51,10 +68,7 @@ const ProgressBar = ({
className={`h-[6px] ${progressBarColor} ${progressBarShape} ${
pauseOnHover && 'group-hover:[animation-play-state:paused]'
}`}
- style={{
- width: `${progress}%`,
- animation: `progress ${animationDuration}s linear forwards`,
- }}
+ ref={progressBarRef}
/>
diff --git a/packages/client/src/pages/quiz-session/ui/QuizHeader.tsx b/packages/client/src/pages/quiz-session/ui/QuizHeader.tsx
index 7a9fd6b9..426eb8f0 100644
--- a/packages/client/src/pages/quiz-session/ui/QuizHeader.tsx
+++ b/packages/client/src/pages/quiz-session/ui/QuizHeader.tsx
@@ -13,7 +13,7 @@ import HamsterImage from '@/shared/assets/characters/햄스터.png';
import { Calendar, Clock, Users } from 'lucide-react';
import { useQueryClient } from '@tanstack/react-query';
-// import ProgressBar from './ProgressBar';
+import ProgressBar from './ProgressBar';
interface QuizHeaderProps {
startTime: number;
@@ -72,7 +72,7 @@ export default function QuizHeader({
-
+
{nickname}
-
-
+
+
{remainingTime}초 남음
- {/*
*/}
+ />
diff --git a/packages/client/src/shared/boundary/AsyncBoundary.tsx b/packages/client/src/shared/boundary/AsyncBoundary.tsx
index c6ad2318..5da6bb03 100644
--- a/packages/client/src/shared/boundary/AsyncBoundary.tsx
+++ b/packages/client/src/shared/boundary/AsyncBoundary.tsx
@@ -1,6 +1,11 @@
import { Suspense } from 'react';
import QuizLoading from '@/pages/quiz-session/ui/QuizLoading';
+import { ErrorBoundary } from 'react-error-boundary';
export default function AsyncBoundary({ children }: { children: React.ReactNode }) {
- return }>{children};
+ return (
+ 안녕하세요?
}>
+
}>{children}
+
+ );
}
diff --git a/yarn.lock b/yarn.lock
index c01b918c..3c558c58 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4553,6 +4553,7 @@ __metadata:
qrcode.react: "npm:^4.1.0"
react: "npm:^18.3.1"
react-dom: "npm:^18.3.1"
+ react-error-boundary: "npm:^4.1.2"
react-router-dom: "npm:^6.27.0"
recharts: "npm:^2.13.3"
socket.io-client: "npm:^4.8.1"
@@ -9212,6 +9213,17 @@ __metadata:
languageName: node
linkType: hard
+"react-error-boundary@npm:^4.1.2":
+ version: 4.1.2
+ resolution: "react-error-boundary@npm:4.1.2"
+ dependencies:
+ "@babel/runtime": "npm:^7.12.5"
+ peerDependencies:
+ react: ">=16.13.1"
+ checksum: 10c0/0737e5259bed40ce14eb0823b3c7b152171921f2179e604f48f3913490cdc594d6c22d43d7abb4ffb1512c832850228db07aa69d3b941db324953a5e393cb399
+ languageName: node
+ linkType: hard
+
"react-is@npm:^16.13.1":
version: 16.13.1
resolution: "react-is@npm:16.13.1"