Temporal은 자바스크립트의 낡은 Date를 대체하기 위해 만들어진 새 날짜·시간 API다. 불변(immutable) 객체, 명확한 타임존 처리, 사람이 읽기 쉬운 메서드로 Date의 고질적인 함정들을 정리했다. TC39 표준화 절차에서 마지막 단계에 있고, 브라우저 구현이 시작되는 중이라 지금은 폴리필로 실무에 쓸 수 있다.
솔직히 Date는 다들 한 번쯤 데였을 거다. 월이 0부터 시작하고, getMonth()가 7월에 6을 돌려주고, 파싱은 브라우저마다 제각각이고, 심지어 객체가 가변이라 setDate 한 번에 원본이 통째로 바뀐다. Temporal은 이 모든 걸 뒤집었다.
가장 큰 차이 세 가지
첫째, 불변이다. 날짜를 더하면 새 객체가 나온다. 원본은 그대로다. 둘째, 월이 1부터 시작한다. 8월은 그냥 8이다. 셋째, 용도별로 타입이 나뉜다. 시각만, 날짜만, 타임존까지 — 상황에 맞는 타입을 골라 쓴다.
// 날짜만 (시간·타임존 없음)
const d = Temporal.PlainDate.from("2026-08-25");
const next = d.add({ days: 10 });
console.log(next.toString()); // 2026-09-04
console.log(d.toString()); // 2026-08-25 (원본 불변!)
// 월이 1부터
console.log(d.month); // 8 (Date였다면 getMonth()가 7)
타입 지도
| 타입 | 담는 것 | 예시 용도 |
|---|---|---|
| Temporal.Instant | 절대 시각(UTC 기준) | 서버 타임스탬프 |
| Temporal.PlainDate | 날짜만 | 생일, 마감일 |
| Temporal.PlainTime | 시각만 | 영업 시작 시간 |
| Temporal.ZonedDateTime | 타임존 포함 날짜·시각 | 회의 일정 |
| Temporal.Duration | 기간(3일 2시간 등) | 차이 계산 |
타임존 계산 — 여기서 진가가 나온다
Date로 "서울 시간 오전 9시가 뉴욕에서는 몇 시?"를 구하려면 라이브러리 없이는 사실상 불가능했다. Temporal은 타임존을 1급 시민으로 다룬다.
const seoul = Temporal.ZonedDateTime.from({
timeZone: "Asia/Seoul",
year: 2026, month: 8, day: 25, hour: 9,
});
const ny = seoul.withTimeZone("America/New_York");
console.log(ny.hour); // 뉴욕 현지 시각으로 변환
기간 차이도 깔끔하다. 두 날짜의 간격을 until로 구하면 Duration이 나오고, 원하는 단위로 반올림까지 지정할 수 있다.
const a = Temporal.PlainDate.from("2026-01-01");
const b = Temporal.PlainDate.from("2026-08-25");
const diff = a.until(b, { largestUnit: "month" });
console.log(diff.months, diff.days); // 7 24
지금 쓰려면 — 폴리필
아직 모든 브라우저에 기본 탑재된 건 아니라서, 실무에서는 폴리필을 얹는다. 대표적으로 @js-temporal/polyfill이 있다.
import { Temporal } from "@js-temporal/polyfill";
const today = Temporal.Now.plainDateISO();
console.log(today.toString());
개인적으로는 신규 코드에서 날짜 로직을 Temporal로 짜되, 외부와 주고받는 경계(DB, API)에서는 ISO 8601 문자열로 직렬화해 넘기는 방식을 추천한다. toString()이 표준 ISO 포맷을 뱉으니 궁합이 좋다. moment나 무거운 날짜 라이브러리를 걷어낼 좋은 명분이기도 하다.
Date와 오갈 때 — 변환 포인트
현실적으로 당분간은 Date와 공존해야 한다. 외부 라이브러리나 브라우저 API가 여전히 Date를 요구하기 때문이다. 다행히 변환 통로가 명확하게 마련돼 있다. 절대 시각을 다루는 Instant에서 Date로, 반대로 Date에서 Instant로 오가는 식이다.
// Date -> Temporal
const legacy = new Date();
const instant = legacy.toTemporalInstant(); // Date에 추가된 메서드
// Temporal -> Date
const back = new Date(instant.epochMilliseconds);
내가 세운 규칙은 단순하다. 애플리케이션 내부 로직은 전부 Temporal로 계산하고, 밖으로 나가는 순간에만 Date나 ISO 문자열로 바꾼다. 이렇게 경계를 딱 그어두면, 월이 0부터 시작하는 함정이나 가변 객체 때문에 생기는 버그가 코드 중심부에서 거의 사라진다. 처음 한 번만 변환 헬퍼를 만들어두면 그다음은 편하다.
자주 묻는 질문
Temporal이 Date를 완전히 대체하나요?
기능적으로는 Date의 거의 모든 용례를 더 안전하게 커버합니다. 다만 Date는 하위 호환을 위해 계속 남아 있고, 기존 API들이 Date 객체를 주고받으므로 경계에서 변환이 필요합니다. 신규 로직은 Temporal로 짜고, 레거시 연동부에서만 Date로 바꿔 넘기는 방식이 현실적입니다.
지금 프로덕션에 써도 되나요?
폴리필을 통해 사용할 수 있습니다. 표준화가 마지막 단계에 있고 API가 안정적이어서 많은 팀이 폴리필로 도입하고 있습니다. 네이티브 지원이 넓어지면 폴리필만 걷어내면 되므로 마이그레이션 부담이 작습니다.
왜 날짜, 시각, 타임존 타입이 따로 있나요?
한 객체에 모든 걸 담으면 "이 값에 타임존이 있나?" 같은 모호함이 생기고 그게 버그의 원천이었기 때문입니다. 생일처럼 시각이 필요 없는 값에 PlainDate를, 회의 일정처럼 타임존이 중요한 값에 ZonedDateTime을 쓰도록 타입으로 강제해 실수를 줄입니다.
day.js나 date-fns 대신 Temporal을 써야 하나요?
Temporal은 표준 API라 별도 라이브러리 의존을 줄일 수 있고, 타임존과 불변성 처리가 표준으로 보장됩니다. 다만 아직 네이티브 지원이 무르익는 중이라 당장은 폴리필 용량이 듭니다. 신규 프로젝트라면 Temporal을 우선 검토하되, 브라우저 지원 요구사항을 함께 따져 결정하면 됩니다.

댓글 0