타이머가 0이 됐는데 점수가 더 오르거나, 두 번째 판이 이상하게 빨리 끝나는 일이 있습니다. 둘 다 화면만 보면 멀쩡해 보여서 넘어가기 쉽습니다. 저도 처음에는 "끝나면 표적이 사라지니까 됐다"고 생각하고 지나갔습니다.

문제는 표적이 사라지는 것과 입력이 막히는 것이 전혀 다른 일이라는 데 있었습니다. 눈에는 끝났는데 코드에서는 안 끝나 있었습니다.

30초 표적 게임으로 시간 표시와 입력 허용 여부, 다음 판 초기화를 하나씩 떼어 확인하겠습니다. 이 예제는 반응 속도를 익히려고 만든 로컬 게임이고, 공정한 온라인 순위를 가리는 장치가 아닙니다.

화면 갱신 횟수로 시간을 세지 않습니다

프레임마다 1을 빼고 1,800프레임이 지나면 끝내는 방식을 처음에 썼습니다. 60프레임이면 30초니까 맞다고 생각했는데, 화면 갱신 주기는 환경마다 다릅니다. 모니터가 빠르면 빨리 끝나고 느리면 늦게 끝납니다.

더 나쁜 것은 탭을 옮겼을 때입니다. requestAnimationFrame은 대부분의 브라우저에서 배경 탭이 되면 멈춥니다. 다른 탭을 보고 돌아오면 그동안 프레임이 안 돌았으니 게임은 시간이 멈춰 있던 셈이 됩니다. 쉬었다 와서 30초를 더 쓰는 게임이 됩니다. 이 동작은 MDN의 requestAnimationFrame 문서에서 2026년 9월 22일에 확인했습니다.

그래서 지금은 시작할 때 performance.now() + 30000을 끝 시각으로 적어 두고, 현재 시각과의 차이만 보여 줍니다. 아래는 실제 게임 코드와 같은 단위로 적은 설명용 계산입니다.

시작 1,000ms → 종료 기준 31,000ms
현재 13,500ms → 남은 시간 17,500ms = 17.5초
현재 31,400ms → 남은 시간 max(0, -400ms) = 0초

창이 다시 그려지는 일과 시간이 흐르는 일을 떼어 놓은 것입니다. 기기 절전이나 프로세스 중단까지 포함한 모든 환경의 시간 정확도를 이 검사로 보증하지는 않습니다. 거기까지는 확인하지 못했습니다.

한 판에서 반드시 볼 세 가지

  1. 시작하고 표적을 세 번 누릅니다. 점수가 3인지 봅니다.
  2. 실제로 30초를 기다립니다. 남은 시간 0.0, 표적 숨김, 최종 3점이 같이 나와야 합니다. 셋 중 하나만 늦어도 그 자리가 범인입니다.
  3. 다시 시작을 누릅니다. 현재 점수는 0이 되고 최고 점수 3은 남아야 합니다.

여기서 멈추면 안 됩니다. 버튼이 안 보인다는 것은 사용자 눈에만 해당하는 이야기입니다. 종료 뒤에 클릭 이벤트가 도착해도 점수가 오르지 않아야 진짜로 끝난 것입니다. 제가 처음에 놓친 자리가 정확히 여기였습니다. 숨기기만 하고 상태를 안 봤습니다.

지금 이 예제의 클릭 처리에는 두 겹이 있습니다. 실행 중인지 보는 조건과 끝 시각을 다시 보는 조건입니다. 숨김은 화면을 위한 것이고, 조건은 이미 날아온 입력까지 막기 위한 것입니다. 둘 중 하나만 있으면 반쪽입니다.

고장 난 위치를 좁히는 표

증상의심할 부분구분해서 재현할 방법
0초 뒤 점수가 바뀜입력 처리의 종료 조건종료 뒤 점수만 관찰
두 번째 판이 빨리 끝남이전 판의 타이머·종료 기준첫 판과 재시작 판 비교
다시 시작해도 3점현재 점수 초기화최고 점수와 현재 점수 분리
작은 화면에서 표적이 잘림표적 크기를 뺀 좌표 계산게임 중 창 너비 변경
다른 탭에 갔다 오니 시간이 늘어남프레임 횟수 기반 카운트짧게 탭을 전환해 비교

두 번째 줄이 제일 자주 걸립니다. 첫 판은 멀쩡한데 재시작 판만 짧다면 앞 판의 타이머가 안 꺼진 채 하나 더 도는 경우가 대부분입니다. 타이머가 둘이면 1초에 2가 깎입니다.

키보드도 따로 보셔야 합니다. 표적이 자리를 옮겨도 버튼 초점이 그대로면 Enter를 눌러 계속 득점할 수 있습니다. 접근성에 필요한 입력 경로이기는 한데, 마우스로 표적을 찾아 누르는 것과 난이도가 같지 않습니다. 온라인 대회를 붙일 생각이라면 입력 방식과 점수 검증을 먼저 설계해야 합니다. 나중에 얹으면 이미 올라간 기록을 어떻게 할지부터 막힙니다.

이번 검사의 범위

2026년 9월 22일 로컬 Chrome에서 확인한 것은 넷입니다. 실제로 30초를 기다린 것, 3점으로 끝나는 것, 종료 뒤 합성 클릭이 무시되는 것, 재시작에서 초기화되는 것.

합성 이벤트는 상태 조건이 도는지 보는 검사이지 실제 터치 지연이나 절전 복귀 시험이 아닙니다. 배경 탭, 기기 절전, 실물 휴대폰은 따로 확인해야 하는데 이번에는 하지 않았습니다.

전체 코드와 제작 요청문은 표적 게임 완성형 프롬프트에 있습니다. 검사 결과를 남에게 넘길 때는 오류 제보 양식에 맞춰 적으면 "안 돼요" 한 줄보다 훨씬 빨리 고쳐집니다. 저장한 기록이 사라지는 쪽 문제라면 브라우저 저장 문제를 구분하는 순서가 따로 있습니다.