JavaScript의 Date 객체는 웹 애플리케이션에서 날짜와 시간을 다루는 핵심적인 도구입니다. 그러나 이 객체의 시간대(Time Zone) 처리 방식은 개발 과정에서 종종 혼란을 야기할 수 있으며, 특히 다양한 형식의 날짜/시간 문자열을 파싱할 때 예상치 못한 결과가 발생하기도 합니다. 이 글에서는 Date 객체의 시간대 동작을 이해하고, 효과적인 시간 처리 및 포맷팅 전략을 제시합니다.
Date 객체의 시간대 파싱 이해하기
new Date() 생성자를 통해 Date 객체를 만들 때, 입력값의 형식에 따라 시간대 해석이 달라집니다.
new Date(): 현재 시스템의 로컬 시간대를 기준으로 하는Date객체를 생성합니다.new Date("YYYY-MM-DDTHH:mm:ss"): ISO 8601 형식으로 시간대 정보가 명시되지 않은 문자열은 일반적으로 로컬 시간대를 기준으로 파싱됩니다.new Date("YYYY-MM-DDTHH:mm:ssZ"): 문자열 끝의Z는 UTC(협정 세계시)를 의미합니다. 이 문자열은 UTC 기준으로 파싱된 후,Date객체가 로컬 시간대(예: 한국 표준시, GMT+9)로 변환하여 보여주므로, UTC 시간 + 로컬 시간대 오프셋만큼 시간이 추가된 것처럼 표시됩니다.new Date("YYYY-MM-DD"): 이 형식의 문자열은 브라우저 구현에 따라 다르게 해석될 수 있습니다. 일부 브라우저에서는 UTC의 해당 날짜 자정으로 간주하고, 이를 로컬 시간대로 변환하면서 시간대가 적용되어 로컬 시간대의 자정 시각보다 시간대 오프셋만큼 시간이 추가된 것처럼 나타날 수 있습니다. 이는 개발자가 가장 자주 혼란을 겪는 부분 중 하나입니다.
// 1. 현재 시스템의 로컬 시간을 기준으로 Date 객체 생성
const currentLocalTime = new Date();
console.log("현재 로컬 시간:", currentLocalTime.toString());
// 출력 예시: Mon Apr 08 2024 14:39:35 GMT+0900 (한국 표준시)
// 2. 시간대 정보 없는 ISO 8601 형식 (로컬 시간대 기준)
const specificTimeNoTimeZone = new Date("2024-04-08T10:00:00");
console.log("특정 로컬 시각:", specificTimeNoTimeZone.toString());
// 출력 예시: Mon Apr 08 2024 10:00:00 GMT+0900 (한국 표준시)
// 3. UTC 기준 시각 ('Z' 접미사 포함)
const utcParsedTime = new Date("2024-04-08T00:00:00Z");
console.log("UTC 시간 파싱 (로컬 표시):", utcParsedTime.toString());
// 출력 예시: Mon Apr 08 2024 09:00:00 GMT+0900 (한국 표준시)
// (UTC 0시가 GMT+9에서는 오전 9시로 표시됨)
// 4. 'YYYY-MM-DD' 형식 파싱의 특이점 (브라우저별 상이, 종종 UTC 자정으로 간주)
const dateOnlyStringParsed = new Date("2024-04-08");
console.log("'YYYY-MM-DD' 파싱 (로컬 표시):", dateOnlyStringParsed.toString());
// 출력 예시: Mon Apr 08 2024 09:00:00 GMT+0900 (한국 표준시)
// (UTC 2024-04-08 00:00:00으로 해석된 후 로컬 시간대로 변환될 수 있음)
Date 객체 사용 시 권장 사항
- 로컬 시스템의 현재 시각을 얻으려면
new Date()를 사용하세요. - 특정 로컬 시각을 문자열로 지정하려면
new Date("YYYY-MM-DD HH:mm:ss")또는new Date("YYYY-MM-DDTHH:mm:ss")와 같이 시간 정보까지 포함하는 형식을 사용하는 것이 안전합니다. new Date("YYYY-MM-DD")나new Date("YYYY-MM-DDTHH:mm:ssZ")와 같이 시간대 혼란을 줄 수 있는 형식의 직접적인 사용은 최소화하고, 필요하다면 명확한 시간대 처리를 위해 Moment.js와 같은 라이브러리를 활용하는 것을 고려하세요.
외부 라이브러리 활용: Moment.js
Moment.js는 JavaScript의 Date 객체가 가진 불편함을 해소하고, 다양한 시간대 및 포맷팅 기능을 제공하는 널리 사용되는 라이브러리입니다. Moment.js는 기본적으로 사용자의 로컬 시간대를 기반으로 동작하여 시간대 관련 문제를 보다 쉽게 관리할 수 있도록 돕습니다.
// 1. 현재 시각을 나타내는 Moment 객체 생성 (로컬 시간대 기준)
const currentMoment = moment();
console.log("현재 모멘트 시간 (기본 포맷):", currentMoment.format());
// 출력 예시: 2024-04-08T14:39:35+09:00
// 2. 'YYYY-MM-DD' 형식 문자열 파싱 (로컬 시간대 해당 날짜의 자정)
const specificDateMoment = moment('2024-04-08');
console.log("특정 날짜 모멘트:", specificDateMoment.format('YYYY-MM-DD HH:mm:ss'));
// 출력 예시: 2024-04-08 00:00:00 (로컬 시간대 기준)
// 3. UTC 문자열 파싱 후 로컬 시간대로 변환
const utcStringMoment = moment('2024-04-08T00:00:00Z');
console.log("UTC 모멘트 파싱 (로컬 표시):", utcStringMoment.format('YYYY-MM-DD HH:mm:ss'));
// 출력 예시: 2024-04-08 09:00:00 (로컬 시간대 기준, UTC 0시 + 9시간)
// 4. 다양한 사용자 정의 포맷팅
console.log("사용자 정의 포맷:", currentMoment.format('YYYY년 MM월 DD일 HH시 mm분 ss초'));
// 출력 예시: 2024년 04월 08일 14시 39분 35초
HTTP 요청 시 날짜/시간 파라미터 처리
JavaScript 환경에서 Date 객체를 HTTP 요청의 파라미터로 직접 포함하여 전송하면, 해당 객체는 JSON.stringify() 과정에서 ISO 8601 형식의 UTC 문자열로 자동 변환됩니다. 이는 new Date()로 생성된 객체뿐만 아니라 Moment 객체도 마찬가지로 동작합니다.
클라이언트의 로컬 시간대 정보가 아닌 UTC 기준으로 데이터가 전송되므로, 백엔드 시스템(예: Spring Boot의 Jackson)에서 특정 시간대 설정을 해두었더라도, 이미 UTC 문자열로 변환된 데이터를 받게 되면 의도와 다르게 처리될 수 있습니다.
API 통신을 위한 권장 방식
날짜 및 시간 파라미터를 API를 통해 전송할 때는 Date 객체나 Moment 객체 자체를 보내기보다는, 원하는 포맷으로 미리 문자열 변환하여 전송하는 것이 가장 안전합니다. 예를 들어, moment().format('YYYY-MM-DD HH:mm:ss')와 같이 명시적인 문자열 형식으로 변환하여 백엔드에 전달하면, 클라이언트의 의도대로 정확한 날짜/시간 정보를 유지할 수 있습니다.
// 1. Date 객체를 직접 파라미터로 사용할 경우 (자동 UTC 변환)
const payloadWithDateObjects = {
meetingStart: new Date(),
meetingEnd: new Date(2024, 3, 8, 17, 0, 0) // 월은 0부터 시작 (4월)
};
console.log("Date 객체 포함 페이로드:", JSON.stringify(payloadWithDateObjects));
// 출력 예시: {"meetingStart":"2024-04-08T05:39:35.000Z","meetingEnd":"2024-04-08T08:00:00.000Z"}
// (클라이언트 로컬 시간대 14:39 -> UTC 05:39, 클라이언트 17:00 -> UTC 08:00)
// 2. Moment 객체를 직접 파라미터로 사용할 경우 (유사하게 UTC 변환)
const payloadWithMomentObjects = {
projectDeadline: moment(),
taskCompletion: moment('2024-04-09T18:30:00')
};
console.log("Moment 객체 포함 페이로드:", JSON.stringify(payloadWithMomentObjects));
// Moment 객체도 기본적으로 Date 객체를 래핑하므로 JSON.stringify 시 유사하게 동작
// 3. 안전한 API 통신을 위한 문자열 포맷팅 (권장)
const formattedPayloadForAPI = {
startDate: moment().format('YYYY-MM-DD'),
startTime: moment().format('HH:mm:ss'),
fullTimestamp: moment().format('YYYY-MM-DD HH:mm:ss')
};
console.log("문자열 포맷팅 페이로드:", JSON.stringify(formattedPayloadForAPI));
// 출력 예시: {"startDate":"2024-04-08","startTime":"14:39:35","fullTimestamp":"2024-04-08 14:39:35"}
// (클라이언트의 로컬 시간대가 유지된 문자열로 전송됨)