JavaScript
JavaScript 날짜/시간 다루기
Date 객체의 문제점과 대안. day.js와 date-fns를 활용한 날짜 처리 방법을 알아봅니다.
JavaScript의 Date 객체는 다루기 까다롭습니다. 타임존 처리, 포맷팅 등에서 많은 버그가 발생합니다. 올바른 날짜 처리 방법을 알아봅니다.
Date 객체의 문제점
// 월이 0부터 시작
const date = new Date(2026, 0, 15); // 2026년 1월 15일
// 가변 객체
const date = new Date();
date.setMonth(date.getMonth() + 1); // 원본이 변경됨
// 불분명한 파싱
new Date('2026-01-15'); // UTC로 파싱
new Date('2026/01/15'); // 로컬 시간으로 파싱
new Date('01/15/2026'); // 브라우저마다 다름
// 타임존 처리가 복잡
const date = new Date('2026-01-15T10:00:00');
console.log(date.toString()); // 로컬 시간
console.log(date.toUTCString()); // UTC 시간
day.js 사용하기
가볍고 moment.js와 API가 비슷합니다.
import dayjs from 'dayjs';
// 생성
const now = dayjs();
const date = dayjs('2026-01-15');
const fromDate = dayjs(new Date());
// 포맷팅
dayjs().format('YYYY-MM-DD'); // 2026-01-15
dayjs().format('YYYY년 M월 D일'); // 2026년 1월 15일
dayjs().format('YYYY-MM-DD HH:mm:ss'); // 2026-01-15 10:30:45
// 조작 (불변)
const nextMonth = dayjs().add(1, 'month');
const lastWeek = dayjs().subtract(1, 'week');
const startOfMonth = dayjs().startOf('month');
const endOfDay = dayjs().endOf('day');
// 비교
dayjs('2026-01-15').isBefore('2026-01-20'); // true
dayjs('2026-01-15').isAfter('2026-01-10'); // true
dayjs('2026-01-15').isSame('2026-01-15', 'day'); // true
// 차이
dayjs('2026-01-20').diff('2026-01-15', 'day'); // 5
플러그인 활용
import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
import 'dayjs/locale/ko';
dayjs.extend(relativeTime);
dayjs.locale('ko');
dayjs().fromNow(); // '방금 전'
dayjs().subtract(1, 'hour').fromNow(); // '1시간 전'
dayjs().add(3, 'day').fromNow(); // '3일 후'
date-fns 사용하기
함수형 API로 트리 쉐이킹에 유리합니다.
import {
format,
addDays,
subMonths,
differenceInDays,
isAfter,
parseISO
} from 'date-fns';
import { ko } from 'date-fns/locale';
// 포맷팅
format(new Date(), 'yyyy-MM-dd'); // 2026-01-15
format(new Date(), 'yyyy년 M월 d일', { locale: ko });
// 조작
addDays(new Date(), 7);
subMonths(new Date(), 1);
// 비교
isAfter(new Date('2026-01-20'), new Date('2026-01-15')); // true
// 차이
differenceInDays(
new Date('2026-01-20'),
new Date('2026-01-15')
); // 5
// 파싱
const date = parseISO('2026-01-15T10:30:00Z');
타임존 처리
UTC로 저장, 로컬로 표시
// 서버로 보낼 때: UTC ISO 문자열
const utcString = new Date().toISOString();
// 2026-01-15T01:30:00.000Z
// 받아서 표시할 때: 로컬 시간으로 변환
import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';
import timezone from 'dayjs/plugin/timezone';
dayjs.extend(utc);
dayjs.extend(timezone);
const localTime = dayjs.utc('2026-01-15T01:30:00Z')
.tz('Asia/Seoul')
.format('YYYY-MM-DD HH:mm');
// 2026-01-15 10:30
날짜만 다룰 때
// 시간 정보 없이 날짜만 저장
const dateOnly = '2026-01-15'; // ISO 날짜 문자열
// 파싱할 때 타임존 영향 받지 않게
const [year, month, day] = dateOnly.split('-').map(Number);
const date = new Date(year, month - 1, day);
흔한 실수와 해결
문자열 파싱
// 나쁜 예: 브라우저마다 다르게 파싱될 수 있음
new Date('01/15/2026');
// 좋은 예: ISO 형식 또는 라이브러리 사용
new Date('2026-01-15');
dayjs('2026-01-15');
parseISO('2026-01-15');
월 계산
// Date 객체는 월이 0부터 시작
const jan = new Date(2026, 0, 1); // 1월
const dec = new Date(2026, 11, 1); // 12월
// 라이브러리는 1부터 시작
dayjs('2026-01-15'); // 1월
날짜 비교
// 나쁜 예: 시간까지 비교됨
const date1 = new Date('2026-01-15');
const date2 = new Date('2026-01-15');
date1 === date2; // false (다른 객체)
date1.getTime() === date2.getTime(); // 시간까지 같아야 true
// 좋은 예: 날짜만 비교
dayjs('2026-01-15').isSame('2026-01-15', 'day'); // true
마무리
Date 객체만으로 날짜를 다루면 버그가 생기기 쉽습니다. day.js나 date-fns 같은 라이브러리를 사용하는 것을 추천합니다.
타임존 처리가 필요하다면 서버에서는 UTC로 저장하고, 클라이언트에서 로컬 시간으로 변환해서 표시하는 패턴을 사용합니다.