동적 Interval을 이용해 보다 정확한 로컬 시계 만들기

JS (TIL & Remind)·2021년 8월 5일
1
post-custom-banner

React에서 setTimeout의 재귀호출과 동적인 딜레이 시간을 이용해 보다 정확한 로컬 시계를 만들고자 한다.

기존 방법

const [timer, setTimer] = useState(null);

useEffect(() => {
    const timeInterval = setInterval(() => {
        // moment 대신 Date 객체를 이용해도 된다.
      const now = moment().format('YYYY-MM-DD hh:mm:ss');
      setTimer(now);
    }, 1000);

    return () => {
      clearInterval(timeInterval)
    }
}, []);
         

문제점

Windows의 시계를 띄워놓고 컴포넌트를 확인했을 때, 컴포넌트의 시간이 Windows 시계보다 0.n초씩 느린 것 처럼 보인다.
Windows의 시계는 0.00초에서 1초씩 증가하지만,
setInterval이 처음 실행 될 때, now의 밀리세컨드(ms)가 0이라는 보장이 없기 때문에 컴포넌트의 시간은 0.n초 부터 1초씩 증가하므로 차이가 나는 것이다.


1. Interval 간격을 줄이는 방법

const [timer, setTimer] = useState(null);

useEffect(() => {
    const timeInterval = setInterval(() => {
      const now = moment().format('YYYY-MM-DD hh:mm:ss');
      setTimer(now);
    }, 100);

    return () => {
      clearInterval(timeInterval)
    }
}, []);

setInterval의 callBack 함수가 실행되는 간격을 줄이면서, 컴포넌트 시간을 0.1초마다 갱신해주는 것이다.
이렇게되면 Windows의 시계와 0.1초 + @(setTimer가 호출되고 렌더링 하는 시간) 정도의 차이로 느끼기 힘들정도의 오차가 난다.

문제점

너무 잦은 함수 호출 및 렌더링이 발생하므로 부하가 발생한다.


2. Interval 간격을 동적으로 주는 방법

const [timer, setTimer] = useState(null);
 
  useEffect(() => {
    const myInterval = () => {
      const now = String(new Date().getTime());
      
      // now의 마지막 3자리는 1초 이하의 milliseconds를 나타낸다.
      const nowMilliseconds = Number(now.substring(now.length - 3));
      const nNextSleep = 1000 - nowMilliseconds;

      setTimeout(() => {
        setTimer(moment().format('YYYY-MM-DD hh:mm:ss'));
        myInterval(); // 재귀호출
      }, nNextSleep);
    };

    myInterval();
  }, []);

이 방법은 setTimeout을 재귀적으로 호출하여 setInterval을 구현하고,
Date 객체의 getTime() 함수를 이용해 Interval 간격을 동적으로 조절하는 방법이다.
myInterval이 실행되는 시점의 밀리세컨드(ms)를 구해서, setTimer가 n.0초마다 실행할 수 있도록 한다.

예를 들어, myInterval을 처음 호출 했을 때의 시간이 1.3초이면,
(1 - 0.3) 초 뒤인 2.0초에 setTimer를 실행시켜, Windows의 시계랑 맞추는 것이다.

사실, 이 방법도 오차가 아예 없는 것은 아니지만, setTimer가 호출되고 시계를 렌더링 하는 시간 정도이기 때문에 느끼기 힘든 수준의 오차이다.

마치며

재귀적인 setTimeout 함수를 이용한 setInterval 구현과, 동적 delay 타임을 이용해 로컬 시계 컴포넌트를 구현해 보았다.
현재는 시계 컴포넌트에만 이 방법을 사용하고 있지만, 추후에 스케줄링이 필요한 로직에 유용하게 사용될 것 같다.

profile
노션에 더욱 깔끔하게 정리되어있습니다. (하단 좌측의 홈 모양 아이콘)
post-custom-banner

0개의 댓글