JavaScript Date 계산이 얼마나 크게 틀릴 수 있을까?

발행: (2026년 1월 15일 오전 07:46 GMT+9)
6 min read
원문: Dev.to

Source: Dev.to

문제

2025년 1월, 캘리포니아 주 산타클라라에서 보고서를 만들기 위해 JavaScript 코드를 작성하고 있었습니다. 한 달 안에 발생한 이벤트 수를 구하고 싶어서 해당 월의 첫 번째 날을 나타내는 Date 객체를 만들고, 한 달을 더한 뒤 하루를 빼서 마지막 날을 구하려 했습니다. 직관적이죠?

하지만 정말 이상한 결과가 나왔습니다. 문제를 다음 코드로 최소화했습니다.

const date = new Date("2024-01-01T00:00:00.000Z");
date.toISOString(); // => "2024-01-01T00:00:00.000Z" as expected
date.setMonth(1);
date.toISOString(); // => "2023-03-04-T00:00:00.000Z" WTF?

2024년 1월 1일에 한 달을 더했는데 2023년 3월 4일이 되었습니다. 무슨 일일까요?

시간과 타임존

미국 서부 해안에서 상황을 설정한 것이 이상하게 보일 수도 있지만, 실제로 중요한 요소였습니다. 이 코드는 UTC와 그보다 동쪽 지역에서는 정상적으로 동작했을 것입니다.

JavaScript Date 객체는 단순히 날짜만 저장하는 것이 아니라 시간도 포함합니다. 날짜와 월만 다루고 싶었지만, 시간도 여전히 영향을 미칩니다.

UTC 기준 2024년 1월 1일 자정은 태평양 표준시(UTC‑8)로는 2023년 12월 31일 오후 4시입니다. date.setMonth(1)은 월을 2월(월은 0부터 시작)로 설정합니다. 하지만 우리는 2023년 12월 31일에 시작했기 때문에 JavaScript는 존재하지 않는 2023년 2월 31일을 처리해야 합니다. 이 날짜는 다음 달로 넘쳐서 3월 3일이 되고, 최종적으로 UTC로 변환되면서 2023년 3월 4일 자정이 됩니다.

각 단계는 논리적으로 맞지만, 예상치 못한 결과 때문에 혼란이 생깁니다.

항상 UTC 사용하기

시간에 신경 쓰지 않고 UTC 기준으로 작업하고 싶었으므로 Date 객체의 setUTCMonth 메서드를 사용해 코드를 고쳤습니다. 원래 코드는 마지막 날을 구하기 위해 하루를 빼는 로직이 있었으므로 setUTCDate도 사용했습니다. 모든 set${timePeriod} 메서드에는 대응되는 setUTC${timePeriod} 메서드가 있습니다.

const date = new Date("2024-01-01T00:00:00.000Z");
date.toISOString(); // => "2024-01-01T00:00:00.000Z"
date.setUTCMonth(1);
date.toISOString(); // => "2024-02-01-T00:00:00.000Z"

이렇게 하면 문제가 해결됩니다.

Temporal을 맞이하라

원래 코드가 잘못된 이유 중 하나는 시간날짜를 동시에 조작하면서 이를 고려하지 않았기 때문입니다. Temporal은 타임존이 없는 캘린더 날짜 전용 객체를 제공합니다.

Temporal.PlainDate를 사용하면 캘린더 날짜를 다루기가 훨씬 쉬워집니다. 월이나 일을 직접 설정하거나 밀리초를 더하는 대신, 기간(duration)을 더합니다. Temporal 객체는 불변(immutable)이며, 각 변경은 새로운 객체를 반환합니다.

const startDate = Temporal.PlainDate.from("2024-01-01"); // => Temporal.PlainDate 2024-01-01
const nextMonth = startDate.add({ months: 1 });        // => Temporal.PlainDate 2024-02-01
const endDate   = nextMonth.subtract({ days: 1 });   // => Temporal.PlainDate 2024-01-31

시간을 신경 쓰지 않고 날짜만 조작할 수 있다니—멋지죠!

타임존을 염두에 두세요

Temporal은 JavaScript 엔진에 점차 도입되고 있습니다. 현재(작성 시점) Firefox에서는 이미 사용할 수 있으며 Chrome 144에서도 막 도입되었습니다. Safari Technical Preview에서는 플래그 뒤에 숨겨져 있습니다. 로컬에서 테스트하려면 Firefox나 Chrome을 열거나 다음 폴리필 중 하나를 사용하세요:

아직 Date를 사용해야 한다면 타임존을 항상 염두에 두세요. 지금이라도 Temporal로 옮기거나 최소한 사용하는 방법을 배우는 것을 권합니다. 시간을 피하려고 해도, 타임존은 언제든 머리를 아프게 만들 수 있습니다.

Back to Blog

관련 글

더 보기 »