자바스크립트 Date 때문에 매번 하루씩 어긋나던 걸 Node 26 Temporal로 갈아탔다
Date는 왜 그렇게 불편했나, Temporal은 뭘 고쳤나
#Node #JavaScript #Temporal #날짜
커플 앱에서 사귄 날부터 며칠째인지(D-day)를 계산하는데, 어느 날 보니 하루가 어긋나 있었다. 또 Date였다. 자바스크립트의 Date는 날짜를 다룰 때마다 한 끗씩 사람을 배신한다. Node 26에 Temporal API가 기본으로 들어왔다길래, 이참에 갈아탔다. 이 글에선 Date가 왜 그렇게 불편했는지, Temporal이 그걸 어떻게 고쳤는지 정리해본다. Date는 설계 자체가 낡았다 Date가 불편한 건 내가 방법을 몰라서가 아니라, 설계가 틀려서 였다. 대표적인 함정들이다. 1. 월이 0부터 시작한다 — 1월이 0, 12월이 11이다. new Date(2026, 5, 14)는 6월 14일이다. 이거 하나로 평생 실수한다. 2. mutable(변경 가능)하다 — date.setMonth()를 하면 원본 객체가 바뀐다. 어디선가 날짜를 슬쩍 바꿔놓으면, 그걸 참조하던 다른 곳이 같이 틀어진다. 추적하기 진짜 힘든 버그다. 3. 타임존이 없다 — Date는 사실상 "로컬 시간"이랑 "UTC" 둘만 안다. 다른 나라 시간대를 제대로 다루려면 라이브러리를 따로 끼워야 했다. 4. 파싱이 제멋대로다 — 문자열을 Date로 바꾸는 동작이 환경마다 들쭉날쭉이라, "되는 줄 알았던" 코드가 다른 데서 깨진다. Temporal이 고친 것 Temporal은 이 문제들을 설계 단계에서 다시 짰다. 1. 1월은 1이다 — 사람이 생각하는 그대로다. 2. immutable(불변)이다 — 한 번 만든 객체는 안 바뀐다. 날짜를 더하면 새 객체가 나온다. 원본이 슬쩍 바뀌는 사고가 구조적으로 안 난다. 3. 타임존을 처음부터 제대로 다룬다 — 시간대를 나중에 끼워맞추다 어긋나는 일이 없다. 4. 용도별로 타입이 나뉜다 — 시간대 없는 순수 날짜는 PlainDate, 타임스탬프는 Instant, 시간대까지 붙은 건 ZonedDateTime, "기간"은 Duration. 뭘 다루는지가 타입에 드러나서 헷갈리지 않는다. D-day 계산이 이렇게 깔끔해졌다 예전엔 두 날짜의 차이를 구하려고 밀리초로 빼서 86400000으로 나누고… 하다가 타임존/서머타임에서 하루가 어긋났다. Temporal은 Duration 으로 이걸 정확하게 준다. // 옛날 — 밀리초 빼기. 타임존 끼면 하루씩 틀어짐 const days = Math.floor((today - start) / (1000 * 60 * 60 * 24)); // Temporal — 날짜끼리 빼면 정확한 기간(Duration)이 나온다 const start = Temporal.PlainDate.from('2024-03-01'); const today = Temporal.Now.plainDateISO(); const dday = start.until(today).days; // 며칠째인지 PlainDate끼리 계산하니까 "시간"이 안 끼어든다. 서머타임이든 타임존이든 영향을 안 받는다. D-day처럼 날짜만 중요한 계산 엔 시간 정보가 빠진 PlainDate가 딱이다. Node 26부터는 기본 탑재 그동안 Temporal은 폴리필(따로 설치하는 임시 구현)로만 써야 했는데, Node 26부터 V8 엔진에 기본으로 들어왔다 . 폴리필을 빼도 그냥 Temporal이 있다. 브라우저도 Chrome, Firefox가 따라붙고 있어서, 이제 진짜 쓸 때가 됐다. 정리하면 오래된 API가 불편할 때, 보통은 "내가 잘 못 쓰나" 싶어서 더 파고든다. 근데 Date는 아무리 잘 써도 불편한 , 설계가 틀린 경우였다. 월이 0부터인 것도, 객체가 바뀌는 것도 내 잘못이 아니라 30년 묵은 설계의 문제였다. Temporal로 옮기고 나서 날짜 관련 버그가 눈에 띄게 줄었다. 날짜 계산에서 자꾸 한 끗씩 틀어지는 경험을 해봤다면, Temporal부터 써보는 걸 권한다. "내가 못한 게 아니었구나" 싶을 거다.