FE

[코알라] 골칫거리 moment를 간단하게 대체하는 Day.js

윤종석 프로필 이미지

윤종석

2023.11.13

10 min read

[코알라] 골칫거리 moment를 간단하게 대체하는 Day.js

안녕하세요, 코멘토 프론트엔드 개발자 윤종석입니다. 코멘토 개발팀에서 어떤 라이브러리를 쓰는지 공유하는 '코알라(코멘토와 알아보는 라이브러리)' 시리즈입니다. 첫 번째 글은 FE팀에서 사용하는 Day.js 라이브러리를 소개해드리려고 합니다.

FE에서 시간 다루기

Javascript에는 시간과 날짜를 다루는 Date라는 내장 객체가 있습니다. 그렇지만 개발하면서 만나는 다양한 요구사항에 맞춰 사용하기 쉽지 않은 객체라서 처음에 쓰게 되면 당황할 일이 자주 생깁니다.

const today = new Date('2023-10-26');

console.log(today.getMonth()); // 9 (10월이 9로 표현된다)

// 날짜를 원하는 방법으로 표현하려면 복잡한 함수를 만들어야 한다.
function formatDate(date) {
    const fullYear = date.getFullYear();
    const month = ('0' + (date.getMonth() + 1)).slice(-2);
    const date = ('0' + date.getDate()).slice(-2);
    
    return `${fullYear}년 ${month}월 ${date}일`;
}

formatDate(today); // 2023년 10월 26일
Date에서 month는 0-11의 범위를 가진다. 처음에 많이 헷갈리는 대표적인 예시. 날짜를 원하는 형식으로 표현하는 것도 쉽지 않다.

Javascript의 차기 표준을 정하는 TC39에서도 Date의 문제점을 해결하기 위해 Temporal이라는 새로운 내장 객체를 도입하는 논의를 진행하고 있습니다. Temporal 도입을 논의하는 문서에서 TC39는 Date 객체를 두고 'ECMAScript의 고질적인 문제점(Date has been a long-standing pain point in ECMAScript)'이라고 표현했습니다.

Moment의 유행과 퇴장

Moment의 커밋 기록. 2011년에 시작했고 최근에는 관리가 거의 되고 있지 않다.

Moment 라이브러리가 2011년에 등장합니다. Date 객체보다 훨씬 간편하게 시간/날짜를 다룰 수 있는 라이브러리였습니다. 나오자마자 인기를 얻은 이 라이브러리는 2020년 9월 기준 무려 일주일에 1,200만의 다운로드 수가 기록됩니다.

import moment from 'moment';

const today = moment('2023-10-25');
console.log(today.month()); // 10

today.format('YYYY년 MM월 DD일'); // 2023년 10월 25일
위에서 Date로 작성한 코드와 같은 역할을 하는 moment의 예시. 월을 표시하는 값이 직관적으로 바뀌었고 날짜 포맷도 쉬워졌다.

코멘토 역시 Moment를 시간 및 날짜 관리를 위해 사용하고 있었습니다. 하지만 2020년 9월, Moment 팀은 라이브러리를 더 이상 개발하지 않고 유지보수만 한다는 선언을 합니다.

We now generally consider Moment to be a legacy project in maintenance mode. It is not dead, but it is indeed done.

당시 공유된 Moment의 개발 종료와 코멘토 내 반응.

Moment는 중단 시점 기준 10년 가까이 된 라이브러리였고 핵심 코드 역시 그만큼 오래되었습니다. 웹 개발의 새로운 흐름에 맞춰서 구조를 바꾸기는 개발팀에게 굉장히 큰 일이었습니다. 구조가 오래되고 여러 기능이 붙다보니 라이브러리의 스크립트 용량도 커졌습니다. 이런 이유로 Moment 팀은 개발 중단을 선언합니다.

4개의 후보

Moment 팀은 개발 중단 안내와 함께 4개의 라이브러리를 대안으로 소개했습니다. date-fns, Day.js, js-joda, Luxon인데요. 당시 코멘토 프론트엔드 팀 Moment팀이 소개한 라이브러리를 일단 후보로 두고 고민을 시작했습니다. 각각 써보면서 어떤 라이브러리가 가장 쓰기 편하고 옮기기 편할지 테스트도 해봤고요.

전환 당시 Notion으로 정리했던 대안들의 장단점과 사용 방법

참고로 잘 정리했다는 글의 링크는 바로 You-Don't-Need-Momentjs입니다. 대안을 고민하시는 분은 링크의 글을 참고하시면 큰 도움이 되실 겁니다.

Day.js를 선택한 이유

저희 팀은 이미 처음에 말씀드렸듯이 Day.js를 대안으로 선택했습니다. 기존에 moment를 쓰던 상황에서 Day.js를 선택한 이유는 어떤 것이었을까요?

편한 전환 과정

이미 Moment를 사용 중이라면, Moment에서 전환하기 가장 쉽다는 것이 Day.js의 큰 강점입니다. Day.js는 처음부터 Moment와 같은 API를 구현하는 것이 목적이었기 때문에 Moment를 사용하는 방법 그대로 Day.js를 사용할 수 있습니다.

