사이드 프로젝트에서 타이머 기능을 구현했다.
setInterval 함수를 사용해 1초마다 타이머를 갱신해주도록 설계했는데 몇가지 문제점이 존재했다.
문제점
- 예상했던 1초보다 더 느리게 갱신된다.
- ms 단위로 측정이 불가능하다.
먼저 이전 코드를 살펴보자.
export interface Time {
hour: number;
min: number;
sec: number;
}
function Timer() {
const [isRunning, setIsRunning] = useState<boolean>(false);
const [isPaused, setIsPaused] = useState<boolean>(false);
const [time, setTime] = useState<Time>({
hour: 0,
min: 0,
sec: 0,
});
useEffect(() => {
const intervalId = setInterval(() => {
setTime((prevTime: Time) => {
if (!isRunning || isPaused) {
clearInterval(intervalId);
return prevTime;
}
let { hour, min, sec } = prevTime;
if (sec === 0) {
if (min === 0) {
if (hour === 0) {
clearInterval(intervalId);
return prevTime;
}
hour -= 1;
min = 59;
sec = 59;
} else {
min -= 1;
sec = 59;
}
} else {
sec -= 1;
}
return {
hour,
min,
sec,
};
});
}, 1000);
return () => clearInterval(intervalId);
}, [isPaused, isRunning, time]);
useEffect내에서 setInterval함수를 1초마다 호출하여time
값을 갱신해 주었다.
문제 원인 setInterval
setInterval
은 자바스크립트의 메인 스레드에서 실행된다.
자바스크립트는 싱글 스레드를 사용하여, 비동기 이벤트들을 큐에 대기 시키는데,
그 사이에 다른 이벤트가 발생하면 setInterval
이벤트가 지연될 수 있다.
또한 HTML5 표준에는 지연(다른 이벤트가)이 없어도, setTimeout, setInterval이 5회 이상 실행될 경우,
4ms 이상의 지연시간이 강제적으로 추가된다고 한다.
HTML 표준에 명시된 것과 같이, 브라우저는 setTimeout 호출이 5번 이상 중첩된 경우 4ms의 최소 타임아웃을 강제합니다.
위 같이 1000로 업데이트 되지 않고 조금씩 밀리는 것을 볼 수 있다.
대안으로 찾은 방법은 requestAnimationFrame 함수 이다.
또한 타이머의 정확성을 위해 타이머 시간 (시, 분, 초)에서 1초마다 값을 빼주는 것이 아닌,
"시작" 버튼을 눌렀을 때의 시작 시간
과 완료 시간
을 계산 해주어야 될 것 같다.
참고 : https://yceffort.kr/2020/01/learning-from-react-count-down
'Front-end > React' 카테고리의 다른 글
[Next13] next/image 속성으로 이미지 최적화 적용하기 (0) | 2023.10.03 |
---|---|
[Next] dayjs UTC시간 -> 현지시간 적용 (0) | 2023.07.21 |
[React] 디바운싱 적용하여 자동 계산 성능 개선하기 (0) | 2023.03.21 |
[React] hooks로 모듈화하여 변경에 유연한 컴포넌트 만들기 (0) | 2023.02.11 |
[React] Proxy 설정하기 (0) | 2022.09.29 |