ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [오픈소스코드리뷰] 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

    댓글

uznam8x