자바스크립트 Date 비교하기

jjanmo·2021년 3월 10일
2

자바스크립트의 Date 객체는 빌트인 객체이면서 생성자 함수이다. 이 Date는 객체지만 비교가능하다. 그것은 내부적으로 숫자 값을 갖고 있기 때문이다. 이 값은 1970년 1월 1일 00:00(UTC)을 기점으로 현재 시간까지의 밀리초를 나타낸다. 이 포스팅을 통해서 Date를 비교하면서 나타난 여러가지 호기심에 대한 내용을 공유하고자 한다.

실제로 코드를 실행할 수 있는 링크입니다. 혹은 브라우저의 콘솔창을 여시고 실습을 해보시면 좀 더 많은 호기심을 해소하실 수도..

일반적인 날짜 비교

  const now = new Date();
  const yesterday = new Date("2021-03-08");

  console.log(now > yesterday); //true
  console.log(now < yesterday); //false

위의 결과값은 예상한 바와 같이 true false 를 반환한다.

궁금증은 moment.js를 사용하면서 시작되었다.

moment.js는 자바스크립트에서 Date를 쉽게 다루기 위해서 사용되는 라이브러리이다. 이 라이브러리는 간결한 메소드를 이용해서 원하는 날짜 형식으로 쉽게 포멧팅하고 조작하여 사용할 수 있게끔 도와준다.

이 포스팅과 관련이 없지만, 안타까운 소식은 moment.js가 개발이 중단되었음을 선언했다.(2020년 말쯤). moment.js를 사용하는 기존의 프로젝트는 이를 알고 차근차근 변경하는 방안을 추천한다. 좋은 소식은 자바스크립트 자체적으로 날짜와 시간에 대한 라이브러리를 제공할 예정이라고 한다. (관련링크)

궁금증1 : moment로 변형된 날짜는 비교가 될까?

const Moment = require("moment"); //아래 코드에서는 모두 생략, 있다고 가정하고 진행

const momentNow = Moment(now).format("YYYY-MM-DD, h:mm:ss a");
const momentYesterday = Moment(yesterday).format("YYYY-MM-DD, h:mm:ss a");

console.log(momentNow > momentYesterday); //true
console.log(momentNow < momentYesterday); //false
console.log(momentNow, momentYesterday);

Date와 같은 결과를 보여주었다. 이렇게만 하고 넘어갔다면 아무런 궁금증을 갖을 수 없었을 것이다. 이제 호기심의 서막이 시작된다.🔥 우선, 포맷을 다르게 하여 비교해 보았다. 포맷을 다르게 하면 어떤 결과가 나올까?

const _momentNow = Moment(now).format("MMMM Do YYYY, h:mm:ss a");
const _momentYesterday = Moment(yesterday).format("MMMM Do YYYY, h:mm:ss a");

console.log(_momentNow > _momentYesterday ); //false
console.log(_momentNow < _momentYesterday); //true
console.log(_momentNow, _momentYesterday);

오잉?! 결과값이 달라졌다. 단지 포맷을 바꿨을뿐인데...그래서 곰곰히 생각해본 결과, 위 결과값은 문자열 비교로 인해 나타난 것이였다. Date는 moment.js에 의해서 Date타입에서 문자열로 변형된다. 그래서 >, < 연산자는 사전 순서로 문자열을 하나씩 비교한다. 이미지를 보면, 첫번째 포맷의 비교에선 '11'과 '08'에서의 문자열 비교로 값이 결정됬다. 두번째 포맷은 '11th'과 '8th'에서의 문자열 비교가 된다. 이런 신기방기한 일이 일어나다니 참 재미있는(?) 자바스크립트 세계이다. 어쨌든 결과값이 날짜와 포맷에 따라서 일정하지 않다는 것은 매우 안좋은 소식이다. Date 타입은 비교가 가능했지만, Moment로 변형을 시켜서 비교하는 것은 비추한다 라는 결론을 내릴 수 있다.

궁금증2 : 그럼 다시 moment를 숫자로 형변환하여 비교하면 어떻게 될까?

console.log(Number(momentNow) > Number(momentYesterday)); //false
console.log(Number(momentNow) < Number(momentYesterday)); //false

moment.js로 변형된 날짜를 비교

이 결과값은 예상이 가능? 모두 false가 나온다. 위에서 언급했듯이 moment.js로 변형되면 Date타입에서 문자열로 변형된다. 문자열을 숫자형을 변형시키면 NaN이 되기 때문에 NaN끼리의 비교는 false가 나온다.

console.log(Number(now) > Number(yesterday)); //true
console.log(Number(now) < Number(yesterday)); //false
console.log(Number(now), Number(yesterday));

Date객체를 숫자로 형변환하여 비교

Date는 원래도 비교가 가능했기 때문에 숫자로 형변환해도 비교가 가능하다. 또한 그 값을 찍어보면 아래와 같이 내부적으로 갖고 있는 숫자값(밀리초단위의 시간)이 출력된다.

