카테고리 없음

[오픈소스코드리뷰] Dayjs

유지남 2022. 8. 5. 17:33

들어가며

기존에 브라우저의 날짜와 시간을 쉽게 표현할 수 있는 Moment.js 를 많이 사용했었는데,
무겁기도 하고(18kb), 더 이상 개발을 진행하지 않아 대안으로 떠오른 경량화된 라이브러리입니다.

Moment.js 와 매우 흡사하여 이미 사용해보신 경험이 있다면 금방 사용 하실수 있고,
모든 브라우저를 지원하며 ie는 9 이상부터 지원됩니다.
현재 리뷰 버전은 1.10.7 입니다.

주요 기능

- 날짜 포맷팅
- 다국어
- 플러그인 확장

리뷰

// files and folder
...
.eslintrc.json
babel.config.js
.travis.yml
prettier.config.js
...


Babel과 테스트 러너인 Karma를 사용 하고, 테스트 프레임워크는 Jest를 사용하네요.
코드 컨벤싱으로는 Prettier, ESLint를 사용하고,
Travis CI 로 구성되어 있네요.
Travis CI를 이용하면 Github 프로젝트에 특정 이벤트가 발생되면, 자동으로 테스트, 빌드, 배포할 수 있는 편리한 서비스 입니다.
비공개 저장소는 유료로 사용할 수 있습니다.

// package.json
{
    "scripts": {
      "test": "TZ=Pacific/Auckland npm run test-tz && TZ=Europe/London npm run test-tz && TZ=America/Whitehorse npm run test-tz && npm run test-tz && jest",
      "test-tz": "date && jest test/timezone.test --coverage=false",
      "lint": "./node_modules/.bin/eslint src/* test/* build/*",
      "prettier": "prettier --write \"docs/**/*.md\"",
      "babel": "cross-env BABEL_ENV=build babel src --out-dir esm --copy-files && node build/esm",
      "build": "cross-env BABEL_ENV=build node build && npm run size",
      "sauce": "npx karma start karma.sauce.conf.js",
      "test:sauce": "npm run sauce -- 0 && npm run sauce -- 1 && npm run sauce -- 2  && npm run sauce -- 3",
      "size": "size-limit && gzip-size dayjs.min.js"
	},
	"pre-commit": [
      "lint"
	],
	"size-limit": [
      {
        "limit": "2.99 KB",
        "path": "dayjs.min.js"
      }
	]
}


size-limit 을 사용하여, 경량화 라이브러리 답게 빌드 된 용량까지 꼼꼼하게 검사 하려는 노력도 보입니다.

// prettier.config.js
module.exports = {
  useTabs: false,
  printWidth: 80,
  singleQuote: true,
  trailingComma: 'none',
  semi: false
}

필요 없는 공백과 콤마(,) 는 전부 허용하지 않네요.
개인적으로는 semi 정도는 true 로 설정하여 개발을 하고 있습니다.

// index.js
import * as C from './constant'
import en from './locale/en'
import U from './utils'

C, U와 같은 약어를 사용하니 유념하면서 코드를 살펴보아야 할 것 같습니다.
실제 프로젝트에서는 가급적 Utils, Constant로 직관적으로 알 수 있도록 변수명을 짓는 것이 좋습니다.

// constant.js
// 생략 ...
// 1) 
export const MILLISECONDS_A_SECOND = 1e3
// 생략 ...
// 2)
export const REGEX_PARSE = /^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[^0-9]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?\.?(\d+)?$/; //3
export const REGEX_FORMAT = /\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g;
// 생략 ...
  1. 1e3 은 1 * 10^3 = 1000 을 뜻합니다. (1 * 10 * 10 * 10)
    가끔 오픈 소스를 보면 수를 이렇게 표현 한 것들이 조금 보이더라고요.
    추가로 0b000001(2진수), 0x000001(16진수) 표현식도 있습니다.
  2. REGEX_PARSE는 2021-01-01 12:01:02 와 같은 형식의 구문을 분석하고,
    REGEX_FORMAT은 YYYY-MM-DD 와 같은 형식을 분석하는 정규식 입니다.
    IE의 경우 날짜만 있는 경우는 2021-01-01은 문제 없지만, 시간과 같이 표현할 경우는 2021/01/01 01:01:01 로 표현해야 모든 브라우저를 대응 할 수 있습니다.
  3. 캡처링 그룹에 (?<year>\d{4}) 라고 넣으면 groups 에 'year' 로 명시되어 직관적으로 사용 할 수 있습니다.

