항상 잘 확인해야하는 시간 개념
최근 사내에서 시간과 관련된 이슈를 해결했었고, 앞으로도 자주 헷갈릴 수 있는 내용에 대해서 기록해보고자 합니다.
어떠한 이슈였나면...
현재 날짜로부터 지정된 개월 수 이전까지의 기간을 의미하는 [startDate, endDate]
형식의 배열을 반환하는 함수가 제대로 동작하지 않았습니다.
왜 제대로 동작하지 않았는지를 언급하기 전에, 함수에 대한 내용을 작성해보겠습니다.
시작날짜와 종료날짜를 반환하는 함수
함수의 입력값(매개변수)과 출력값(반환값)입니다.
- 입력값
- 지정된 개월 수(numberOfMonthsAgo)
- 출력값
- [시작 날짜(startDate), 종료 날짜(endDate)]
코드로 직접 확인해보겠습니다.
(보안상 사내 코드를 넣을 순 없으므로, 다른 코드로 대체하겠습니다.)
export const getPeriodStartEndBeforeMonth = (numberOfMonthsAgo: number = 3): [string, string] => {
// 현재 날짜 및 시간 가져오기
const today = new Date();
// N개월 전의 날짜 계산
const startDate = new Date(today.getFullYear(), today.getMonth() - numberOfMonthsAgo, today.getDate(), 0, 0, 0, 0);
// 오늘의 끝 시간 계산 (23:59:59)
const endDate = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 23, 59, 59, 999);
// 포맷팅 후 반환
return [formatDate(startDate), formatDate(endDate)];
}
export const formatDate = (date: Date): string => {
const pad = (n: number) => (n < 10 ? '0' + n : n);
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
}
해당 함수를 호출하는 시점을 2023년 12월 21일
이라고 가정해본다면, 함수의 반환 값이 어떻게 될까요?
getPeriodStartEndBeforeMonth(3)
저는 이렇게 같이 생각했는데, 혹시 이렇게 생각하셨나요?
["2023-09-21 00:00:00", "2023-12-21 23:59:59"]
정확한 반환 값은 시작 날짜에서 하루를 더한 날짜인 2023-09-22 00:00:00
이여야 합니다.
왜 이런 결과가 나와야하는지 확인해보겠습니다.
시간 개념에 대해서 짚고 넘어가기
00시 00분 00초부터 23:59:59초 까지를 1일(하루)로 간주하는 것이 시간 체계입니다.
만약, 시간 조건이 없는 상황을 생각해보겠습니다.
현재 날짜가 2023년 12월 21일이라면 3달 전은 언제일까요?
2023-09-21
~ 2023-12-21
입니다.
그렇다면, 시간 조건을 붙힌 결과를 한번 확인해보겠습니다.
["2023-09-21 00:00:00", "2023-12-21 23:59:59"]
- 시작 날짜인
2023-09-21 00:00:00
부터2023-09-21 23:59:59
까지는 1일입니다. - 종료 날짜인
2023-12-07 00:00:00
부터2023-12-07 23:59:59
까지도 1일입니다.
따라서, 시작 날짜와 종료날짜의 차이는 3달 하고도 하루가 차이가 납니다.
다시 쉽게 풀어서 작성해보면,
2023-09-21 00:00:00
~ 2023-12-21 00:00:00
은 3달이고,2023-12-21 00:00:00
~ 2023-12-21 23:59:59
은 하루입니다.
따라서, 3달 하고도 하루가 차이가 나는 것이였습니다.
이러한 이유로 함수가 제대로 동작하지 않았습니다.
그렇다면, 시작 날짜에 하루를 더한 날짜를 반환하도록 함수를 수정함으로써, 이를 해결할 수 있었습니다.
const startDate = new Date(today.getFullYear(), today.getMonth() - numberOfMonthsAgo, today.getDate() + 1, 0, 0, 0, 0);
함수를 수정 후 단위 테스트를 작성해야하는 상황에서 추가적으로 개선하면 좋을 것 같은 느낌을 받았습니다.
테스트하기 어려운 코드를 테스트하기 쉬운 코드로 변경
우선, 왜 테스트하기 어려운 코드인지... 아래 사진을 한번 보면 알아채기 쉽습니다.
getPeriodStartEndBeforeMonth
함수 안에서 날짜를 생성하고 있고, 이는 날짜와의 관계와 강하게 의존하고 있습니다.
'강하게 의존하는게 무슨 상관이야?' 라고 생각하실 수 있습니다.
하지만, 테스트 코드를 작성할 때 느슨하게 변경해야하는 명분이 생깁니다.
함수를 호출하는 시점과 테스트 코드를 실행하는 시점이 동일하다면, 항상 성공하지만..
다음날인, 2023년 12월 23일
에 실행하게 된다면.. 기대 값(expected)을 변경하지 않는 한, 함수는 항상 실패하게 됩니다.
이러한 경우 테스트 가능한 코드로 리팩토링하려면 강하게 결합되어있는 의존관계를 느슨하게 바꿔주어야 합니다.
그렇다면, 테스트 코드도 항상 성공할 수 있도록 변경할 수 있습니다.
이처럼 객체 간의 의존관계에 대한 결정권을, 의존관계를 가지는 객체가 가지는 것이 아닌 외부의 누군가 맡김으로써 해결했습니다.
마무리
사내에서 처음으로 이슈를 해결하고, 도움이 될 수 있다는 생각에 정말 뿌듯했습니다.
앞으로도, 좋은 코드를 만들 수 있도록 노력하고자 합니다.
긴글 읽어주셔서 감사합니다.
'생각' 카테고리의 다른 글
아는 것에서 모르는 것으로 (0) | 2024.03.03 |
---|---|
Chat GPT 써봤어요? (0) | 2023.11.03 |
좋은 코드(Good Code) (0) | 2023.09.30 |
댓글