console.log("type");
console.log(typeof now, typeof momentNow); //object string

다시 한 번 확인해보자. Date는 객체타입이고 moment.js에 의해서 변형된 Date는 문자열 타입이다.

궁금증3

이런 경우엔 어떻게 될까?

const sNow = Moment(new Date()).format("YYYYMMDDHHmm");
const sYesterday = Moment(new Date("2021-03-08")).format("YYYYMMDDHHmm");
console.log(sNow > sYesterday); 
console.log(sNow < sYesterday); 
console.log(Number(sNow) > Number(sYesterday)); 
console.log(Number(sNow) < Number(sYesterday)); 
console.log(sNow, sYesterday); 
결과값을 예상해보자 🔥
true false true false 202103110813(현재날짜시간) 202103080900

왜 이런 결과값이 나오는지 설명할 수 있을까?
먼저 Date타입은 moment.js에 의해서 문자열로 변형된다. 첫번째, 두번째 콘솔은 문자열끼리의 비교이다. 그런데 그 문자열의 포맷이 연월일시분(YYYYMMDDHHmm)으로 되어있다. 그렇기 때문에 비교를 하면 항상 날짜 비교과 동일한 결과값이 나온다.
이제 여기까지 나온 결과값에 숫자로 형변환을 한다. 그러면 그 문자열은 숫자로 변형이 되어 숫자끼리의 비교와 같아진다. 여기서 갑자기 머리가 아파질 수 있다. 앞에선 문자열을 형변환하면 NaN이 된다고 하지 않았나? 문자열을 숫자로 형변환하는 것은 그 때 그 때 달라욧!! 위의 예처럼 문자열 숫자라면 그 문자열은 숫자로 변환될 수 있고 비교 가능해진다. 결과적으로 moment.js로 위에 처럼 포맷팅을 하면 Date와 동일하게 비교를 할 수 있게 된다.

정리

자바스크립트는 이상한(?) 형변환에 때문에 위와 같은 현상이 발생한다. 기본적으로 객체타입은 숫자로 형변환이 이루어지면 NaN(Not A Number)으로 변형된다. 그래서 비교할 수 없는 상태(?)로 되며, NaN과의 비교는 무조건 false가 나온다.

console.log(Number({ age: 25 })); //NaN

그런데 객체 타입인 날짜는 내부적으로 숫자로 바뀌게 됨으로서 비교가능해지는 독특한 특징을 갖게 된다. 반면 moment.js로 변형된 날짜의 경우 문자열 타입을 갖게 된다. 문자열의 숫자로의 형변환은 어떤 문자인지에 따라서 다르다.

10 < 'jjanmo'
10 < '15'

첫번째는 당연히 false가 나오고 두번째는 true가 된다. 즉 숫자인 문자열은 형변환이 되어 숫자가 될 수 있지만, 숫자가 아닌 문자열은 NaN이 되어 비교할 수 없는 상태가 되는 것이다. 이처럼 moment.js로 변형된 날짜 문자열은 어떻게 변형되는냐에 따라서 비교가능할 수도, 비교 불가능할 수도 있다.

결론

그냥 Date로 비교가 가능한데, 누가 보면 굳이 왜 저런 요상망측한 짓을 하는 것이냐고 물을 수도 있다. 그런데 개발을 하다보면 다양한 상황들이 나오고 그 상황에 맞추다 보면 요상한 짓(?)을 해야할 때가 생긴다. 그러다보면 기본에 대한 생각을 잊어버리고 상황에 맞춘 해결책으로 문제를 해결하고 또 그 해결책이 잘 돌아간다. 해결여부에 초첨을 맞추다보면 표준, 기준, 기본에 대해서 잊고 지나가는 경우가 많아진다. 위 호기심은 단순 호기심에서 시작되었지만, 결국 자바스크립트의 요상한(?) 형변환에 대해서 다시 한 번 리뷰할 수 있는 시간이였다. 이때문에 강제형변환으로 인한 예상치 못한 결과가 나오는 코드를 쓰지 않도록, 될 수 있으면 기본에 충실한 코드, 예상이 가능한 코드를 쓰도록 노력해자라는 나름 의미있는 교훈을 얻을 수 있었다.

부족한 글 읽어주셔서 감사합니다. 내용에 대한 피드백은 언제나 환영입니다. 앞으로 조금이라도 도움이 될 수 있는 글을 쓰기 위해서 노력하겠습니다.

profile
눈길을 걸어갈 때 어지럽게 걷지 말기를.

6개의 댓글

comment-user-thumbnail
2021년 3월 11일

이게 멈니까!!!

1개의 답글
comment-user-thumbnail
2021년 3월 11일

다시 해주세요!!!

답글 달기
comment-user-thumbnail
2021년 3월 11일

좋은 글 감사합니다 ~

2개의 답글