정규식을 공부해두면 좋은데, 매번 사용하지 않아 자주 잊어버리는 게 정규식입니다.
그래서 정규식이 어렵습니다.

저는 오픈 소스를 보면, 가장 먼저 utils 파일 부터 확인을 하는데요.
가장 많이 사용하는 함수들이 모여 있고, 대부분 테스트코드를 짜놓기 때문에 현재 프로젝트에 당장 사용할 수 있는 함수도 가끔 보입니다.

// utils.js
// 생략 ...
const padStart = (string, length, pad) => {
  const s = String(string)
  if (!s || s.length >= length) return string
  return `${Array((length + 1) - s.length).join(pad)}${string}`
}
// 생략 ...


string 앞에 지정한 길이(length) 만큼 pad 문자열을 더해 주는 함수입니다.
예를 들어 padStart('a', 2, '0')를 호출하면 0a 이 나오는 것이죠.
(Array(length).join(pad) + string).slice(1-length) 로 조금 더 짧게 표현할 수 있습니다.
이런 함수들은 자주 사용하니 꼭 알아 두시면 좋습니다.

// index.js
// 생략 ...
// 1)
function add(number, units) {
  ...
  const step = {
  [C.MIN]: C.MILLISECONDS_A_MINUTE,
  [C.H]: C.MILLISECONDS_A_HOUR,
  [C.S]: C.MILLISECONDS_A_SECOND
  }[unit] || 1 // ms

  return Utils.w(nextTimeStamp, this)
}
function subtract(number, string) {
	return this.add(number * -1, string)
}
// 생략 ...
// 2)
toISOString() {
  return this.$d.toISOString()
}
// 생략 ...

  1. add, subtract 함수는 자용 사용되는 함수입니다.
    중간에 step 함수를 보면 Switch 문 대신에 object 형태로 나열 한 후에 리턴하는 방법도 괜찮아 보이네요.
  2. 2020-01-01T12:01:02Z와 같은 형태로 표현되는 함수입니다.
    가끔 API 연동을 하다보면 이와 같은 Timezone 형태를 자주 볼 수 있습니다.

자주사용되는 플러그인

// libs/dayjs.js
// 플러그인 추가 방법 예시
import dayjs from 'dayjs';
import weekday from 'dayjs/plugin/weekday';
dayjs.extend(weekday);

export default dayjs;

localeData

locale 설정을 할 수 있습니다.

weekday

일, 월, 화, 수 ... 등의 요일을 locale에 맞게 표현해 줍니다.
weekday, localeData 가장 기본적 플러그인이므로, 기본적으로 확장해 두는게 좋습니다.

arraySupport

dayjs([2010, 1, 14, 15, 25, 50, 125])와 같은 배열 형태를 지원합니다.

ToArray, ToObject

arraySupport, objectSupport와 같이 사용하면 좋은 플러그인이며,
배열과 오브젝트로 반환하는 함수로 함수형으로 개발할 때 유용하게 사용됩니다.

UTC, Timezone

나라별 시간을 보정하기 위한 플러그인 입니다.

Uses

const dayjs = require('dayjs');
const today = dayjs('2021-01-01 13:00:01');
const updated = dayjs('2021-01-01 13:00:00');

const time =
  ['y:년', 'M:달', 'd:일', 'h:시간', 'm:분', 's:초'].reduce((a, b) => {
    const [key, value] = b.split(':');
    const diff = today.diff(updated, key);
    return !a && diff > 0 ? `${diff}${value} 전` : a;
  }, '') || '방금';

console.log(time) // 1초 전;

TIP

리액트 UI Framework인 ant design에서는 date picker, time picker, calendar 가 momentjs로 개발되어 있습니다.
dayjs로 변경하는 방법은 https://ant.design/docs/react/replace-moment 를 참고하시면 되고,
weekday, localeData plugin 을 꼭 추가해주어야 합니다.

마치며

이상으로 dayjs 리뷰를 해보았습니다.
새로운 개발 방법들을 알게 되어 유익한 시간이였던 것 같습니다.

감사 합니다.

https://github.com/iamkun/dayjs

iamkun/dayjs

⏰ Day.js 2KB immutable date-time library alternative to Moment.js with the same modern API - iamkun/dayjs

github.com