트러블 슈팅 - '경계조건'과 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가지 문제점을 야기할 수 있다.
- 타임존 변환 문제
- toISOString()은 UTC로 변환
- 로컬 시간이 UTC-9 (한국)이면 날짜가 하루 전으로 밀릴 수 있다 - Date 생성자 파싱 문제
- new Date(month)에서 파싱할 때 예상과 다르게 해석될 수 있다. - 경계값 처리
- lte('date', '2025-08-30') 같이 30일로 잘못 계산되면 31일 데이터 누락이 발생한다.
결국, 말일은 복잡한 날짜 계산이 필요해서 타임존이나 Date 객체 처리 과정에서 오차가 발생했다고 볼 수 있다.
'개인 프로젝트 > 개인 기록장' 카테고리의 다른 글
| UX 개선 - 소프트 삭제, 낙관적 업데이트(삭제와 복구) (0) | 2025.09.04 |
|---|---|
| 트러블 슈팅 - 제약조건 변경에 따른 프로필 생성 누락 (0) | 2025.09.04 |
| Zod와 React Hook Form (0) | 2025.09.02 |
| 트러블 슈팅 - 프로필 생성 null과 TanStack Query의 retry (0) | 2025.09.01 |
| 프로젝트 기획 (0) | 2025.08.22 |
댓글