import moment from 'moment';

const today = moment('2023-10-25');
console.log(today.month()); // 10

today.format('YYYY년 MM월 DD일'); // 2023년 10월 25일

/////////

import dayjs from 'dayjs';

const today = dayjs('2023-10-25');
console.log(today.month()); // 10

today.format('YYYY년 MM월 DD일'); // 2023년 10월 25일
위의 예시 코드를 변환한 코드. import문과 이름 빼고는 완전히 같은 모습을 볼 수 있습니다.
// Moment.js
moment().startOf('month');

// date-fns
import startOfMonth from 'date-fns/startOfMonth';
startOfMonth(new Date());

// dayjs
dayjs().startOf('month');

// Luxon
DateTime.local().startOf('month');

// Temporal
Temporal.Now.zonedDateTimeISO().with({day: 1});
다른 라이브러리와 같은 기능을 비교한 코드. 보시다시피 다른 라이브러리보다 Dayjs가 Moment와 가장 비슷합니다. 코드 출처: https://github.com/you-dont-need/You-Dont-Need-Momentjs

라이브러리를 아예 교체해야 하는 상황에서 이 정도로 교체 작업이 끝난다는 것은 굉장히 큰 강점이었습니다. 기존 코드를 그대로 사용할 수 있는 것은 물론이고 새로운 API를 배워야 하는 부담도 훨씬 줄어들기 때문이죠. 여전히 높은 Moment의 사용률을 고려하면 다른 라이브러리에 비해 Day.js가 크게 앞서는 부분입니다.

적은 용량과 충분한 성능

출처: https://github.com/you-dont-need/You-Dont-Need-Momentjs

Day.js의 용량은 보시다시피 굉장히 적은 편입니다. 특히 대체하는 Moment와 비교하면 1/10 미만으로 줄어든 것을 볼 수 있습니다. 다른 경쟁 라이브러리와 비교해도 적은 편이죠. 라이브러리의 크기가 작으면 서비스의 JS 크기도 줄어들고 그만큼 사용자의 경험도 개선됩니다.

라이브러리 실행 시간
Moment 1078.948ms
date-fns 398.107ms
Day.js 765.358ms
Luxon 2306.765ms

위의 예시 코드를 실행하는데 걸린 시간을 비교한 표입니다. (출처: 링크) 성능에서 가장 좋은 모습을 보이는 date-fns만큼은 아니지만 Moment보다는 좋은 성능을 보여주는 것을 볼 수 있습니다. 시간 관련 작업은 저희 서비스에서는 아주 중요한 부분은 아니기 때문에 사용하기 충분한 성능이라고 판단했습니다.

실제 전환 과정

간단한 전환이 Day.js의 장점이지만 실제 서비스인만큼 어떤 오류가 발생할지 알 수 없기 때문에 조심스럽게 접근했습니다. 우선 Day.js를 설치하고 Moment와 같이 사용을 했고 개발 중에 새롭게 시간 작업이 필요할 때 Day.js를 사용해서 작업하고, 기존에 Moment를 사용한 코드를 만날 때마다 Day.js로 변환하는 방식으로 점점 Day.js의 비중을 늘려나갔습니다. 이런 식으로 해서 지금은 메인 프로젝트에서는 Moment를 쓰는 코드가 완전히 사라진 상황입니다. 🎉🎉

의존성도 끊임없이 발전시켜야 한다

직접 작성하는 코드뿐만 아니라 의존하는 외부 라이브러리 역시 끊임없이 변화합니다. 오늘 다룬 Moment처럼 아예 개발이 중단되기도 하고, 새로운 버전이 이전 버전과 완벽하게 호환되지 않는 경우도 있습니다. 생각지도 못한 보안 문제가 발견되는 경우도 있고요. 서비스 중인 제품의 의존성을 쉽게 바꿀 수는 없겠지만 방치해두면 언젠가는 제품의 발목을 잡게 되는 일이 생길 수 있습니다. 이번 글이 라이브러리를 바꾸거나 버전을 올리려고 하는 분들에게 참고가 될 수 있으면 좋겠습니다.

오늘 다룬 라이브러리들의 최근 5년 간 다운로드 수(출처: npm trends). 아직도 Moment가 가장 높은 것, dayjs가 최근 들어서 date-fns를 앞지른 것을 볼 수 있습니다.

위의 그래프에서 보다시피 아직도 Moment의 사용률은 높습니다. 기존 프로젝트를 설치하는 경우도 있을 것이고 오랜 세월 다양한 곳에서 쓰이다 보니 블로그나 강의에서 사용해서 그걸 따라 설치하는 경우도 많다고 생각합니다. 하지만 새로운 환경에서 시작할 때는 한 번쯤 라이브러리의 개발 현황을 살펴보고 최대한 오래 지원받을 수 있는 환경에서 시작하는 것을 생각해보면 좋겠습니다.


커리어의 성장을 돕는 코멘토에서 언제나 함께 성장할 개발자를 기다리고 있습니다. 채용 페이지에서 코멘토가 어떤 회사인지, 어떤 사람을 찾는지 더 자세히 확인해보세요. 😊