All Articles

timestamp를 현재시간으로 바꿔주는 moment.js와 커링의 콜라보

image.png

moment.js에 대한 설명을 하기 이전에 먼저 currying에 대해 짚고 넘어가보자! currying은 지난번에 포스팅했던 클로저를 활용해서 코드를 좀 더 단순화 하는 기법이다. 클로저가 궁금한 분들은 지난번 포스팅 고고!

currying

간단한 예제부터 살펴보자. 더하기를 해줄거다!

let sum = function(x) {
  return function(y) {
    return x+y;
  }
}

sum이라는 변수 안에 x를 인자로 받는 익명함수를 넣어줄건데, 이 함수는 y를 인자로 받는 익명함수를 리턴하고, 그 익명함수는 최종적으로 x+y를 리턴한다.

화살표함수를 이용해서 쫌 더 줄여보자!

const sum = x => y => x+y

놀라운 세상!! 근데 더하기 같은 경우는 순서가 그다지 중요하지 않으므로 이번엔 제곱연산을 해보자.

Answer라는 함수를 만들어줄건데, 이 Answer를 이용해서 제곱하는 square, 세제곱하는 cubic, 네제곱하는 strange도 만들어줄거다.

function Answer(num) {
  return function (n) {
    return Math.pow(n,num)
  }
}

const square = Answer(2)
const cubic = Answer(3)
const strange = Answer(4)

console.log(square(2)) // 4
console.log(cubic(2)) // 8
console.log(strange(2)) // 16

콘솔을 찍어보면 정확히 내가 원하는 값이 나온다! 커링을 이용해서 더 줄여볼까?

const Answer = num => n => Math.pow(n,num)

더하기는 순서가 상관없었지만, 제곱은 인자가 들어가는 순서가 중요하므로 조심 또 조심!
n의 num제곱을 결과값으로 리턴해야 하는데, square는 Answer의 제곱, 즉 여기서 Answer에 들어가는 인자는 num이다. 이 Answer함수 내부에 들어있는 n을 인자로 받는 익명함수가 결국 우리가 원하는 값을 리턴하는데 square(3)은 결국 Answer(2)(3)과 똑같은 값이기 때문에 3의 제곱인 9를 리턴한다.

자 그럼 이제 moment에서 적용해보자.

moment.js

moment.js, react-moment… 이것저것 종류가 참 많다. 둘 다 깔긴 했었는데, 나는 일단 timestamp를 예쁜 날짜형식으로 바꾸는게 목적이어서 moment-timezone을 설치했다.

설치는 간편하게 이렇게! yarn add moment-timezone
가져다 쓸때도 간단하게 import moment from "moment-timezone"

moment도 moment지만 나는 Hooks도 어색하고, timezone의 숫자를 얻어내는 것부터 힘들었다. 어쨌든… 먼저 firestore field에서 timezone 숫자값을 얻어내자.

지난번과 코드가 약간 달라졌다. ClassInfo라는 객체에 모든 정보가 담겨있도록 받았다.

const ClassInfo = db.collection("left_seat").doc('wecoder')
const CurrentIndexOfClass = db.collection("left_seat").doc('current_th')

const fetchData = async () => {
  try {
    const classIndex = await CurrentIndexOfClass.get()
      .then(doc => {
        const result = doc.data()
        return result.current
      })

    const classInfo = await ClassInfo.get()
      .then(doc => doc.data())
    return classInfo[classIndex]
  } catch (err) {
    console.log(err)
    throw err
  }
}

ClassInfo에서 특정 숫자를 입력했을때 해당하는 정보를 받아오기 위해 classIndex라는 숫자 또한 이용했다.

classInfo에서 startingdate,closingdate를 통해 timezone 숫자를 받아오는데 closing_date 자체가 객체기 때문에 seconds라는 property까지 접근해주어야 한다.
그렇게 접근했을때의 값은 unix number이기 때문에 우리는 moment를 이용해 내가 원하는 형식의 날짜로 다시 한 번 변형해줘야 한다.

const allDate = value => {
  if (!value || !value.seconds) return ''
  return moment(value.seconds * 1000).tz('Asia/Seoul').format('YYYY년 M월 D일')
}

랜더링 되는 중에 undefined가 잠시잠깐 뜰 수 있는데, 그걸 방지하기 위해 value 값이 없거나 value.seconds가 없을때 에러나서 터지지 말라고 쿠션 조건을 한줄 넣어 주었다.

리턴되는 moment함수에선 value.seconds에 1000을 곱해줬는데, 이 작업을 해주지 않으면 1970년으로 나온다 ㅋㅋㅋ
그리고 .tz에 해당 지역에 대한 정보를 넣어줘야 시차(?)가 맞는다.

format은 진짜 진짜 진짜로 핵 신기하다.
YYYY-MM-DD 해도 되고, YYYY년 MM월 DD일 해도 된다 오오~
근데 MM월 말고 M월이라고 해도 된다.
M월이라고 하면 한 자릿수인 달엔 01월, 02월 이렇게 표시되는데 M만 하면 그런 걱정은 놉!

진짜 똑똑한 라이브러리다 와~~~ 만든 사람에게 박수 👏👏👏

어찌됐든… 이렇게 만든 allDate를 그냥 밑에다가 적용하기만 하면 된다. allDate(classInfo.starting_date) 이렇게!

근데 나에게 주어진 과제는 연도가 포함된 날짜와 연도가 포함되지 않은 날짜 두 개!
식을 두 번씩 쓰면 코드가 쓸데없이 길어지기 때문에 utils에 moment라는 함수를 따로 만들어주었다. 이렇게 함수를 따로 빼주면 moment도 한번만 import 하면 되니까 완전 개이득!

// utils/moment.js

import moment from "moment-timezone"

const formatDate = config => {
    return value => {
        if (!value || !value.seconds) return ''
        return moment(value.seconds * 1000)
            .tz('Asia/Seoul')
            .format(config)
    }
}

export const withYear = formatDate('YYYY년M월D일')
export const withoutYear = formatDate('M월D일')

일단 함수로 한 번 빼줘서 moment를 한 번만 import해도 되고,
위에서 allDate를 쓰는 식으로 썼다면 비슷한 템플릿의 코드를 두 번 썼어야 했던 것도 withYear과 withoutYear이라는 변수를 선언해서 코드는 단 한 번만 써준다 와~

적용할 때는 <span>개강일: {withoutYear(classInfo.starting_date)}</span> 걍 요렇게 해주면 끝!!

아 물론 utils/moment함수를 import 해줘야 적용할 수 있겠쥐~ 뭔가 굉장히 똑똑해진 느낌쓰~

앞으로 코드를 어떻게 하면 더 효율적으로 줄일 수 있을지를 많이 고민해봐야 할 것 같다!

Reference

  • 허선생님의 꿀팁