1- import { useState , useEffect , useCallback } from 'react' ;
1+ import { useCallback , useEffect , useRef , useState } from 'react' ;
2+ import TimerWorker from '@/workers/timer.worker?worker' ;
23
34interface 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-
3616export 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