JavaScript Temporal API – 9년 만에 Date를 대체하는 새 표준

  • Post category:기술

3줄 요약

1. Temporal API가 TC39 Stage 4에 도달하며 ES2026 표준에 정식 포함된다. ECMAScript 역사상 가장 큰 단일 추가(약 4,500개 테스트)다.
2. 30년간 개발자를 괴롭혀 온 Date 객체의 가변성, 0-based 월, 타임존 미지원 문제를 근본적으로 해결한다.
3. Chrome 144, Firefox 139, Edge 144에서 이미 사용 가능하며, Temporal.PlainDate, Temporal.ZonedDateTime 등 용도별 8개 타입을 제공한다.

결론부터 말하면

JavaScript의 Date 객체는 1995년 Java에서 급하게 포팅된 이후 30년간 변하지 않았다. Temporal API는 불변 객체, IANA 타임존 네이티브 지원, 나노초 정밀도, 용도별 분리된 타입을 제공하며 이 문제를 완전히 해결한다. 2026년 3월 Stage 4에 도달했고, 주요 브라우저에서 이미 지원 중이다.

Temporal API란?

Temporal은 JavaScript의 Date 객체를 대체하기 위해 설계된 새로운 날짜/시간 API다. TC39(ECMAScript 표준 위원회)에서 2017년 Stage 1으로 시작해 9년간 개발되었으며, 2026년 3월 Stage 4에 도달하면서 ES2026 표준에 포함이 확정됐다.

Date 객체 하나로 모든 날짜/시간 개념을 처리하던 기존 방식 대신, Temporal은 상황에 맞는 8개의 독립된 타입을 제공한다. 모든 객체는 불변(immutable)이며, 시간대 처리가 내장되어 있다.

Bloomberg, Google, Mozilla, Microsoft, Igalia 등이 공동으로 개발에 참여했으며, Test262 기준 약 4,500개의 테스트가 추가되어 ECMAScript 역사상 가장 큰 규모의 기능 추가다.

기존 Date 객체의 문제점

Date가 왜 교체 대상이 되었는지 구체적으로 살펴보자.

가변성(Mutability)

const meeting = new Date(2026, 2, 15, 14, 0); // 3월 15일

scheduleMeeting(meeting);

// meeting 객체가 함수 내부에서 변경되었을 수 있다
console.log(meeting); // 예측 불가

Date 객체는 setMonth(), setDate() 등으로 원본이 직접 변경된다. 여러 곳에서 같은 객체를 참조하면 의도치 않은 버그가 발생한다.

0-based 월 인덱싱

new Date(2026, 0, 1);  // 1월 1일 (0 = 1월)
new Date(2026, 11, 1); // 12월 1일 (11 = 12월)

1월이 0이고 12월이 11이다. 이 설계는 off-by-one 에러의 대표적 원인이다.

브라우저별 파싱 차이

new Date("2026-01-01"); // 어떤 브라우저는 UTC로, 다른 브라우저는 로컬 시간으로 해석

같은 문자열이 환경에 따라 다른 결과를 반환한다. 날짜 문자열 파싱 규칙이 명확하지 않기 때문이다.

타임존 지원 부재

const date = new Date();
date.getTimezoneOffset(); // 오프셋만 반환, IANA 타임존 이름은 없음

Date는 타임존이 아닌 오프셋만 지원한다. “Asia/Seoul”이나 “America/New_York” 같은 IANA 타임존을 다룰 방법이 없다. DST(서머타임) 전환도 제대로 처리하지 못한다.

날짜 전용 타입 없음

생일처럼 시간 정보가 불필요한 경우에도 Date는 항상 시간과 타임존 정보를 포함한다. “3월 15일”이라는 단순한 날짜를 표현할 깔끔한 방법이 없다.

Temporal의 8가지 타입

Temporal은 용도에 따라 정확한 타입을 선택해서 사용한다.

