목록으로
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로 저장하고, 클라이언트에서 로컬 시간으로 변환해서 표시하는 패턴을 사용합니다.