Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fix] 페이지 벗어나 있으면 타이머 안 움직이는 에러 해결 #311

Merged
merged 2 commits into from
Jul 30, 2024

Conversation

eonseok-jeon
Copy link
Member

@eonseok-jeon eonseok-jeon commented Jul 30, 2024

Related Issue : Closes #298


🧑‍🎤 Summary

  • 다른 페이지 구경하고 있으면 타이머 작동 안 하는 에러 해결

🧑‍🎤 Comment

다른 페이지를 구경하든 말든 타이머가 작동되기 위해선
현재 시간으로 비교하는 게 좋을 거라고 판단했어요
아이디어 출처

5시 5분 30초에 인증 전송 버튼을 클릭하면 5시 10분 30초에 마감되도록 말이죠

일단 기존과 동일하게 useEffect를 이용해서 isActive 상태가 되면 타이머가 작동되도록 했어요
false면은 다시 초기화 되고요

useEffect(() => {
  let timeout: NodeJS.Timeout | null = null;
  if (isActive) {
    // ...
  } else {
    setSeconds(INITIAL_TIME - 1);
  }
  // ...
}, [isActive, onResetTimer]);

이때 INITIAL_TIME - 1을 해주는 이유는
예를 들어 5분 30.001초에 전송 버튼을 눌렀으면 약 1초의 시간 뒤에 타이머가 4:59로 변하게 되는데 30.832초에 눌렀다면 0.2초 뒤에 4:59로 변하게 됩니다
결과적으로 5:00에서 곧바로 4:59로 바뀌게 되어 마치 에러처럼 보이더라고요
차라리 59에서 천천히 58로 내려가는 게 더 낫다고 판단해서 -1을 해줬습니다

isActive이면 전송 버튼을 클릭한 시간과 5분이 지난 시간을 갖도록 하였습니다

const initialDate = new Date();
const ExpiryTime = new Date(initialDate.getTime() + INITIAL_TIME * 1000);

이후 현재 시간과 비교하는 함수를 만들어 줬어요

const tick = () => {
  const now = new Date();
  const diffInSeconds = differenceInSeconds(ExpiryTime, now);

  if (diffInSeconds > 0) {
    setSeconds(diffInSeconds === INITIAL_TIME ? INITIAL_TIME - 1 : diffInSeconds);
    timeout = setTimeout(tick, 1000 - (now.getTime() % 1000));
  } else {
    onResetTimer();
    setSeconds(INITIAL_TIME - 1);
  }
};

현재 시간과 마감되는 시간을 date-fns 라이브러리를 이용하여 비교해 줍니다

const now = new Date();
const diffInSeconds = differenceInSeconds(ExpiryTime, now);

차이가 0보다 크면 seconds를 update 해주고
차이가 0보다 작으면 초기화 시킵니다

if (diffInSeconds > 0) {
  setSeconds(diffInSeconds === INITIAL_TIME ? INITIAL_TIME - 1 : diffInSeconds);
  timeout = setTimeout(tick, 1000 - (now.getTime() % 1000));
} else {
  onResetTimer();
  setSeconds(INITIAL_TIME - 1);
}

시간초를 보다보면 58초에서 56초로 바로 건너뛸 때가 있었습니다 (timer drift)
setInterval 또는 setTimeout을 사용할 때 JavaScript의 이벤트 루프나 다른 비동기 작업들 때문에
지정한 간격보다 실제 호출 시간이 지연될 수 있는데
이러한 미세한 지연이 누적되면 예상 시간과 실제 시간이 점점 차이가 나게 되는 것입니다
setTimeout을 이용해서 tick을 다시 호출하는 것은 이를 막아주기 위함입니다
참고자료

now.getTime() % 1000 -> 현재 시간의 밀리초
1000 - (now.getTime() % 1000) -> 1초에서 해당 밀리초 만큼 빼줌으로써 다음 초의 정각까지 남은 시간을 계산해요
예를 들어, 567 밀리초가 지난 경우, 1000 - 567 = 433 밀리초가 남은 것입니다!
이후 setTimeout(tick, 1000 - (now.getTime() % 1000))를 통해 남은 밀리초 뒤에 tick이 다시 실행시켜
매초마다 정확히 실행되도록 수정할 수 있었습니다 :)

Copy link

height bot commented Jul 30, 2024

Link Height tasks by mentioning a task ID in the pull request title or commit messages, or description and comments with the keyword link (e.g. "Link T-123").

💡Tip: You can also use "Close T-X" to automatically close a task when the pull request is merged.

Copy link
Member

@lydiacho lydiacho left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

작동 잘 되는 것, 코드 모두 잘 확인했습니다
고생하셨습니다 :)

@eonseok-jeon eonseok-jeon merged commit e1a2302 into develop Jul 30, 2024
1 check passed
@eonseok-jeon eonseok-jeon deleted the fix/#298_background-timer branch July 30, 2024 20:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Fix] 페이지 벗어나 있으면 타이머 안 움직이는 에러 해결
2 participants