타입설명사용 예시
PlainDate날짜만 (시간 없음)생일, 공휴일
PlainTime시간만 (날짜 없음)알람, 영업시간
PlainDateTime날짜 + 시간 (타임존 없음)로컬 이벤트
ZonedDateTime날짜 + 시간 + 타임존글로벌 서비스, 항공편
InstantUTC 절대 시점서버 로그, 타임스탬프
Duration시간 간격경과 시간, 타이머
PlainYearMonth연월월별 청구, 리포트
PlainMonthDay월일기념일, 매년 반복 이벤트

핵심 코드 예시

PlainDate — 시간대 없는 날짜

// 생성
const birthday = Temporal.PlainDate.from("2026-03-15");
const today = Temporal.Now.plainDateISO();

// 불변 연산 — 원본은 변하지 않는다
const nextWeek = today.add({ days: 7 });
console.log(today.toString()); // 원본 그대로
console.log(nextWeek.toString()); // 7일 후

// 두 날짜 사이의 간격
const diff = today.until(birthday);
console.log(diff.days); // 남은 일수

ZonedDateTime — 타임존 포함 날짜/시간

// 서울과 뉴욕의 같은 '순간'
const seoul = Temporal.ZonedDateTime.from(
  "2026-03-15T09:00:00+09:00[Asia/Seoul]"
);

const newYork = seoul.withTimeZone("America/New_York");

console.log(seoul.toString()); // "2026-03-15T09:00:00+09:00[Asia/Seoul]"
console.log(newYork.toString()); // "2026-03-14T19:00:00-05:00[America/New_York]"

// DST 자동 처리
const london = Temporal.ZonedDateTime.from(
  "2026-03-29T00:30:00+00:00[Europe/London]"
);

const plus1h = london.add({ hours: 1 }); // "2026-03-29T02:30:00+01:00[Europe/London]"
// 01:30은 DST 전환으로 존재하지 않으므로 자동 건너뜀

Instant — 절대 시점

// 서버 이벤트 기록
const eventTime = Temporal.Now.instant();
console.log(eventTime.epochMilliseconds); // Unix 타임스탬프 (ms)

// 특정 시점을 각 시간대로 변환
const instant = Temporal.Instant.from("2026-03-15T00:00:00Z");
const inSeoul = instant.toZonedDateTimeISO("Asia/Seoul");
const inTokyo = instant.toZonedDateTimeISO("Asia/Tokyo");
console.log(inSeoul.hour); // 9 (UTC+9)
console.log(inTokyo.hour); // 9 (UTC+9)

Duration — 시간 간격 계산

const projectStart = Temporal.PlainDate.from("2026-01-10");
const deadline = Temporal.PlainDate.from("2026-03-31");
const remaining = projectStart.until(deadline);

console.log(remaining.toString()); // "P80D" (80일)

// Duration을 다른 단위로 변환
const duration = Temporal.Duration.from({ hours: 130, minutes: 20 });

console.log(duration.total({ unit: "second" })); // 469200

Date vs Temporal 비교

항목DateTemporal
불변성가변 (원본 변경됨)불변 (새 객체 반환)
월 인덱스0부터 시작 (0 = 1월)1부터 시작 (1 = 1월)
타임존오프셋만 지원IANA 타임존 네이티브 지원
정밀도밀리초나노초
타입단일 타입용도별 8개 타입
DST 처리수동자동
달력그레고리력만히브리력, 이슬람력 등 지원
파싱브라우저마다 다름엄격하고 일관된 규칙

브라우저 지원 현황 (2026년 3월 기준)

브라우저버전지원 상태
Firefox139+지원 (2025년 5월~)
Chrome144+지원 (2026년 1월~)
Edge144+지원 (2026년 1월~)
SafariTechnology Preview부분 지원 (플래그 필요)
Node.jsv26예정
TypeScript6.0 Beta타입 정의 지원 (2026년 2월~)

