본문 바로가기
개인 프로젝트/개인 기록장

트러블 슈팅 - 달력 월말 처리 문제와 date-fns 라이브러리

by 학습하는 청년 2025. 9. 4.

트러블 슈팅 - '경계조건'과 date-fns 라이브러리

트러블

달력에 다른 날짜들에 대한 값이 잘 나타나지만, 31일에 대해서는 값이 나타나지 않고 있다. 다른 월에 대해 확인해보니, 모든 월말에 대해 동일한 현상이 나타났다.

아마도, 날짜 계산에서 월말 처리 문제일 가능성이 높아보인다.

 

useBudget 훅의 월 범위 계산을 date-fns 라이브러리를 사용하여 해결하고자 한다.

why?

  • endOfMonth: 윤년, 30일/31일 등 모든 예외 상황을 자동으로 처리해주기 때문이다.
  • 유지보수성: 수동 계산에서 발생할 수 있는 오류(윤년, 월별 일수 차이 등)를 방지
  • 일관성: 프로젝트에서 이미 date-fnd 라이브러리를 사용하고 있다.

기존 코드

// 날짜 필터링
if (date) {
  incomeQuery = incomeQuery.eq('date', date);
  expenseQuery = expenseQuery.eq('date', date);
} else if (month) {
  const startOfMonth = `${month}-01`;
  const endOfMonth = new Date(
    new Date(month).getFullYear(),
    new Date(month).getMonth() + 1,
    0
  ).toISOString().split('T')[0];
  
  incomeQuery = incomeQuery.gte('date', startOfMonth).lte('date', endOfMonth);
  expenseQuery = expenseQuery.gte('date', startOfMonth).lte('date', endOfMonth);
}

 

수정 코드

if (date) {
  incomeQuery = incomeQuery.eq('date', date);
  expenseQuery = expenseQuery.eq('date', date);
} else if (month) {
  const startDate = startOfMonth(parseISO(`${month}-01`));
  const endDate = endOfMonth(parseISO(`${month}-01`));

  const startOfMonthStr = format(startDate, 'yyyy-MM-dd');
  const endOfMonthStr = format(endDate, 'yyyy-MM-dd');

  incomeQuery = incomeQuery.gte('date', startOfMonthStr).lte('date', endOfMonthStr);
  expenseQuery = expenseQuery.gte('date', startOfMonthStr).lte('date', endOfMonthStr);
}

 

 

Q. 어떤 차이가 있는가?

1. 복잡성

  • 기존: JavaScript Date 생성자의 트릭 사용 (getMonth() + 1, 0)
  • date-fns: 직관적인 함수명 (startOfMonth, endOfMonth)

2. 가독성

  • 기존: getMonth() + 1, 0 이 '이전 달 마지막 날'을 의미한다는 것을 알아야 한다.
  • date-fns: 함수명 자체가 의도를 명확히 표현

3. 에러 가능성

  • 기존: 수동 계산으로 인한 실수 발생 가능 (월 인덱스 혼동, 타임존 이슈 등)
  • date-fns: 검증된 라이브러리가 모든 예외 상황 처리

4. 타임존 처리

  • 기존: toISOString().splite('T')[0]로 UTC 변환 후 절삭
  • date-fns: format() 함수가 로컬 타임존 고려해서 안전하게 처리

5. 윤년/월별 일수

  • 기존: JavaScript의 Date 생성자 특성에 의존 (0일 = 이전 달 마지막 날)
  • date-fns: 내부적으로 모든 달력 규칙을 정확히 구현
결론: 스크린샷처럼 31일이 누락된 것은 기존 코드에서 월말 계산 시 경계 조건을 잘못 처리했기 때문이었다. date-fns를 사용하여 경계 조건 버그에 대응하였다.

Q. 왜 월말에서만 이런 문제가 나타날까?

매달 1일에 대해서는 다음과 같이 문자열 조합이라 문제없이 나타난다.

const startOfMonth = `${month}-01`;

 

하지만 기존 코드에서는 3가지 문제점을 야기할 수 있다.

  1. 타임존 변환 문제
    - toISOString()은 UTC로 변환
    - 로컬 시간이 UTC-9 (한국)이면 날짜가 하루 전으로 밀릴 수 있다
  2.  Date 생성자 파싱 문제
    - new Date(month)에서 파싱할 때 예상과 다르게 해석될 수 있다.
  3. 경계값 처리
    - lte('date', '2025-08-30') 같이 30일로 잘못 계산되면 31일 데이터 누락이 발생한다.
결국, 말일은 복잡한 날짜 계산이 필요해서 타임존이나 Date 객체 처리 과정에서 오차가 발생했다고 볼 수 있다.

댓글