시간은 프로그램에서 가장 익숙한 의존성처럼 보인다. now()를 호출하고 두 시각을 비교하면 예약이 만료됐는지, 세션이 유효한지, 재시도할 때가 됐는지를 알 수 있다고 생각하기 쉽다. 그러나 운영 환경에서는 머신의 시계가 서로 다르고, 시스템 시각이 보정으로 앞뒤로 움직이며, 스케줄러가 늦거나 중복 실행된다. 시간은 단순한 값이 아니라 설계해야 할 경계다.

원문: Beyond Happy Path Engineering: Time

시간은 하나의 도구가 아니다

먼저 코드가 묻는 질문을 분리해야 한다.

  • 얼마나 오래 걸렸는가? → monotonic time
  • 사용자가 언제 행동했는가? → wall-clock time
  • 어떤 변경이 먼저인가? → logical time 또는 버전·시퀀스
  • 어느 업무일에 속하는가? → business time

경과 시간을 재는 timeout과 사용자에게 표시할 발생 시각은 같은 시간 타입으로 처리하면 안 된다. 전자는 시계가 조정돼도 앞으로만 흐르는 성질이 중요하고, 후자는 사람이 이해할 수 있는 달력과 시간대가 중요하다.

clock skew는 정상적인 운영 조건이다

예약이 10시 15분에 만료된다고 하자. 결제 요청을 처리하는 서버의 시계는 10시 14분이라고 보지만, 만료 worker의 시계는 2분 빠른 10시 16분일 수 있다. 두 서버가 같은 데이터를 읽어도 서로 다른 결론을 낸다.

시간 동기화는 오차를 줄이지만 정확성의 증명이 아니다. 따라서 만료 worker는 timestamp를 후보를 찾는 필터로 사용하고, 실제 상태 변경 때는 다음 조건을 원자적으로 확인해야 한다.

  1. 아직 예약 상태인가?
  2. 결제나 완료 처리가 이미 반영되지 않았는가?
  3. 이 상태 전이가 현재 시점에 허용되는가?

경계에 가까운 경우에는 grace period, pending 상태, 재조정 작업을 두는 편이 안전하다.

타임스탬프는 순서를 보장하지 않는다

분산 환경에서 두 이벤트의 timestamp가 어느 쪽이 먼저인지 알려주지 못하는 경우가 많다. 네트워크 지연, 서로 다른 시계, 재시도 때문에 실제 인과관계와 기록 시각의 순서가 달라질 수 있기 때문이다.

경쟁하는 갱신의 승자를 정해야 한다면 버전 번호, 데이터베이스 transaction, stream sequence, 명시적인 상태 머신을 사용해야 한다. timestamp는 감사 로그와 관찰을 위한 유용한 정보지만, 단독으로 “이 이벤트가 반드시 먼저 발생했다”는 증거가 되어서는 안 된다.

스케줄러는 제때 한 번만 실행되지 않는다

백그라운드 작업은 늦게 실행되거나, 장애 복구 후 밀린 작업을 한꺼번에 실행하거나, 중복 실행될 수 있다. 따라서 “정각에 한 번 실행된다”는 가정 대신 다음 성질을 설계해야 한다.

  • 작업을 여러 번 실행해도 결과가 안전한가?
  • 늦게 도착한 작업이 최신 상태를 덮어쓰지 않는가?
  • 놓친 구간을 재처리할 수 있는가?
  • 작업이 현재 상태를 다시 확인하는가?

이 관점에서 expiration job은 과거에 만들어진 결정을 무조건 실행하는 명령이 아니라, 현재 상태를 확인한 뒤 가능한 상태 전이를 시도하는 worker가 된다.

비즈니스 시간은 timestamp보다 풍부하다

“오늘”, “자정”, “영업일”, “청구 월”, “배송 마감”은 UTC timestamp 하나로 충분히 표현되지 않는다. 사용자의 시간대, 매장의 위치, 서머타임, 공휴일, 월말 규칙이 함께 필요하다.

저장할 때는 명확한 instant와 시간대 정보를 보존하고, 화면이나 업무 판단에 사용할 때는 도메인의 달력 규칙을 적용해야 한다. 특히 서머타임 전환일에는 존재하지 않는 시각이나 두 번 나타나는 시각이 생길 수 있으므로 경계 입력을 별도로 테스트해야 한다.

실무 설계 원칙

시간 관련 코드는 다음 질문을 명시하면 훨씬 견고해진다.

  • 이 값은 instant인가, duration인가, calendar rule인가?
  • 이 비교가 clock skew를 견딜 수 있는가?
  • 늦거나 중복 실행돼도 같은 결과를 만드는가?
  • 상태 변경은 버전 또는 조건부 갱신으로 보호되는가?
  • 운영자가 시계 이상, 지연, 재조정을 관찰할 수 있는가?

테스트도 정상적인 시간 흐름만 확인해서는 부족하다. 시계가 뒤로 움직이는 경우, worker가 늦게 실행되는 경우, 경계 시각에 두 작업이 경쟁하는 경우, 시간대와 서머타임이 바뀌는 경우를 가짜 시계와 통제된 스케줄러로 재현해야 한다.

결론 및 시사점

한 줄 결론: 시간은 단순한 timestamp가 아니라 경과 측정·사건 기록·순서 결정·비즈니스 규칙을 나눠 다뤄야 하는 시스템 경계다.