본문 바로가기
생각

항상 잘 확인해야하는 시간 개념과 좋은 코드 만들기

by bkuk 2023. 12. 22.

항상 잘 확인해야하는 시간 개념

최근 사내에서 시간과 관련된 이슈를 해결했었고, 앞으로도 자주 헷갈릴 수 있는 내용에 대해서 기록해보고자 합니다.

어떠한 이슈였나면...

현재 날짜로부터 지정된 개월 수 이전까지의 기간을 의미하는 [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

댓글