From 7f60d5a79a7213eb76485b66f5dae235410d31a6 Mon Sep 17 00:00:00 2001 From: Shawn Kang Date: Sat, 4 Oct 2025 23:13:18 +0900 Subject: [PATCH 1/8] =?UTF-8?q?feat:=20=EC=8B=9C=EA=B0=84=20=EC=B4=9D?= =?UTF-8?q?=EB=9F=89=EC=A0=9C=20=ED=83=80=EC=9D=B4=EB=A8=B8=20=ED=9B=85?= =?UTF-8?q?=EC=97=90=20=EC=A7=84=ED=96=89=EB=A5=A0=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=EB=B3=80=EC=88=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/page/TimerPage/hooks/useTimeBasedTimer.ts | 103 +++++++++++++----- 1 file changed, 77 insertions(+), 26 deletions(-) diff --git a/src/page/TimerPage/hooks/useTimeBasedTimer.ts b/src/page/TimerPage/hooks/useTimeBasedTimer.ts index d9957547..52419597 100644 --- a/src/page/TimerPage/hooks/useTimeBasedTimer.ts +++ b/src/page/TimerPage/hooks/useTimeBasedTimer.ts @@ -38,6 +38,33 @@ export function useTimeBasedTimer(): TimeBasedTimerLogics { const targetTimeRef = useRef(null); const speakingTargetTimeRef = useRef(null); + // 진행률 계산용 변수 + const [denominator, setDenominator] = useState(1); + const updateDenominator = useCallback( + (isOpponentDone: boolean) => { + if (isOpponentDone) { + if (isSpeakingTimerAvailable) { + setDenominator(speakingTimer ?? 0); + } else { + setDenominator(totalTimer ?? 0); + } + } else { + if (isSpeakingTimerAvailable) { + setDenominator(defaultTime.defaultSpeakingTimer ?? 0); + } else { + setDenominator(defaultTime.defaultTotalTimer ?? 0); + } + } + }, + [ + defaultTime.defaultSpeakingTimer, + defaultTime.defaultTotalTimer, + isSpeakingTimerAvailable, + speakingTimer, + totalTimer, + ], + ); + /** * 타이머 시작을 위해 사용하는 저수준 함수 */ @@ -84,30 +111,37 @@ export function useTimeBasedTimer(): TimeBasedTimerLogics { * - isDone(완료) 상태일 땐 시작X * - 1초마다 totalTimer, speakingTimer(필요시) 감소 */ - const startTimer = useCallback(() => { - if (intervalRef.current !== null || totalTimer === null || isDone) { - return; - } + const startTimer = useCallback( + (isOpponentDone: boolean) => { + if (intervalRef.current !== null || totalTimer === null || isDone) { + return; + } - // 목표 시각을 실제 시각 기반으로 계산 - // 예를 들어, 현재 시각이 오후 13시 00분 30초인데, 1회당 발언 시간이 30초라면, - // 1회당 발언 시간이 모두 끝나는 시간은 13시 01분 00초이므로, - // 해당 시간을 목표 시간으로 두는 식임 - const startTime = Date.now(); - targetTimeRef.current = startTime + totalTimer * 1000; - if (isSpeakingTimerAvailable) { - speakingTargetTimeRef.current = startTime + speakingTimer * 1000; - } + // 목표 시각을 실제 시각 기반으로 계산 + // 예를 들어, 현재 시각이 오후 13시 00분 30초인데, 1회당 발언 시간이 30초라면, + // 1회당 발언 시간이 모두 끝나는 시간은 13시 01분 00초이므로, + // 해당 시간을 목표 시간으로 두는 식임 + const startTime = Date.now(); + targetTimeRef.current = startTime + totalTimer * 1000; + if (isSpeakingTimerAvailable) { + speakingTargetTimeRef.current = startTime + speakingTimer * 1000; + } - // 타이머 인터벌 시작 - setTimerInterval(); - }, [ - isDone, - isSpeakingTimerAvailable, - setTimerInterval, - speakingTimer, - totalTimer, - ]); + // 분모 갱신 + updateDenominator(isOpponentDone); + + // 타이머 인터벌 시작 + setTimerInterval(); + }, + [ + isDone, + isSpeakingTimerAvailable, + setTimerInterval, + speakingTimer, + totalTimer, + updateDenominator, + ], + ); /** * 타이머 일시정지 @@ -151,6 +185,9 @@ export function useTimeBasedTimer(): TimeBasedTimerLogics { } else { setSpeakingTimer(defaultTime.defaultSpeakingTimer); } + + // 분모 갱신 + updateDenominator(isOpponentDone); }, [ isSpeakingTimerAvailable, @@ -158,6 +195,7 @@ export function useTimeBasedTimer(): TimeBasedTimerLogics { defaultTime.defaultTotalTimer, totalTimer, pauseTimer, + updateDenominator, ], ); @@ -184,18 +222,25 @@ export function useTimeBasedTimer(): TimeBasedTimerLogics { // 계산한 시간을 1회당 발언 시간으로 설정 setSpeakingTimer(nextSpeakingTime); + updateDenominator(isOpponentDone); return nextSpeakingTime; } else { // # 1회당 발언 시간을 사용하지 않을 경우 // 전체 발언 시간 타이머는 초기값으로 리셋 + updateDenominator(isOpponentDone); if (totalTimer === 0 || totalTimer === null) { return 0; } return totalTimer; } }, - [defaultTime.defaultSpeakingTimer, isSpeakingTimerAvailable, totalTimer], + [ + defaultTime.defaultSpeakingTimer, + isSpeakingTimerAvailable, + totalTimer, + updateDenominator, + ], ); /** @@ -205,7 +250,6 @@ export function useTimeBasedTimer(): TimeBasedTimerLogics { */ const resetAndStartTimer = useCallback( (isOpponentDone: boolean) => { - console.log(`# resetAndStartTimer 호출`); const newTime = resetTimerForNextPhase(isOpponentDone); if (intervalRef.current !== null || totalTimer === null || isDone) { @@ -224,6 +268,9 @@ export function useTimeBasedTimer(): TimeBasedTimerLogics { // 타이머 인터벌 시작 setTimerInterval(); + + // 분모 갱신 + updateDenominator(isOpponentDone); }, [ resetTimerForNextPhase, @@ -231,6 +278,7 @@ export function useTimeBasedTimer(): TimeBasedTimerLogics { isDone, isSpeakingTimerAvailable, totalTimer, + updateDenominator, ], ); @@ -257,8 +305,9 @@ export function useTimeBasedTimer(): TimeBasedTimerLogics { setTotalTimer(null); setSpeakingTimer(null); setIsDone(false); + updateDenominator(false); intervalRef.current = null; - }, [pauseTimer]); + }, [pauseTimer, updateDenominator]); useEffect(() => () => pauseTimer(), [pauseTimer]); @@ -278,6 +327,7 @@ export function useTimeBasedTimer(): TimeBasedTimerLogics { setDefaultTime, setIsDone, clearTimer, + denominator, }; } @@ -291,7 +341,7 @@ export interface TimeBasedTimerLogics { defaultSpeakingTimer: number | null; }; isSpeakingTimerAvailable: boolean; - startTimer: () => void; + startTimer: (isOpponentDone: boolean) => void; pauseTimer: () => void; resetTimerForNextPhase: (isOpponentDone: boolean) => number; resetAndStartTimer: (isOpponentDone: boolean) => void; @@ -305,4 +355,5 @@ export interface TimeBasedTimerLogics { >; setIsDone: Dispatch>; clearTimer: () => void; + denominator: number; } From f7a3ec2863d832ba8e172cb8ee0338cecb1a3749 Mon Sep 17 00:00:00 2001 From: Shawn Kang Date: Sat, 4 Oct 2025 23:13:52 +0900 Subject: [PATCH 2/8] =?UTF-8?q?feat:=20=EC=8B=9C=EA=B0=84=20=EC=B4=9D?= =?UTF-8?q?=EB=9F=89=EC=A0=9C=20=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=8A=A4=EC=97=90=20=EC=A7=84=ED=96=89=EB=A5=A0=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EB=B3=80=EC=88=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/page/TimerPage/components/TimeBasedTimer.tsx | 8 ++++++-- src/page/TimerPage/components/TimerView.tsx | 8 ++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/page/TimerPage/components/TimeBasedTimer.tsx b/src/page/TimerPage/components/TimeBasedTimer.tsx index c5e79b9c..afb2682f 100644 --- a/src/page/TimerPage/components/TimeBasedTimer.tsx +++ b/src/page/TimerPage/components/TimeBasedTimer.tsx @@ -12,13 +12,15 @@ type TimeBasedTimerInstance = { totalTimer: number | null; speakingTimer: number | null; isRunning: boolean; - startTimer: () => void; + startTimer: (isOpponentDone: boolean) => void; pauseTimer: () => void; resetCurrentTimer: () => void; + denominator: number; }; interface TimeBasedTimerProps { timeBasedTimerInstance: TimeBasedTimerInstance; + isOpponentFinished: boolean; isSelected: boolean; onActivate?: () => void; prosCons: TimeBasedStance; @@ -28,6 +30,7 @@ interface TimeBasedTimerProps { export default function TimeBasedTimer({ timeBasedTimerInstance, + isOpponentFinished, isSelected, prosCons, teamName, @@ -40,6 +43,7 @@ export default function TimeBasedTimer({ startTimer, pauseTimer, resetCurrentTimer, + denominator, } = timeBasedTimerInstance; const minute = Formatting.formatTwoDigits( @@ -177,7 +181,7 @@ export default function TimeBasedTimer({ {/* 조작부 */} startTimer(isOpponentFinished)} onPause={pauseTimer} onReset={resetCurrentTimer} stance={prosCons} diff --git a/src/page/TimerPage/components/TimerView.tsx b/src/page/TimerPage/components/TimerView.tsx index 933ee49d..a1f31e12 100644 --- a/src/page/TimerPage/components/TimerView.tsx +++ b/src/page/TimerPage/components/TimerView.tsx @@ -53,9 +53,13 @@ export default function TimerView({ state }: { state: TimerPageLogics }) { isRunning: timer1.isRunning, startTimer: timer1.startTimer, pauseTimer: timer1.pauseTimer, + denominator: timer1.denominator, resetCurrentTimer: () => timer1.resetCurrentTimer(timer2.isDone), }} item={data.table[index]} + isOpponentFinished={ + timer2.totalTimer === null || timer2.totalTimer <= 0 + } isSelected={prosConsSelected === 'PROS'} onActivate={() => handleActivateTeam('PROS')} prosCons="PROS" @@ -79,9 +83,13 @@ export default function TimerView({ state }: { state: TimerPageLogics }) { isRunning: timer2.isRunning, startTimer: timer2.startTimer, pauseTimer: timer2.pauseTimer, + denominator: timer2.denominator, resetCurrentTimer: () => timer2.resetCurrentTimer(timer1.isDone), }} item={data.table[index]} + isOpponentFinished={ + timer1.totalTimer === null || timer1.totalTimer <= 0 + } isSelected={prosConsSelected === 'CONS'} onActivate={() => handleActivateTeam('CONS')} prosCons="CONS" From e529528d2c267033cb4cd041ef5b377811ba0572 Mon Sep 17 00:00:00 2001 From: Shawn Kang Date: Sat, 4 Oct 2025 23:14:10 +0900 Subject: [PATCH 3/8] =?UTF-8?q?fix:=20=ED=95=9C=20=EC=AA=BD=20=EC=8B=9C?= =?UTF-8?q?=EA=B0=84=20=EC=86=8C=EC=A7=84=20=EC=8B=9C=20=EB=8B=A4=EB=A5=B8?= =?UTF-8?q?=20=EC=AA=BD=20=EC=9B=90=ED=98=95=20=EC=A7=84=ED=96=89=EB=8F=84?= =?UTF-8?q?=20=EC=A4=84=EC=96=B4=EB=93=A4=EC=A7=80=20=EC=95=8A=EB=8A=94=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TimerPage/components/TimeBasedTimer.tsx | 48 +++++++++++-------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/src/page/TimerPage/components/TimeBasedTimer.tsx b/src/page/TimerPage/components/TimeBasedTimer.tsx index afb2682f..e8f7bdb5 100644 --- a/src/page/TimerPage/components/TimeBasedTimer.tsx +++ b/src/page/TimerPage/components/TimeBasedTimer.tsx @@ -59,30 +59,38 @@ export default function TimeBasedTimer({ ); const initRawProgress = (): number => { - if (speakingTimer === null) { - // 1회당 발언 시간 X일 때... - if (item.timePerTeam && totalTimer && item.timePerTeam > 0) { - // 팀당 발언 시간 타이머가 정상 동작 중이고 남은 시간이 있을 경우, 진행도를 계산 - if (totalTimer <= 0) { - return 100; - } - return ((item.timePerTeam - totalTimer) / item.timePerTeam) * 100; + if (isOpponentFinished) { + if (speakingTimer === null) { + // 1회당 발언 시간 X일 때... + return ((denominator - (totalTimer ?? 0)) / denominator) * 100; } else { - // 팀당 발언 시간 타이머가 멈추거나 완료된 경우, - // 완료(100%)에 해당하는 진행도를 반환 - return 100; + // 1회당 발언 시간 O일 때... + return ((denominator - (speakingTimer ?? 0)) / denominator) * 100; } } else { - // 1회당 발언 시간 O일 때... - if (item.timePerSpeaking && speakingTimer && item.timePerSpeaking > 0) { - // 1회당 발언 시간 타이머가 정상 동작 중이고 남은 시간이 있을 경우, 진행도를 계산 - return ( - ((item.timePerSpeaking - speakingTimer) / item.timePerSpeaking) * 100 - ); + if (speakingTimer === null) { + // 1회당 발언 시간 X일 때... + if (item.timePerTeam && totalTimer && item.timePerTeam > 0) { + // 팀당 발언 시간 타이머가 정상 동작 중이고 남은 시간이 있을 경우, 진행도를 계산 + if (totalTimer <= 0) { + return 100; + } + return ((item.timePerTeam - totalTimer) / denominator) * 100; + } else { + // 팀당 발언 시간 타이머가 멈추거나 완료된 경우, + // 완료(100%)에 해당하는 진행도를 반환 + return 100; + } } else { - // 1회당 발언 시간 타이머가 멈추거나 완료된 경우, - // 완료(100%)에 해당하는 진행도를 반환 - return 100; + // 1회당 발언 시간 O일 때... + if (item.timePerSpeaking && speakingTimer && item.timePerSpeaking > 0) { + // 1회당 발언 시간 타이머가 정상 동작 중이고 남은 시간이 있을 경우, 진행도를 계산 + return ((item.timePerSpeaking - speakingTimer) / denominator) * 100; + } else { + // 1회당 발언 시간 타이머가 멈추거나 완료된 경우, + // 완료(100%)에 해당하는 진행도를 반환 + return 100; + } } } }; From f2e58e7e750bc739581148d718c114ca729a8307 Mon Sep 17 00:00:00 2001 From: Shawn Kang Date: Sat, 4 Oct 2025 23:40:25 +0900 Subject: [PATCH 4/8] =?UTF-8?q?refactor:=20=EB=B3=80=EC=88=98=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=20=ED=86=B5=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/page/TimerPage/components/TimeBasedTimer.tsx | 8 ++++---- src/page/TimerPage/components/TimerView.tsx | 8 ++------ 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/page/TimerPage/components/TimeBasedTimer.tsx b/src/page/TimerPage/components/TimeBasedTimer.tsx index e8f7bdb5..555a4fb0 100644 --- a/src/page/TimerPage/components/TimeBasedTimer.tsx +++ b/src/page/TimerPage/components/TimeBasedTimer.tsx @@ -20,7 +20,7 @@ type TimeBasedTimerInstance = { interface TimeBasedTimerProps { timeBasedTimerInstance: TimeBasedTimerInstance; - isOpponentFinished: boolean; + isOpponentDone: boolean; isSelected: boolean; onActivate?: () => void; prosCons: TimeBasedStance; @@ -30,7 +30,7 @@ interface TimeBasedTimerProps { export default function TimeBasedTimer({ timeBasedTimerInstance, - isOpponentFinished, + isOpponentDone, isSelected, prosCons, teamName, @@ -59,7 +59,7 @@ export default function TimeBasedTimer({ ); const initRawProgress = (): number => { - if (isOpponentFinished) { + if (isOpponentDone) { if (speakingTimer === null) { // 1회당 발언 시간 X일 때... return ((denominator - (totalTimer ?? 0)) / denominator) * 100; @@ -189,7 +189,7 @@ export default function TimeBasedTimer({ {/* 조작부 */} startTimer(isOpponentFinished)} + onStart={() => startTimer(isOpponentDone)} onPause={pauseTimer} onReset={resetCurrentTimer} stance={prosCons} diff --git a/src/page/TimerPage/components/TimerView.tsx b/src/page/TimerPage/components/TimerView.tsx index a1f31e12..8c9efc86 100644 --- a/src/page/TimerPage/components/TimerView.tsx +++ b/src/page/TimerPage/components/TimerView.tsx @@ -57,9 +57,7 @@ export default function TimerView({ state }: { state: TimerPageLogics }) { resetCurrentTimer: () => timer1.resetCurrentTimer(timer2.isDone), }} item={data.table[index]} - isOpponentFinished={ - timer2.totalTimer === null || timer2.totalTimer <= 0 - } + isOpponentDone={timer2.totalTimer === null || timer2.totalTimer <= 0} isSelected={prosConsSelected === 'PROS'} onActivate={() => handleActivateTeam('PROS')} prosCons="PROS" @@ -87,9 +85,7 @@ export default function TimerView({ state }: { state: TimerPageLogics }) { resetCurrentTimer: () => timer2.resetCurrentTimer(timer1.isDone), }} item={data.table[index]} - isOpponentFinished={ - timer1.totalTimer === null || timer1.totalTimer <= 0 - } + isOpponentDone={timer1.totalTimer === null || timer1.totalTimer <= 0} isSelected={prosConsSelected === 'CONS'} onActivate={() => handleActivateTeam('CONS')} prosCons="CONS" From 0fd7be98e56cab3b9b7b762f6d54b299694a4875 Mon Sep 17 00:00:00 2001 From: Shawn Kang Date: Sat, 4 Oct 2025 23:50:09 +0900 Subject: [PATCH 5/8] =?UTF-8?q?refactor:=20startTimer=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EB=8B=A8=EC=B6=95=ED=82=A4=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/page/TimerPage/hooks/useTimerHotkey.ts | 24 +++++++++++++--------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/page/TimerPage/hooks/useTimerHotkey.ts b/src/page/TimerPage/hooks/useTimerHotkey.ts index cf066907..169f706d 100644 --- a/src/page/TimerPage/hooks/useTimerHotkey.ts +++ b/src/page/TimerPage/hooks/useTimerHotkey.ts @@ -50,11 +50,19 @@ export function useTimerHotkey(state: TimerPageLogics) { } // 찬/반 타이머 토글 (시작/정지) - const toggleTimer = (timer: typeof timer1 | typeof timer2) => { - if (timer.isRunning) { - timer.pauseTimer(); - } else { - timer.startTimer(); + const toggleTimeBasedTimer = () => { + if (prosConsSelected === 'PROS') { + if (timer1.isRunning) { + timer1.pauseTimer(); + } else { + timer1.startTimer(timer2.isDone); + } + } else if (prosConsSelected === 'CONS') { + if (timer2.isRunning) { + timer2.pauseTimer(); + } else { + timer2.startTimer(timer1.isDone); + } } }; @@ -68,11 +76,7 @@ export function useTimerHotkey(state: TimerPageLogics) { normalTimer.startTimer(); } } else { - if (prosConsSelected === 'PROS') { - toggleTimer(timer1); - } else if (prosConsSelected === 'CONS') { - toggleTimer(timer2); - } + toggleTimeBasedTimer(); } break; case 'KeyR': From 02b6318126162f2d63cb9b53a8f4ce17e3d64bb2 Mon Sep 17 00:00:00 2001 From: Shawn Kang Date: Sat, 4 Oct 2025 23:51:56 +0900 Subject: [PATCH 6/8] =?UTF-8?q?fix:=20=EB=B9=8C=EB=93=9C=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/page/TimerPage/stories/TimeBasedTimer.stories.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/page/TimerPage/stories/TimeBasedTimer.stories.tsx b/src/page/TimerPage/stories/TimeBasedTimer.stories.tsx index e6ae3cf5..690f6378 100644 --- a/src/page/TimerPage/stories/TimeBasedTimer.stories.tsx +++ b/src/page/TimerPage/stories/TimeBasedTimer.stories.tsx @@ -10,6 +10,7 @@ const mockTimerInstance = { isDone: false, defaultTime: { defaultTotalTimer: 150, defaultSpeakingTimer: 50 }, isSpeakingTimer: true, + denominator: 1, startTimer: () => {}, pauseTimer: () => {}, resetTimerForNextPhase: () => {}, From 2fa19659778b65b9c9f8041370ecd9de12f5482e Mon Sep 17 00:00:00 2001 From: Shawn Kang Date: Sat, 4 Oct 2025 23:55:12 +0900 Subject: [PATCH 7/8] =?UTF-8?q?refactor:=20CodeRabbit=20=EB=A6=AC=EB=B7=B0?= =?UTF-8?q?=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/page/TimerPage/components/TimerView.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/page/TimerPage/components/TimerView.tsx b/src/page/TimerPage/components/TimerView.tsx index 8c9efc86..478d3a8c 100644 --- a/src/page/TimerPage/components/TimerView.tsx +++ b/src/page/TimerPage/components/TimerView.tsx @@ -57,7 +57,7 @@ export default function TimerView({ state }: { state: TimerPageLogics }) { resetCurrentTimer: () => timer1.resetCurrentTimer(timer2.isDone), }} item={data.table[index]} - isOpponentDone={timer2.totalTimer === null || timer2.totalTimer <= 0} + isOpponentDone={timer2.isDone} isSelected={prosConsSelected === 'PROS'} onActivate={() => handleActivateTeam('PROS')} prosCons="PROS" @@ -85,7 +85,7 @@ export default function TimerView({ state }: { state: TimerPageLogics }) { resetCurrentTimer: () => timer2.resetCurrentTimer(timer1.isDone), }} item={data.table[index]} - isOpponentDone={timer1.totalTimer === null || timer1.totalTimer <= 0} + isOpponentDone={timer1.isDone} isSelected={prosConsSelected === 'CONS'} onActivate={() => handleActivateTeam('CONS')} prosCons="CONS" From 97ac92e1ebd96ea772cd72a347a435798c91c56f Mon Sep 17 00:00:00 2001 From: Shawn Kang Date: Fri, 10 Oct 2025 12:19:56 +0900 Subject: [PATCH 8/8] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=ED=83=80=EC=9E=85=20=EB=AA=85=EC=8B=9C=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/page/TimerPage/hooks/useTimeBasedTimer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/page/TimerPage/hooks/useTimeBasedTimer.ts b/src/page/TimerPage/hooks/useTimeBasedTimer.ts index 52419597..8f4c8641 100644 --- a/src/page/TimerPage/hooks/useTimeBasedTimer.ts +++ b/src/page/TimerPage/hooks/useTimeBasedTimer.ts @@ -39,7 +39,7 @@ export function useTimeBasedTimer(): TimeBasedTimerLogics { const speakingTargetTimeRef = useRef(null); // 진행률 계산용 변수 - const [denominator, setDenominator] = useState(1); + const [denominator, setDenominator] = useState(1); const updateDenominator = useCallback( (isOpponentDone: boolean) => { if (isOpponentDone) {