기술 가이드

IoT 로그 시간 설계: UTC·단조 시계·재부팅 순서를 함께 기록하기

시각 보정과 오프라인 전송에 대비해 측정 시각·수신 시각·경과 시간을 구분합니다. 가상 시계와 UTC·KST 변환을 Python으로 확인합니다.

목차

IoT 장치가 재부팅 후 과거 날짜로 로그를 남기거나, 시간 동기화 직후 응답 시간이 음수가 되는 경우가 있습니다. 측정값 자체는 맞아도 시간을 해석하는 기준이 섞이면 장애 순서와 지연 원인을 판단하기 어렵습니다.

이 글은 장치·게이트웨이 로그에서 절대 시각, 경과 시간과 이벤트 순서를 함께 기록하는 방법을 설명합니다. 코드는 Python 3.12 표준 라이브러리를 기준으로 작성했으며, 실제 시스템 시계를 변경하지 않습니다.

UTC 시각과 경과 시간의 역할 나누기

UTC 시각은 서로 다른 시스템의 로그를 대조할 때 사용합니다. 경과 시간은 같은 시계에서 두 지점 사이의 차이를 구할 때 사용합니다. 시스템 날짜를 나타내는 시계는 관리자 설정이나 동기화 과정에서 보정될 수 있으므로 타임아웃 계산에 그대로 쓰면 문제가 생길 수 있습니다.

Python의 time.monotonic()은 뒤로 가지 않는 시계이며 시스템 시각 갱신의 영향을 받지 않습니다. 반환값의 기준점은 정의되어 있지 않아 두 호출 사이의 차이를 사용해야 합니다. 정수 나노초가 필요하면 time.monotonic_ns()를 사용할 수 있습니다. Python 3.12 time 문서

단조 시계의 값을 다른 장치의 값과 빼서 전송 지연을 구해서는 안 됩니다. 또한 이름만 보고 MCU의 부팅 후 시간과 Python 단조 시계의 원점이 같다고 가정하지 않습니다. 시스템 절전 시간을 포함하는지는 대상 플랫폼과 사용 API에서 별도로 확인합니다.

로그에 남길 필드 정하기

아래는 자체 로그 형식의 예입니다. 한 필드에 모든 시간 의미를 넣기보다 각 값의 기준과 단위를 문서화합니다.

필드 예시 또는 의미
device_id 장치 식별자
boot_id 재부팅을 구분하는 식별자
seq 같은 부팅 안의 이벤트 순번
device_time_utc 측정 당시 장치가 판단한 UTC 시각
clock_quality 미동기화·동기화·유지 상태 등 자체 정의
uptime_ms 장치 부팅 기준의 경과 시간
received_at_utc 서버가 수신한 UTC 시각

장치 시각을 모르면 null과 미동기화 상태를 저장합니다. 수신 시각을 측정 시각인 것처럼 대신 넣으면 오프라인으로 쌓인 로그가 실시간 측정처럼 보일 수 있습니다. 필요하면 마지막 동기화 이후 경과 시간과 추정 오차도 별도로 기록합니다.

예를 들어 장치가 10분간 연결을 잃었다가 한꺼번에 전송하면 received_at_utc는 거의 같을 수 있습니다. 이때 원래 수집 순서는 부팅 식별자와 이벤트 순번으로 복원하고, 시각의 신뢰도는 동기화 상태를 보고 판단합니다.

시계가 뒤로 보정되는 상황 재현하기

다음 코드는 가상의 두 관측값을 비교합니다. 컴퓨터의 시계나 NTP 설정을 건드리지 않습니다.

from datetime import datetime, timezone

wall_start = datetime(2026, 9, 13, 0, 0, 10, tzinfo=timezone.utc)
wall_end = datetime(2026, 9, 13, 0, 0, 8, tzinfo=timezone.utc)
mono_start_ns = 1_000_000_000
mono_end_ns = 1_250_000_000

print("wall seconds:", (wall_end - wall_start).total_seconds())
print("elapsed ms:", (mono_end_ns - mono_start_ns) / 1_000_000)

출력은 wall seconds: -2.0, elapsed ms: 250.0입니다. 달력 시각은 2초 뒤로 움직였지만 예제의 경과 시간은 250ms입니다. 실제 프로그램에서는 작업 전후의 단조 시계 값을 읽어 차이를 저장하고, UTC 시각은 로그 검색용으로 함께 남깁니다.

이 예제는 시각이 갑자기 바뀌는 경우를 단순화한 것입니다. 모든 시간 동기화가 시계를 뒤로 뛰게 만드는 것은 아닙니다. 시스템은 상태와 설정에 따라 점진적으로 보정할 수도 있습니다.

UTC와 KST를 표시 단계에서 변환하기

시간대 정보가 없는 문자열 2026-09-13 09:00:00만 저장하면 UTC인지 한국 시각인지 구분하기 어렵습니다. API와 저장 형식에 시간대 또는 UTC 오프셋을 명시하고, 화면에서 필요한 시간대로 변환합니다.

from datetime import datetime, timezone, timedelta

event = datetime.fromisoformat("2026-09-13T00:00:00+00:00")
kst = timezone(timedelta(hours=9))
print(event.astimezone(kst).isoformat())

결과는 2026-09-13T09:00:00+09:00입니다. 이미 존재하는 시각을 변환할 때는 astimezone()을 사용합니다. 시간대 없는 값을 무조건 UTC라고 붙이는 것은 실제 변환과 다르므로, 원래 입력이 어떤 기준인지 먼저 확인합니다. Python datetime의 시간대 설명

장애 순서를 해석하는 운영 규칙

같은 장치·같은 부팅에서는 이벤트 순번을 우선 사용하고, 순번 누락과 중복을 표시합니다. 재부팅을 넘는 순서는 부팅 식별자만 사전순으로 정렬해서 결정하지 않습니다. 서버가 기록한 연결 이력과 장치의 시각 품질을 함께 살펴봅니다.

여러 장치 사이의 사건 순서는 UTC만으로 확정하기 어려울 수 있습니다. 제어 요청 ID를 응답과 함께 남기면 어느 요청 때문에 어떤 동작이 발생했는지 연결할 수 있습니다. 동기화 오차 범위 안의 두 사건은 순서가 불확실하다고 표현하는 편이 정확합니다.

운영 전에는 미동기화 부팅, 오프라인 누적 전송, 시각 보정, 장치 재부팅을 각각 재현해 봅니다. 타임아웃이 비정상적으로 늘어나지 않는지, 과거 로그가 최신 경보를 덮어쓰지 않는지, 단위가 밀리초와 초 사이에서 섞이지 않는지를 확인합니다. 이벤트 중복 저장은 MQTT 이벤트 ID 설계 글과 함께 검토할 수 있습니다.

관련 글

처리 중입니다...

잠시만 기다려주세요.