Safari를 제외한 주요 브라우저에서 이미 사용 가능하다. Safari도 Technology Preview에서 테스트할 수 있으며, 정식 지원은 2026년 내 예상된다.

프로덕션 환경에서 모든 브라우저를 지원해야 한다면 @js-temporal/polyfill 또는 temporal-polyfill을 사용할 수 있다.

npm install @js-temporal/polyfill
import { Temporal } from "@js-temporal/polyfill";

const now = Temporal.Now.plainDateISO();

console.log(now.toString()); // 폴리필로 모든 환경에서 동일하게 동작

기존 코드 마이그레이션 가이드

Date에서 Temporal로 전환할 때 자주 쓰는 패턴을 정리했다.

// (Before) Date — 현재 날짜
const today = new Date();
const year = today.getFullYear();
const month = today.getMonth() + 1; // 0-based 보정 필요

// (After) Temporal — 현재 날짜
const today = Temporal.Now.plainDateISO();
const year = today.year;
const month = today.month; // 1-based, 보정 불필요
// (Before) Date — 날짜 비교
const d1 = new Date("2026-03-15");
const d2 = new Date("2026-04-01");
const diffMs = d2 - d1;
const diffDays = Math.floor(diffMs / (1000  60  60 * 24)); // 직접 계산

// (After) Temporal — 날짜 비교
const d1 = Temporal.PlainDate.from("2026-03-15");
const d2 = Temporal.PlainDate.from("2026-04-01");
const diff = d1.until(d2);

console.log(diff.days); // 17 — 내장 메서드로 바로 계산
// (Before) Date — 타임존 변환
// 외부 라이브러리(moment-timezone, date-fns-tz) 필수

// (After) Temporal — 타임존 변환
// 외부 라이브러리 없이 네이티브로 처리
const seoul = Temporal.Now.zonedDateTimeISO("Asia/Seoul");
const london = seoul.withTimeZone("Europe/London");

Moment.js, date-fns는 이제 필요 없나?

State of JS 조사에서 날짜 처리는 “정적 타입 부족” 다음으로 두 번째 고통 포인트로 꼽혔다. 이 문제를 해결하기 위해 Moment.js(2011), date-fns, Luxon 같은 라이브러리가 등장했고, 현재 npm에서 주당 8천만 다운로드를 기록하고 있다.

Temporal이 표준화되면서 이 라이브러리들의 핵심 기능(타임존 처리, 불변 연산, 포매팅)이 네이티브로 대체된다. 다만 Moment.js의 상대 시간 표현(3일 전)이나 date-fns의 풍부한 유틸리티 함수처럼 Temporal이 직접 제공하지 않는 편의 기능도 있다.

당장 기존 프로젝트의 라이브러리를 걷어낼 필요는 없지만, 새 프로젝트에서는 Temporal 네이티브로 시작하는 것이 번들 크기와 유지보수 측면에서 유리하다.

9년의 여정 — 주요 마일스톤

시기사건
1995년Brendan Eich가 Java의 java.util.Date를 JavaScript로 포팅
2011년Moment.js 등장, Date의 한계를 라이브러리로 우회하기 시작
2017년Maggie Johnson-Pint(Moment.js 관리자)가 TC39에 Temporal 제안, Stage 1
2021년Stage 2.7 — 설계 확정
2023년Stage 3 — 브라우저 구현 시작
2024년temporal_rs(Rust 기반 공유 엔진) 개발, Google + Boa 협업
2025년 5월Firefox 139, 최초로 Temporal 정식 지원
2026년 1월Chrome 144, Edge 144 지원
2026년 3월TC39 Stage 4 도달 — ES2026 표준 확정

Bloomberg의 금융 시스템에서 나노초 단위의 시간 정밀도와 IANA 타임존 지원이 필요했던 것이 Temporal 개발의 핵심 동력이었다. Bloomberg 엔지니어 Philipp Dunkel이 스펙 챔피언을, Jason Williams가 TC39 위원으로 표준화를 이끌었다.

참고 자료

답글 남기기