개요
예로부터 Javasciprt 환경에서 날짜와 시간을 parsing하기 위한 도구로 가장 유명하게 사용돼 온 라이브러리는 Moment.js
였습니다. 그런데 비교적 최근인 2020년 9월, Moment.js
의 사망선고가 내려졌습니다. 이미 대체해서 사용하실 분들은 다들 대체하셨겠지만, 혹시 아직도 대체하지 않고 그대로 사용중이신 분들을 위한 포스팅입니다. goodbye Moment........ 😢
Moment.js는 2020년 9월 기준, 일주일에 1,200만 건 이상 다운로드 되고 있습니다. 하지만, Moment는 이전 세대의 자바스크립트 생태계를 위해 만들어졌고, 현대의 웹은 많이 달라졌습니다. 우리는 새로운 기능보다 안정성을 중시하기로 했습니다.
(중략)
최근 크롬 개발 툴은 Moment를 교체하라는 권고안을 제시하기 시작했고, 우리는 이러한 움직임을 지지합니다.
(중략)
우리는 이제 Moment를 maintenance mode의 legacy project로 간주합니다. 죽은 건 아니지만, 정말로 끝난 거예요. (It is not dead, but it is indeed done.)
끝났다라는 말의 의미에 대해서, Moment.js
팀은 다음과 같은 말을 덧붙였습니다.
- 우리는 새로운 기능을 추가하지 않을 것이다.
- 우리는 Moment의 API를 불변으로 바꾸지 않을 것이다.
- 우리는 Tree shaking이나 Bundle size issue를 다루지 않을 것이다.
- 우리는 어떠한 큰 변화도 하지 않을 것이다. (버전 3은 없다)
- 우리는 오랫동안 알려져 있는 버그를 해결하지 않을 수도 있다.
- 하지만 우리는 Moment가 이미 수많은 기존 프로젝트에서 사용되고 있음을 알기 때문에, 중대한 보안 문제가 발생할 경우에 이를 해결할 것이다.
Tree shaking?
본문 3번에 언급된 Tree shaking은 직역하면 나무 흔들기
로 번역되는데요, 간략하게 설명을 하고 넘어갈 필요성을 느꼈습니다. 이것은 Webpack v4에서 제공되는 기능으로, Webpack이 JS 모듈을 번들링할 때, 사용하지 않는 코드를 제거하는 최적화 과정을 말합니다. Moment는 그동안 이 문제에 대해서 많은 비판을 받아왔습니다. 전혀 사용되지 않는 모듈/코드들을 Bundle에 포함시켜 불필요한 용량을 차지했거든요.
Dayjs?
이렇게 공식적으로 Moment.js
가 사망선고를 받기 전에도 이미 앞서 언급된 Tree shaking과 Bundling size issue로 인해서 Moment.js
를 대체할 수 있을만한 라이브러리에 대한 수요가 있었습니다. Moment.js
의 번들 크기가 커지는 주된 이유는 바로 사용하지 않는 locale이 죄다 들어가기 때문이었는데요, 압축을 해도 거대한 모듈의 크기를 보세요 🤭
보통 Moment.js
의 대체재로 언급되는 라이브러리는 위에서 보이듯 크게 luxon
date-fns
dayjs
로 나누어집니다.
위의 npm trend 페이지에서 보이듯 가장 인기가 많은 건 dayjs
였습니다. 다운로드 수가 많은 것은 date-fns
인 반면 luxon
은 그다지 반응이 좋지 않네요. 세 개의 라이브러리 중에서 가장 용량이 크기도 하고, 기존의 Moment.js
와는 다소 낯선 사용 방식들이 걸림돌이 되지 않았을까 조심스럽게 예상해봅니다.
date-fns
와 dayjs
, 두 라이브러리 모두 불변성을 포함하고, typescript를 지원하며, tree shaking을 통해 module의 크기를 줄일 수 있습니다. 하지만 저는 dayjs
를 선택했는데요, 이유는 다음과 같습니다.
- locale을 따로 떼어놓지 않고 일반적으로 사용할 때는 여전히
date-fns
의 크기는 크다. dayjs
의 라이브러리는Moment.js
와 거의 완벽하게 호환된다.
당장 프로젝트에 사용하는 모든 Moment.js
를 교체하지 않는다고 하더라도, 언젠가 교체를 하게 될 일이 생긴다면 가급적이면 동일한 API를 사용하는 라이브러리를 사용하는 것이 리팩토링에 있어서 훨씬 수월할 것입니다. 기존에 Moment.js
를 사용했던 인원이라면 특별한 러닝커브도 필요하지 않을 것이고요.
How to use
앞서 Moment.js
와 dayjs
의 API가 비슷하다고 말씀드렸습니다. 따라서 기존에 Moment.js
를 사용하는 방법과 동일하게 사용할 수가 있는데요. 가장 Basic한 사용방법은 다음과 같습니다.
import moment from 'moment'
moment() // 현재 날짜, 시각을 출력합니다
moment('2020-10-30', 'YYYY-MM-DD').format() // 2020-10-30T00:00:00+09:00
moment('30-10-2020', 'DD-MM-YYYY').format() // 상동
위와 같이 사용할 수 있습니다만, 만일 날짜나 시간을 추가하거나 빼는 add
subtract
를 사용하게 되면 다음과 같은 문제가 생깁니다.
const nowDate = moment('2020-10-30')
const nextDate = nowDate.add(1, 'days') // 2020-10-31
const prevDate = nowDate.subtract(4, 'days') // 2020-10-27
코드를 보면 알 수 있겠지만 저는 nowDate
로 10월 30일을 설정하고 add
와 subtract
를 모두 nowDate
에서 수행했습니다. 하지만 prevDate
는 nextDate
에서 4일을 뺀 10월 27일을 표시하고 있습니다. 이것이 Moment.js
의 가장 큰 단점으로 꼽히는 불변성에 관한 문제입니다. 이를 해결하기 위해서 Moment.js
는 그동안 clone()을 제공해왔습니다.
// clone date
const nowDate = moment('2020-10-30')
const nextDate = nowDate.clone().add(1, 'days') // 2020-10-31
const prevDate = nowDate.clone().subtract(4, 'days') // 2020-10-26
이와는 대조적으로 dayjs
는 불변성을 유지하고 있습니다.
import dayjs from 'dayjs'
const nowDate = dayjs('2020-10-30')
const nextDate = nowDate.add(1, 'date') // 2020-10-31
const prevDate = nowDate.subtract(4, 'date') // 2020-10-26
Moment.js
를 경량화 해서 만들어진 것이 dayjs
이다 보니 이따금씩 기본 모듈에는 지원하지 않는 format이 발생하기도 합니다. UTC가 대표적인데요, 가장 빈번하게 사용되는 형식이라고 생각하는데, 기본적으로 제공하고 있지는 않습니다. 하지만 이런 경우라도 dayjs
에 plugin으로 넣어주기만 하면 손쉽게 사용할 수 있습니다.
import dayjs from 'dayjs'
import utc from 'dayjs/plugin/utc'
dayjs.extend(utc)
dayjs().utc() // ISO 8601에 따른 현재 날짜가 표시됩니다