⏱️ 온라인 스톱워치 (랩 타이머 지원)
시작 버튼을 누르면 도구가 실제로 30밀리초 단위로 시간 조각을 더하는 것이 아니라, 시작 순간의 performance.now() 값을 기록하고, 다시 그 값을 현재 시간에서 빼어 경과 시간을 계산합니다. 이 값은 매번 새로 계산되며, 누적되지 않습니다. 이 차이는 중요합니다. 브라우저는 배터리 절약을 위해 백그라운드 탭에서 setInterval을 느리게 하기 때문입니다. 이는 일반적인 타이머가 시간이 흐르면서 초를 놓치는 원인입니다. 이 스톱워치는 타이머가 실제로 몇 번 실행되었는지 세는 것이 아니라, 타임스탬프의 차이를 기반으로 시간을 계산하므로, 탭에서 벗어난 후 다시 돌아오면, 그 순간 정확한 시간이 표시됩니다.
이 스톱워치는 브라우저의 탭에서 화면 업데이트가 느려지더라도 정확한 시간을 유지할 수 있는 무료 브라우저 랩 타이머입니다. 이는 setInterval의 틱을 세는 대신, monotonic performance.now() 시계와 저장된 시작 타임스탬프를 비교하여 경과 시간을 추적합니다.
설계부터 프라이빗. 이 도구는 전부 브라우저 안에서 실행돼요 — 입력한 내용은 업로드되거나 저장되지 않으며, 오프라인에서도 작동합니다.
소개
어떻게 시간을 재나
내부적으로 세 가지 상태가 있습니다: 누적된 `elapsed` 값(밀리초 단위), `startAt`이라는 `performance.now()`의 순간 값을 저장한 값, 그리고 `running`이라는 불리언 플래그입니다. 화면에 표시되는 숫자를 생성하는 함수는 `elapsed`에 더해, `running`이 참이라면 현재 시간과 `startAt`의 차이를 더한 값입니다. `setInterval`은 이 함수를 30밀리초 간격으로 호출하여 화면을 다시 그립니다. 이 간격 자체는 시간 정보를 포함하지 않으며, 단지 화면 갱신을 위한 트리거입니다. 이는 스톱워치를 제작하는 올바른 방식이며, 일반적인 타이머가 틱이 지연되거나 건너뛰거나 병합될 경우 점점 편차가 생기는 문제를 직접적으로 피할 수 있습니다.
다른 탭으로 가도 시간이 안 밀린다
이 설계 선택은 다른 탭으로 전환할 때에도 동일하게 적용됩니다. 브라우저는 CPU와 배터리 절약을 위해 백그라운드 탭의 타이머를 제한할 수 있습니다. 30ms 간격으로 설정된 `setInterval`은 탭이 보이지 않을 때 1초 간격으로 지연될 수 있습니다. 이 스톱워치에서는 이 설정이 화면에 표시되는 숫자가 얼마나 자주 갱신되는지에만 영향을 미칩니다. 실제 계산(현재 타임스탬프에서 시작 타임스탬프를 뺀 값에 이미 누적된 시간을 더한 값)은 갱신이 건너뛰어졌는지에 관계없이 동일하게 작동합니다. 다른 탭으로 돌아오면 다음 화면 갱신 시 정확한 경과 시간이 표시됩니다. 이는 탭 전환으로 인해 시간이 손실되거나 증가하지 않고, 단지 화면이 부드럽게 흐르지 않는다는 점만입니다.
performance.now()를 쓰는 이유
이 작업에 적합한 기본적인 선택은 `performance.now()`를 사용하는 것입니다. `Date.now()`는 시스템의 벽 시계를 반영하며, 벽 시계는 점프할 수 있습니다. 예를 들어, 노트북이 수면에서 깨어나 NTP 서버와 동기화하거나, 시스템 시간이 수동으로 변경되거나, 시간대 조정이 이루어질 경우 `Date.now()`는 중간에 앞으로 또는 뒤로 점프할 수 있습니다. `performance.now()`는 monotonic 시계로 정의되어 있으며, 항상 일정한 속도로 증가하며, 이러한 보정에 영향을 받지 않습니다. 이는 스톱워치가 필요로 하는 보장이며, 일반적인 벽 시계 타이머는 제공하지 않습니다.
정직한 한계 — 저장하지 않는다
정직한 한계는 지속성입니다. 이 구현에서는 `elapsed`과 `laps` 배열을 페이지 메모리의 일반적인 자바스크립트 변수로 유지합니다. 소스 코드의 어떤 부분도 `localStorage` 또는 이 사이트의 자체 `Playground.store()` 도우미를 호출하지 않으며, 다른 Toolio 도구들이 사용하는 저장 기능도 없습니다. 이는 페이지를 새로고침하거나 탭을 닫거나 다른 도구로 이동하면 실행 시간과 기록된 모든 랩이 즉시 영구히 삭제됨을 의미합니다. 이전 세션을 복구하거나, 장치 간 동기화, 백업 기능은 없습니다. 왜냐하면 처음부터 저장에 아무것도 쓰지 않기 때문입니다. 랩 기록이 실제로 중요한 경우, 예를 들어 운동 기록이나 발표 시간 기록이라면, 이 페이지가 그 값을 기억하지 않으므로, 이동하기 전에 숫자를 복사하거나 스크린샷을 찍어야 합니다.
사용 방법
- 시작 버튼을 클릭하면 버튼이 즉시 "중지"로 라벨이 변경되고, 화면의 시계가 실제 시간에 따라 오르기 시작하며, 30밀리초 간격으로 화면이 다시 그려집니다.
- 실행 중일 때 "랩" 버튼을 클릭하면 현재 경과 시간을 스냅샷으로 저장합니다. 새 항목은 랩 목록의 최상단에 고정되어 표시되며, 이전 랩 이후의 시간(스플릿)과 누적 총 시간을 모두 보여줍니다.
- 같은 버튼을 다시 클릭하면 "중지"로 라벨이 변경되어 화면이 동결됩니다. 경과 시간은 내부적으로 유지되며, 아무것도 손실되지 않으며, 버튼은 "시작"으로 되돌아옵니다.
- 다시 "시작"을 클릭하면, 정확히 이전에 중단한 지점에서 시간 계산을 계속합니다. 0부터 시작하지 않습니다.
- "초기화" 버튼을 클릭하면 화면을 0으로 초기화하고, 기록된 모든 랩을 삭제하며, 두 버튼을 최초의 라벨로 되돌립니다. 이는 새 세션을 시작하는 유일한 방법이며, 이전 세션을 보호하기 위한 저장 기능이 없기 때문입니다.
자주 묻는 질문
- 이 스톱워치는 실제로 내부적으로 시간을 어떻게 측정하는가?
- 시작 버튼을 누르는 순간 `performance.now()`를 한 번 읽고 그 값을 저장합니다. 이후 화면에 표시되는 값은 "이미 누적된 시간"에 "현재 `performance.now()` 값에서 저장된 시작 타임스탬프를 뺀 값"을 더한 것입니다. 이 값은 30ms 간격으로 화면이 다시 그려질 때마다 새로 계산됩니다. 이는 단순히 실행 중인 총합에 고정된 +30ms를 더하는 방식이 아닌, 이는 많은 수작업 타이머가 시간이 지나면서 점점 동기화를 놓치는 이유입니다.
- 다른 브라우저 탭으로 전환하면 시간이 어떻게 되는가?
- 백그라운드 탭은 제한됩니다. 브라우저는 CPU와 배터리 절약을 위해 30ms 간격의 `setInterval`을 1초 간격으로 지연하거나 더 느리게 할 수 있습니다. 이로 인해 화면에 표시되는 시간이 멈추거나 뛰어넘는 것처럼 보일 수 있습니다. 그러나 이 스톱워치는 항상 타임스탬프의 차이를 기반으로 경과 시간을 계산하며, 실행된 틱의 수를 세는 것이 아니라, 탭으로 돌아왔을 때 화면이 다시 그려지는 순간 정확한 경과 시간을 표시합니다. 이는 탭 전환으로 인해 시간이 손실되거나 증가하지 않고, 단지 화면의 부드러움이 손상될 뿐입니다.
- 왜 `performance.now()`를 사용하고 `Date.now()`를 사용하지 않나요? 이는 실제로 중요한가요?
- 예. `Date.now()`는 시스템의 벽 시계를 반영하며, 이는 앞으로 또는 뒤로 점프할 수 있습니다. 예를 들어, 노트북이 수면에서 깨어나 NTP 서버와 동기화하거나, 시스템 시간이 수동으로 변경되었을 때입니다. `performance.now()`는 항상 일정한 속도로 증가하는 monotonic 시계이며, 이러한 보정에 영향을 받지 않습니다. 이는 스톱워치가 필요로 하는 속성이며, 이 구현에서는 정확한 시계를 사용하고 있습니다.
- 이 스톱워치는 경과 시간이나 랩 기록을 저장하는 곳이 있나요?
- 아니요. 다른 Toolio 도구와 달리, 이 데이터를 위해 브라우저의 `localStorage`를 사용하지 않습니다. 화면에 보이는 모든 내용은 이 특정 탭이 열려 있는 동안 페이지 메모리에만 존재합니다. 페이지를 새로고침하거나 탭을 닫거나 다른 도구로 이동하면, 경과 시간과 모든 랩은 영원히 사라집니다. 복구나 자동 저장 기능도 없으며, 처음부터 저장에 아무것도 쓰지 않기 때문입니다. 기록이 필요하다면, 이동하기 전에 숫자를 복사하거나 스크린샷을 찍어야 합니다.
- 랩 수에 제한이 있나요? 그리고 각 랩 행에 있는 두 숫자는 무엇을 의미하나요?
- 제한이 없습니다. 랩 목록은 계속 증가하며, 가장 최근의 랩은 항상 상단에 고정됩니다. 각 행은 두 숫자를 보여줍니다: 강조 색으로 표시된 스플릿, 이전 랩 이후의 경과 시간; 회색으로 표시된 총 시간, 시작 버튼을 누른 이후의 누적 시간입니다. 이는 전체 실행 시간을 잃지 않고, 개별 구간(예: 운동 세션, 발표의 한 부분)을 평가할 수 있도록 합니다.