자바스크립트로 상대시간 만들고 테스트코드 작성하기 (하루 전, 일주일 전…)
라이브러리 없이 자바스크립트로 상대시간을 만들어보자.
저는 바닐라 자바스크립트를 정말 좋아하지만 ㅋㅋ
이번엔 바닐라는 아니고 타입스크립트를 조금 사용해서 작성했습니다.
Dayjs와 같은 라이브러리를 사용하면 손쉽게 만들 수 있는데요,
저는 직접 만든다고 나댔지만 날짜는 라이브러리를 쓰는 게 좋겠다..는 생각입니다.. 꼭이요.. 😓
'날짜' 라고 한다면 역시 Date 객체부터 떠올릴 건데, '상대 시간을 어떻게 만들 것인가'가 오늘의 주제입니다.
Date 객체만으로도 만들 수 있겠지만, 오늘은 Date 객체와 Intl의 메서드를 사용해서 만들어보고자 해요.
Intl을 아시나요?
Intl은 국제화 API로 자바스크립트 내장 API되시는 분입니다.
사실 저는 너무 생소하게 느껴지는 API인데 로컬라이징 하시는 분들이라면 써보셨을 수도 있을 것 같단 생각이드네요.
하지만, 오늘은 국제화는 주제가 아니므로..!
'ko' 기준으로 날짜만 상대시간으로 바꿀 것이기 때문에 자세한 내용은 생략하고 본론부터 들어가볼게요.
Intl은 Intl.RelativeTimeFormat이라는 메서드를 제공하고 있는데 이걸 활용하면 상대 시간으로 쉽게 변환할 수 있다는 특징이 눈에 띄었어요. 다만, unit 값을 직접 정해줘야 한다는 점에서 약간의 연산이 필요할 수 있을 것 같더라고요.
유효한 unit 값은 다음과 같아요.
"year" : 년도
"quarter" : 분기
"month" : 달
"week" : 주
"day" : 일
"hour" : 시간
"minute" : 분
"second" : 초
저는 quarter는 필요 없을 것 같으니 분기를 제외하고 만들어볼 생각이에요.
Date 객체에 getTime 메소드를 사용해서 밀리초를 구하고, 밀리초를 초단위로 변환해주는 방법으로 로직을 만들었어요.
초단위로 변환한 것을 분, 분을 시.. 이런 식으로 계속 변환한 다음에 절대 값으로 비교해서 리턴하는 방법으로 코드를 작성해주었습니다. if문 지옥이 되긴 했네요. 🤔
date를 인자로 받고 있는데 Date 타입 객체가 아닌, string으로 받고 있다는 점에서 내부에서 예외처리를 한번 더 작성해주었어요.
Date 객체를 사용하지 않은 이유는 대충 날짜 비슷하게 생긴 string 타입으로 던져서 조금 더 범용성있게(?) 사용하고 싶었기 때문입니다.
제일 거슬리는 점은 역시 Math.round와 if(Math.abs~) 로직이 계속해서 반복된다는 점이 있겠습니단..ㅠ
공통 로직을 묶어서 리팩토링해볼 수 있을 것 같은데, 지금은 그것보다 더 중요한 게 잘 돌아가는 코드인가..?라서 우선순위는 조금 뒷전으로...ㅠ
if 문 분기에서 Math.abs를 사용하지 않을 경우 '~일 전', '어제'와 같은 형태로 나타날 텐데요,
현재 날짜 - 과거 날짜 로직 계산은 문제없지만, 과거 날짜 - 현재 날짜 라면 if문에서 모든 if문 조건에 충족하지 못해 undefined가 반환되는 문제가 있었어요.
이런 예외사항을 그때그때 호출하면서 확인할 수는 없으니 테스트 코드를 작성해서 조금이나마 해소하고자 해요.
테스트 도구는 vitest를 사용할 예정입니다.
테스트 짜려고 보니 내부에서 현재 날짜를 만들어주는 로직이 있어서 테스트하기가 쉽지 않다는 문제가 있네요.
이 부분은 파라미터로 받도록 변경해주었습니다. ( 전체 코드는 하단에 있어요 :) )
겸사겸사 파라미터 이름이 조금 아쉬운 것 같아서 파라미터 네이밍도 수정해주었어요.
역시 네이밍은 명료하게 짓기가 어렵네요. 또르르.. 😥
테스트 코드 작성
어차피 TC 짤거면서... 처음부터 TDD로 했어야 하나 싶은 생각이 듭니다.
이미 다 짜버렸지만 ㅋㅋㅋ 실패하는 코드부터 던져보겠습니다.
실패했으니 다시 값을 변경해서 성공하는 코드로 만들어줄게요.
기초적인 것만 알아서 테스팅 라이브러리를 잘 다루진 못하지만 나머지 TC도 추가해주겠습니다.
잘 되는 듯 했지만.. 뭔가 잘못된 로직을 발견해버렸어요. 😓
예상하는 날짜는 5일 전이지만 ㅋㅋ
'뭐여'는 제가 예상 값으로 아무거나 던진 값이고.. 실제 결과는 '지난주'가 리턴되고 있네요.
왜 지난주가 나오나 했더니 week 로직을 누락했던 걸 깜빡하고 중간에 추가해주었는데요,
이 week 부분이 잘못되어 있었어요.. ㅠ. ㅠ... day와 week가 겹치지 않도록 분기문을 다음과 같이 수정해주었습니다.
점점 비대해지며 슈퍼 함수가 되가는 중
끝으로 동일한 날짜 값을 넣을 경우 undefined가 되어버리는 문제도 테스트 코드 덕분에 발견할 수 있었어요.
그래서 초 단위 분기문은 디폴트 리턴 값으로 수정해주었습니다.
윤년 반영이 안되어 있는다는 점, 1년을 365일로 일반화했다는 점에서 100% 정확한 날짜 계산은 아니지만
대략적으로 현재 시간 대비 상대 시간을 계산해주는 로직을 완성했습니다.
시간 남으면 중복로직 리팩토링을 도전해보면 좋을 것 같은데
날짜 라이브러리 만드신 분들이 새삼 대단하다고 느껴짐을 깨닫는 순간..ㅋㅋ
후기
그냥 라이브러리 쓰자.