Skip to content

Commit 13f1361

Browse files
committed
feat: 웹 워커 타이머 구현
1 parent ed677c9 commit 13f1361

File tree

1 file changed

+62
-43
lines changed

1 file changed

+62
-43
lines changed

apps/frontend/src/hook/useTimer.ts

Lines changed: 62 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { useState, useEffect, useCallback } from 'react';
1+
import { useCallback, useEffect, useRef, useState } from 'react';
2+
import TimerWorker from '@/workers/timer.worker?worker';
23

34
interface TimerConfig {
45
initialTime: number;
@@ -7,67 +8,85 @@ interface TimerConfig {
78
}
89

910
/**
10-
* 카운트다운 타이머를 관리하는 커스텀 훅입니다.
11-
*
12-
* @description
13-
* 이 훅은 시작 제어 기능을 갖춘 카운트다운 타이머 기능을 제공합니다.
14-
* 초기 시간과 타이머 완료 시 실행될 선택적 콜백을 받습니다.
15-
*
16-
* @example
17-
* ```typescript
18-
* const { time, start } = useTimer({
19-
* initialTime: 60,
20-
* onComplete: () => console.log('타이머 완료!'),
21-
* });
22-
*
23-
* // 타이머 시작
24-
* start();
25-
* ```
11+
* Web Worker를 활용한 정확한 카운트다운 타이머 커스텀 훅
2612
*
2713
* @param {TimerConfig} config - 타이머 설정 객체
28-
* @param {number} config.initialTime - 카운트다운 초기 시간(초 단위)
29-
* @param {() => void} [config.onComplete] - 타이머 완료 시 실행될 선택적 콜백
30-
*
31-
* @returns {object} 현재 시간과 시작 함수를 포함하는 객체
32-
* @returns {number} returns.time - 카운트다운의 현재 시간
33-
* @returns {() => void} returns.start - 타이머를 시작하는 함수
14+
* @returns {object} 타이머 상태와 컨트롤 함수들
3415
*/
35-
3616
export const useTimer = ({ initialTime, onComplete }: TimerConfig) => {
3717
const [time, setTime] = useState(initialTime);
3818
const [isRunning, setIsRunning] = useState(false);
19+
const workerRef = useRef<Worker | null>(null);
3920

4021
useEffect(() => {
41-
let timer: NodeJS.Timeout | null = null;
42-
43-
if (isRunning) {
44-
timer = setInterval(() => {
45-
setTime((prev) => {
46-
const nextTime = prev - 0.1;
47-
if (nextTime <= 0) {
48-
setIsRunning(false);
49-
onComplete?.();
50-
return 0;
51-
}
52-
return nextTime;
53-
});
54-
}, 100);
22+
// Worker가 이미 존재하면 종료
23+
if (workerRef.current) {
24+
workerRef.current.terminate();
5525
}
5626

57-
return () => {
58-
if (timer) {
59-
clearInterval(timer);
27+
// 새 Worker 생성
28+
workerRef.current = new TimerWorker();
29+
30+
// Worker 메시지 핸들러
31+
workerRef.current.onmessage = (event) => {
32+
const { type, payload } = event.data;
33+
// console.log('Received from worker:', type, payload); // 디버깅용
34+
35+
switch (type) {
36+
case 'TICK':
37+
setTime(payload.time);
38+
break;
39+
case 'COMPLETE':
40+
setTime(0);
41+
setIsRunning(false);
42+
onComplete?.();
43+
break;
6044
}
6145
};
62-
}, [isRunning, onComplete]);
6346

47+
// Clean up
48+
return () => {
49+
workerRef.current?.terminate();
50+
};
51+
}, []);
52+
53+
// 타이머 시작
6454
const start = useCallback(() => {
65-
if (isRunning) return;
55+
if (isRunning || !workerRef.current) return;
56+
57+
workerRef.current.postMessage({
58+
type: 'START',
59+
payload: {
60+
duration: initialTime,
61+
serverTime: Date.now(),
62+
},
63+
});
64+
6665
setIsRunning(true);
66+
}, [isRunning, initialTime]);
67+
68+
// 타이머 정지
69+
const stop = useCallback(() => {
70+
if (!isRunning || !workerRef.current) return;
71+
72+
workerRef.current.postMessage({ type: 'STOP' });
73+
setIsRunning(false);
6774
}, [isRunning]);
6875

76+
// 타이머 리셋
77+
const reset = useCallback(() => {
78+
if (!workerRef.current) return;
79+
80+
workerRef.current.postMessage({ type: 'RESET' });
81+
setTime(initialTime);
82+
setIsRunning(false);
83+
}, [initialTime]);
84+
6985
return {
7086
time,
87+
isRunning,
7188
start,
89+
stop,
90+
reset,
7291
};
7392
};

0 commit comments

Comments
 (0)