작업 시작을 10시라고 적고 끝 시각을 빼면 소요 시간을 구할 수 있을 것 같습니다. 그런데 시스템 시계가 동기화 과정에서 조정되면 값이 음수가 되거나 실제보다 크게 늘어날 수 있습니다. 달력에 남길 시각과 같은 실행 안의 경과 시간은 다른 시계를 쓰는 편이 맞습니다. 이 글은 주입한 가상 시계 값으로 뒤로·앞으로 이동하는 벽시계와 제한 시간 예산을 대조하고, 별도로 실제 Python 단조 시계로 고정 해시 작업 한 번의 경과를 측정합니다. 운영체제 시계를 바꾸거나 실제 장애를 발생시키지는 않았습니다.
언제 일어났는지와 얼마나 걸렸는지는 다른 질문입니다.
사용자에게 “2026년 10월 15일에 실행했다”고 보여 주려면 날짜와 시각을 갖는 벽시계 기록이 필요합니다. 반면 함수 호출 전후의 차이나 남은 제한 시간을 구하려면 같은 실행에서 역행하지 않는 경과 시계가 유용합니다. 벽시계는 사람이 공유할 수 있는 시점 표현이지만 조정될 수 있고, 단조 시계의 기준점은 보통 달력의 특정 날짜가 아닙니다.
단조 값 20.5를 1970년 이후 20.5초라고 표시하면 안 됩니다. 같은 시계에서 얻은 값끼리의 차이에 의미가 있으며 시스템 간 임의의 값 비교는 별도 정의가 필요합니다. 로그에는 wallTimestamp와 elapsed 같은 필드를 나눠 두면 둘을 실수로 더하거나 빼는 일을 줄일 수 있습니다. Node와 브라우저의 performance 계열도 시간 원점을 따로 갖습니다. 이 글의 Python 결과를 다른 프로세스나 서버의 performance.now와 직접 연결한 시험으로 읽지 않습니다.
벽시계가 뒤로 이동하는 수열을 주입했습니다.
가상 벽시계는 1000.0, 1000.2, 998.5초이며 같은 단계의 단조 시계는 20.0, 20.2, 20.5초입니다. 처음과 끝을 빼면 벽시계 경과는 −1.5초, 단조 경과는 0.5초입니다. 이 숫자는 실제 운영체제 조정을 관측한 기록이 아니라 함수에 넣기 위해 만든 고정 수열입니다. 시계 선택의 영향을 반복 가능하게 설명하려는 입력입니다.
음수가 나오면 무조건 0으로 잘라 쓰는 방식은 근본 해결이 아닙니다. 오류를 숨기면서 실제 0.5초의 소비도 잃기 때문입니다. 예외 신호를 기록하거나 측정에 적합한 시계로 바꿔야 합니다. 벽시계를 이벤트의 날짜로 보존하는 목적은 그대로 남기고, 경과 시간에는 다른 기준을 사용합니다. 시스템에서 시간이 왜 조정됐는지, 실제 장애나 사용자 영향이 있었는지 판단하려면 별도 로그가 필요하며 이 예제는 그런 원인 분석을 하지 않았습니다.
앞으로 튄 벽시계도 작업을 느려 보이게 합니다.
두 번째 가상 사례는 벽시계 입력 쌍 [1000, 1005.5]와 단조 입력 쌍 [20.0, 20.5]를 따로 넣습니다. 코드는 각각 끝에서 시작을 빼 5.5초와 0.5초를 계산하고, 그 결과를 예산 잔여 계산에 사용합니다. 뒤로 가는 경우만 방어하면 앞으로 조정됐을 때의 과대 측정을 놓칩니다. 음수가 아니므로 정상적인 지연으로 받아들이기 쉽지만, 차이 5초는 이 수열의 시계 이동에 의해 생겼습니다. 경과 값이 양수라는 사실만으로 올바른 측정이라고 볼 수 없습니다.
여기서도 실제 서버가 5초 빨라졌다거나 시스템 시간이 잘못됐다는 경험담은 없습니다. 두 시계의 가상 입력을 같이 보여 줘 어떤 산술이 영향을 받는지 확인합니다. 운영 환경에서는 단조 시계가 제공되는지, 절전·가상화·플랫폼별 동작이 목적에 맞는지를 해당 런타임 문서에서 확인해야 합니다. 초보다 미세한 단위를 반환한다고 장치의 정확도까지 그만큼 좋아지는 것도 아닙니다. 표현 정밀도, 시계 해상도와 실제 정확도를 구분해서 설명하세요.
제한 시간을 매 단계 새로 주면 총 예산이 늘어납니다.
가상 작업의 전체 예산을 0.4초로 정했습니다. 단조 시작 20.0을 기준으로 계산한 남은 값은 처음 0.4, 중간 약 0.2, 끝 약 −0.1초입니다. 마지막에는 예산을 초과했으므로 다음 작업을 계속하지 않는 판단이 가능합니다. 부동소수점 때문에 JSON에는 0.2나 −0.1이 긴 소수로 보일 수 있지만 표시를 반올림하더라도 비교에 쓰는 원시 값은 유지합니다.
각 단계마다 무조건 0.4초를 새로 부여하면 전체 작업이 0.4초 안에 끝난다는 계약과 다릅니다. 한 번 정한 시작 또는 마감점에서 현재 단조 값을 빼 남은 예산을 전달해야 합니다. 이 코드는 실제 네트워크 타임아웃을 설정하지 않았고 주입 수열의 예산 산술만 확인했습니다. 재시도·대기열·취소와 자원 정리는 별도 구현입니다. 제한 시간이 지났다는 숫자를 기록한 것과 실행 중인 작업을 안전하게 중단한 것은 같은 기능이 아닙니다.
예산을 절대 단조 마감점으로 쓰면 시작 20.0+0.4=20.4이며, 매 단계 20.4−현재 값만 계산하면 됩니다. 시작을 매번 다시 정하거나 중간 단계에서 벽시계로 바꾸지 않습니다. 제한 시간보다 긴 작업을 호출하기 전에 남은 예산이 0 이하인지 확인하고, 하위 API에 전달할 단위가 초인지 밀리초인지 점검합니다. 부동소수점의 작은 표시 차이를 없애려고 미리 반올림하면 경계 판단이 바뀔 수 있으므로 표시와 판정을 분리합니다.
벽시계로 남은 예산을 계산한 반례를 함께 둡니다.
뒤로 이동한 벽시계의 −1.5초 경과를 0.4초 예산에서 빼면 남은 시간이 1.9초로 늘어납니다. 실제 가상 단조 흐름에서는 이미 0.5초를 사용했는데 오히려 더 기다리게 되는 계산입니다. 앞으로 이동한 사례에서는 0.4−5.5=−5.1초가 되어 실제 소비보다 큰 초과로 보입니다. 같은 예산이라도 측정 기준에 따라 실행 결정을 다르게 만들 수 있습니다.
절대 마감 시각이 필요한 예약과 상대 제한 시간은 여기서도 구분합니다. “내일 오전 10시”는 달력과 시간대의 질문이고 “지금부터 400밀리초 안”은 경과 시간의 질문입니다. 단조 시계로 내일 날짜를 직접 표시하거나 벽시계만으로 모든 상대 예산을 관리하는 식으로 합치지 않습니다. 두 기준을 연결해야 하는 API라면 연결 순간과 변환 규칙을 명시합니다. 이 예제에서는 시스템의 일정을 등록하거나 알림·이메일·결제를 실행하지 않았습니다.
| 주입 시나리오 | 벽시계 경과 초 | 단조 경과 초 | 벽시계 예산 잔여 초 |
|---|---|---|---|
| 뒤로 이동 | -1.5 | 0.5 | 1.9 |
| 앞으로 이동 | 5.5 | 0.5 | -5.1 |
| 올바른 단조 기준 | 벽시계 사용 안 함 | 0.5 | -0.1에 해당 |
실제 실행에서는 고정 해시 작업 한 번을 따로 쟀습니다.
가상 수열만으로 실제 런타임 API를 호출했다고 주장하지 않기 위해 코드에는 작은 SHA-256 계산 한 번을 perf_counter_ns 전후에 넣었습니다. 반복한 고정 바이트 열의 해시와 실제 경과 나노초를 JSON에 남깁니다. elapsed가 음수가 아닌지와 get_clock_info에서 해당 시계가 monotonic인지 확인했습니다. 이 측정은 컴퓨터의 성능 점수나 다른 언어와의 속도 비교가 아닙니다.
해시 계산 시간은 실행 환경의 부하, 캐시와 런타임 등에 따라 달라지므로 독자가 같은 숫자를 얻어야 통과하는 검사가 아닙니다. 결과 파일에는 당시 실행에서 얻은 원시 값을 보존하되 본문에서 특정 시간을 일반 속도로 단정하지 않습니다. 독립 재실행에서는 고정 입력 해시와 구조·비음수 판정을 비교하고 경과 값은 달라질 수 있다고 봅니다. 같은 일을 여러 번 쟀을 때의 분포, 준비 단계와 계측 오버헤드를 분석한 정식 벤치마크는 수행하지 않았습니다.
가상 시나리오의 고정 결과와 실제 API 관측은 JSON에서도 분리했습니다. simulatedWallSeconds와 forwardJump, deadlineBudget은 독자가 어떤 컴퓨터에서 실행해도 같은 입력에 대한 같은 산술을 보여 줍니다. realMeasurement의 perfCounterElapsedNs만 고정 해시 작업을 실제 계측한 값이라 다시 실행할 때 달라집니다. 경과 숫자 자체를 정답으로 비교하지 않고 비음수 여부와 고정 입력의 SHA-256을 확인하세요. 이 분리는 시계 이동 시나리오를 실제 OS에서 발생시켰다고 잘못 읽지 않게 하는 장치입니다.
단위와 정밀도를 바꾸는 단계에서도 정보를 보존합니다.
perf_counter_ns는 정수 나노초 값으로 반환하므로 밀리초로 보여 주려면 차이를 1,000,000으로 나눕니다. 큰 절대 값을 먼저 소수로 바꿔 뺄지, 정수 차이를 구해 바꿀지에 따라 정밀도 손실을 고려해야 할 수 있습니다. 이 글의 코드는 정수 끝−시작 차이를 먼저 남깁니다. 나노초라는 단위가 실제 시계의 모든 값을 나노초 정확도로 측정했다는 뜻은 아니므로 resolution과 API 설명을 함께 읽습니다.
음수·비정상 값이나 다른 프로세스의 기준점을 섞은 입력을 발견했을 때 단위만 고쳐 덮어쓰지 말고 원본과 변환 경로를 남깁니다. 로그에는 시작·끝 원시 값, 사용한 API, 단위, 런타임과 실험 범위를 두는 편이 좋습니다. 사람의 작업 시간을 잴 때 컴퓨터가 잠들어 있던 기간을 포함해야 하는지 같은 선택도 필요합니다. 단조성 하나가 모든 운영 요구를 대신하지 못한다는 점을 계측 명세에서 드러내야 합니다.
보고서에는 가상 시나리오와 실제 측정을 분리합니다.
정확한 요약은 “주입한 시계 이동 수열에서 벽시계 뺄셈은 −1.5초 또는 5.5초였고 단조 차이는 0.5초였다. 실제 고정 해시 작업은 Python 단조 시계로 한 번 계측했다”입니다. 운영체제의 시계를 직접 바꿔 복구했다거나 실제 서비스 장애를 재현했다는 주장은 넣지 않습니다. 가상 시나리오가 보여 주는 산술과 실제 API 호출의 범위가 다른 자료이기 때문입니다.
독자에게 필요한 첫 행동은 현재 로그의 duration이 어떤 시계에서 나왔는지 찾는 것입니다. 생성 시각과 경과 값을 같은 열에 저장하고 있다면 필드를 나누고, 타임아웃은 전체 예산을 지키는지 고정 수열로 시험하세요. 여러 작업이 겹쳤을 때의 합산은 시간 구간을 합치는 별도 글의 범위이며, 날짜가 바뀌는 표시는 시간대 글과 함께 봐야 합니다. 측정한 숫자 하나가 많아지는 것보다 그 숫자가 어떤 질문에 답하는지 명확한 것이 중요합니다.
입력과 판정을 다시 확인하려면
이 글의 가상 입력과 코드는 2026년 10월 15일 로컬 Python 3에서 실행했습니다. 글의 발행일과 실행일은 다릅니다. 별도 설치 패키지 없이 Python 표준 라이브러리로 실행할 수 있습니다.
터미널에서 python3 monotonic-wall-clock-timing-20261015.py를 실행하면 JSON을 출력합니다. 사용자 자료나 비밀번호는 필요하지 않습니다. 입력과 실행 환경은 결과 JSON에 함께 남겼습니다.
계산 기준을 확인한 원문과 다음 작업
- Python — time — monotonic·perf_counter와 시계 정보 API의 범위를 확인했습니다.
- Node.js — Performance APIs — performance.now와 시간 원점의 정의를 확인했습니다.
- W3C — High Resolution Time — 단조 시간과 벽시계의 관계·시간 원점의 정의를 확인했습니다.
공식 원문은 2026-10-15에 확인했습니다. 문서가 설명하는 API·정의와 이 글의 가상 계산 결과는 구분해서 읽어 주세요.



