[Study] FE/React

[React] 타이머 컴포넌트 만들기

stop-zero 2023. 5. 18. 16:09
과제형 코딩 테스트 연습

 

n초부터 1초마다 1씩 감소하는 타이머를 구현해보려고 한다. stop 버튼을 누르면 타이머가 멈추도록 할 것이다. 

react에서는 useState hook과 useEffect hook을 활용하여 설정할 수 있다. 

자바스크림트에서는 setTimeout 과 setInterval 함수로 해결할 수 있다. 

그 중 setInterval()를 호출해서 고정된 시간 지연으로 함수를 반복적으로 호출하고 setInterval 이 반환한는 interval ID를 clearInterval() 함수로 제거한다. 

import { useState, useEffect, useRef } from 'react';

const Time = () => {
  //컴포넌트는 useState 훅을 사용하여 min, sec 두 개의 상태 변수 정의
  const [min, setMin] = useState(5);
  const [sec, setSec] = useState(0);
  const time = useRef(300); // useRef hook time 변수 생성, 초 단위로 5분
  const timerId = useRef(null); // 간격 타이머의 Id 저장

  // 타이머 시작
  // - 의존성 배열이 비어있으므로 한 번만 실행됨
  // - setInterval 1초마다 실행
  // - 나중에 사용하기 위해 timerId.current ref에 저장되는 Id를 반환
  // - 간격 함수 내에서 남은 시간을 분과 초를 반환하여 min,sec 상태 변수 업데이트
  useEffect(() => {
    timerId.current = setInterval(() => {
      setMin(parseInt(time.current / 60));
      setSec(time.current % 60);
      time.current -= 1; // 남은 시간을 추적하기 위해 1씩 감소
    }, 1000);

    return () => clearInterval(timerId.current); // 컴포넌트가 마운트 해제될 때 간격을 지우기 위해 clearInterval 함수 반환
  }, []);

  // 시간이 0에 도달했을 때 확인
  // sec 상태 변수가 변경될 때마다 실행
  useEffect(() => {
    if (time.current <= 0) {
      console.log('time out');
      clearInterval(timerId.current); // 간격지우고 콘솔에 메시지 기록
      // 타임 아웃을 처리하기 위해 이벤트를 dispatch
    }
  }, [sec]);

  // stop 후 다시 시작
  const startTimer = () => {
    time.current = min * 60 + sec;
    timerId.current = setInterval(() => {
      setMin(parseInt(time.current / 60));
      setSec(time.current % 60);
      time.current -= 1;
    }, 1000);
  };

  // setInterval()를 멈추기 위한 clearInterval() 호출
  const stopTimer = () => {
    clearInterval(timerId.current);
  };

  return (
    <div className="timer">
      <h3>Count Down</h3>
      {min} 분 {sec} 초
      <div>
        <button onClick={startTimer}>Start</button>
        <button onClick={stopTimer}>Stop</button>
      </div>
    </div>
  );
};

export default Time;