여는 글 — 새벽 2시가 두 번 온 날

만화로 보는 요약 — 먼저 읽어보세요
새벽 두 번의 2시 → 시간은 하늘·철도·원자가 차례로 정함 → 분산 시스템에서는 다시 어긋남.
면접 실전 질문: ① DST가 같은 지역 시각을 두 번 만들 수 있는 이유는? ② 벽시계와 단조 시계의 역할은 어떻게 다른가? ③ 스케줄러 작업을 중복 실행으로부터 어떻게 지킬까?
이 책은 시간을 “그냥 now() 찍으면 되는 것”으로 다뤄온 백엔드 개발자를 위해, 시간이 하나로 합의되지 않는 이유를 역사와 코드로 함께 따라간다.
당직 폰이 울린 건 새벽이었다. 정산 배치가 실패해서가 아니다. 실패였다면 차라리 쉬웠다. 문제는 로그에 같은 배치가 두 번 찍혀 있다는 거였다. 어젯밤 02:00에 한 번, 그리고 한 시간 뒤 다시 02:00에 한 번. 잡 이름도 같고 실행 조건도 같은데, 실행 ID만 다른 두 줄이 나란히 앉아 있었다.
정산 배치는 고객 계좌에서 돈을 빼가는 잡이다. 로그가 두 번이면, 계좌에서도 두 번 빠져나갔다. 같은 금액이, 같은 고객에게서, 정확히 같은 조건으로 두 번.
재시도는 아니었다. 스케줄러가 이중으로 떠 있지도 않았다. 서버를 아무리 뒤져도 원인이 안 보였다 — 배치는 정확히 스케줄대로, 정확히 한 번씩, 두 번 돌았을 뿐이다. 그러고 보니 그날 밤은 유독 길었다. 자정부터 다음 자정까지 다시 맞춰보니, 하루가 24시간이 아니라 25시간이었다.
원인은 한참 뒤에야 밝혀졌다. 그리고 그 원인은 코드 한 줄의 실수가 아니었다.
이 책이 결국 하려는 이야기는 이거다. 시간은 처음엔 하늘이 정했다. 해시계 그림자가 정오를 가리켰고, 계절이 돌아오는 주기가 한 해의 길이를 정했다. 다음엔 나라가 정했다 — 기차가 마을과 마을을 잇기 시작하자, 마을마다 다르던 정오가 표준시 하나로 묶였다. 다음엔 원자시계가 정했다 — 지구의 자전조차 흔들린다는 걸 알게 되자, 인류는 세슘 원자의 진동을 초의 기준으로 삼았다.
그런데 분산 시스템에서는 — 아무도 정하지 못한다. 서버마다 시계가 조금씩 어긋나고, 벽시계 하나가 가리키는 시각조차 어느 순간엔 존재하지 않거나 두 번 존재한다. 그 정산 배치가 두 번 돈 그 밤도, 그 오래된 문제의 최신 버전이었을 뿐이다.
이 책은 그 배치가 왜 두 번 돌았는지 궁금한 사람을 위해 썼다. 시간을 Instant.now()나
LocalDateTime.now() 한 줄로만 여겨온 백엔드 개발자, 타임존은 그저 설정 파일의 문자열
값 정도로만 알던 개발자를 위한 책이다. 정답부터 외우게 하지 않고, 위의 순서를 그대로
따라간다 — 시간이 어긋나던 자리마다 멈춰 서면서. 장마다 실행 가능한 Java·SQL 코드가
함께 있고, 각 장은 그 장을 한 컷으로 요약한 삽화로 시작한다.
이 책을 다 읽고 나면, 그 새벽의 로그가 왜 두 줄이었는지, 하루가 왜 25시간이 됐는지 정확히 설명할 수 있게 된다 — 6장에서 그 밤을 다시 연다. 지금은, 그 모든 시간 개념이 시작된 자리부터 가보자.