Refactor: Codebase review and cleanup
Review the entire codebase for potential issues and perform necessary cleanup.
This commit is contained in:
@@ -1,47 +1,85 @@
|
||||
|
||||
import { format, parse, addMonths, subMonths } from 'date-fns';
|
||||
import { ko } from 'date-fns/locale';
|
||||
|
||||
/**
|
||||
* 한글 월 이름 배열
|
||||
* 월 형식 검증 함수 (YYYY-MM 형식)
|
||||
*/
|
||||
export const MONTHS_KR = [
|
||||
'1월', '2월', '3월', '4월', '5월', '6월',
|
||||
'7월', '8월', '9월', '10월', '11월', '12월'
|
||||
];
|
||||
export const isValidMonth = (month: string): boolean => {
|
||||
const regex = /^\d{4}-(0[1-9]|1[0-2])$/;
|
||||
return regex.test(month);
|
||||
};
|
||||
|
||||
/**
|
||||
* 현재 월 가져오기
|
||||
* 현재 년월 가져오기
|
||||
*/
|
||||
export const getCurrentMonth = (): string => {
|
||||
const now = new Date();
|
||||
const month = now.getMonth(); // 0-indexed
|
||||
return `${MONTHS_KR[month]}`;
|
||||
return format(new Date(), 'yyyy-MM');
|
||||
};
|
||||
|
||||
/**
|
||||
* 이전 월 가져오기
|
||||
*/
|
||||
export const getPrevMonth = (currentMonth: string): string => {
|
||||
const currentMonthIdx = MONTHS_KR.findIndex(m => m === currentMonth);
|
||||
export const getPrevMonth = (month: string): string => {
|
||||
// 입력값 검증
|
||||
if (!isValidMonth(month)) {
|
||||
console.warn('유효하지 않은 월 형식:', month);
|
||||
return getCurrentMonth();
|
||||
}
|
||||
|
||||
if (currentMonthIdx === 0) {
|
||||
// 1월인 경우 12월로 변경
|
||||
return `${MONTHS_KR[11]}`;
|
||||
} else {
|
||||
const prevMonthIdx = currentMonthIdx - 1;
|
||||
return `${MONTHS_KR[prevMonthIdx]}`;
|
||||
try {
|
||||
// 월 문자열을 날짜로 파싱
|
||||
const date = parse(month, 'yyyy-MM', new Date());
|
||||
// 한 달 이전
|
||||
const prevMonth = subMonths(date, 1);
|
||||
// yyyy-MM 형식으로 반환
|
||||
return format(prevMonth, 'yyyy-MM');
|
||||
} catch (error) {
|
||||
console.error('이전 월 계산 중 오류:', error);
|
||||
return getCurrentMonth();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 다음 월 가져오기
|
||||
*/
|
||||
export const getNextMonth = (currentMonth: string): string => {
|
||||
const currentMonthIdx = MONTHS_KR.findIndex(m => m === currentMonth);
|
||||
export const getNextMonth = (month: string): string => {
|
||||
// 입력값 검증
|
||||
if (!isValidMonth(month)) {
|
||||
console.warn('유효하지 않은 월 형식:', month);
|
||||
return getCurrentMonth();
|
||||
}
|
||||
|
||||
if (currentMonthIdx === 11) {
|
||||
// 12월인 경우 1월로 변경
|
||||
return `${MONTHS_KR[0]}`;
|
||||
} else {
|
||||
const nextMonthIdx = currentMonthIdx + 1;
|
||||
return `${MONTHS_KR[nextMonthIdx]}`;
|
||||
try {
|
||||
// 월 문자열을 날짜로 파싱
|
||||
const date = parse(month, 'yyyy-MM', new Date());
|
||||
// 한 달 이후
|
||||
const nextMonth = addMonths(date, 1);
|
||||
// yyyy-MM 형식으로 반환
|
||||
return format(nextMonth, 'yyyy-MM');
|
||||
} catch (error) {
|
||||
console.error('다음 월 계산 중 오류:', error);
|
||||
return getCurrentMonth();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 표시 형식으로 변환 (yyyy년 MM월)
|
||||
*/
|
||||
export const formatMonthForDisplay = (month: string): string => {
|
||||
try {
|
||||
// 입력값 검증
|
||||
if (!isValidMonth(month)) {
|
||||
console.warn('유효하지 않은 월 형식:', month);
|
||||
return format(new Date(), 'yyyy년 MM월', { locale: ko });
|
||||
}
|
||||
|
||||
// 월 문자열을 날짜로 파싱
|
||||
const date = parse(month, 'yyyy-MM', new Date());
|
||||
// yyyy년 MM월 형식으로 반환 (한국어 로케일)
|
||||
return format(date, 'yyyy년 MM월', { locale: ko });
|
||||
} catch (error) {
|
||||
console.error('월 형식 변환 중 오류:', error);
|
||||
return month;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2,126 +2,115 @@
|
||||
import { Transaction } from '@/contexts/budget/types';
|
||||
import { parseTransactionDate } from '@/utils/dateParser';
|
||||
import { format } from 'date-fns';
|
||||
import { ko } from 'date-fns/locale';
|
||||
|
||||
/**
|
||||
* 월별로 트랜잭션 필터링 - 개선된 버전
|
||||
* 트랜잭션을 월별로 필터링
|
||||
*/
|
||||
export const filterTransactionsByMonth = (
|
||||
transactions: Transaction[],
|
||||
selectedMonth: string
|
||||
): Transaction[] => {
|
||||
console.log(`월별 트랜잭션 필터링: ${selectedMonth}, 총 데이터 수: ${transactions.length}`);
|
||||
|
||||
// 필터링 전 샘플 데이터 로그
|
||||
if (transactions.length > 0) {
|
||||
console.log('샘플 트랜잭션 날짜:', transactions.slice(0, 3).map(t => t.date));
|
||||
}
|
||||
|
||||
// 선택된 월의 숫자 추출 (ex: "4월" -> 4)
|
||||
const selectedMonthNumber = parseInt(selectedMonth.replace('월', ''));
|
||||
if (isNaN(selectedMonthNumber) || selectedMonthNumber < 1 || selectedMonthNumber > 12) {
|
||||
console.error('잘못된 월 형식:', selectedMonth);
|
||||
export const filterTransactionsByMonth = (transactions: Transaction[], selectedMonth: string): Transaction[] => {
|
||||
if (!transactions || transactions.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const filtered = transactions.filter(transaction => {
|
||||
// 트랜잭션 타입 확인 - 지출 항목만 포함
|
||||
if (transaction.type !== 'expense') {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
// 날짜가 없는 경우 필터링 제외
|
||||
if (!transaction.date) {
|
||||
console.warn('날짜 없는 트랜잭션:', transaction);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 날짜 파싱
|
||||
const parsedDate = parseTransactionDate(transaction.date);
|
||||
if (!parsedDate) {
|
||||
console.warn('날짜 파싱 실패:', transaction.date);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 월 비교
|
||||
const transactionMonth = parsedDate.getMonth() + 1; // 0-based -> 1-based
|
||||
const isMatchingMonth = transactionMonth === selectedMonthNumber;
|
||||
|
||||
if (isMatchingMonth) {
|
||||
console.log(`트랜잭션 매칭: ${transaction.title}, 날짜: ${transaction.date}, 월: ${transactionMonth}`);
|
||||
}
|
||||
|
||||
return isMatchingMonth;
|
||||
} catch (e) {
|
||||
console.error('트랜잭션 필터링 중 오류:', e, transaction);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`월별 필터링 결과: ${filtered.length}개 항목 (${selectedMonth})`);
|
||||
return filtered;
|
||||
};
|
||||
|
||||
/**
|
||||
* 검색어로 트랜잭션 필터링
|
||||
*/
|
||||
export const filterTransactionsByQuery = (
|
||||
transactions: Transaction[],
|
||||
searchQuery: string
|
||||
): Transaction[] => {
|
||||
if (!searchQuery.trim()) return transactions;
|
||||
console.log(`월별 필터링 시작: ${selectedMonth}, 트랜잭션 수: ${transactions.length}`);
|
||||
|
||||
const query = searchQuery.toLowerCase().trim();
|
||||
console.log(`검색어 필터링: "${query}"`);
|
||||
|
||||
const filtered = transactions.filter(transaction => {
|
||||
try {
|
||||
return (
|
||||
(transaction.title?.toLowerCase().includes(query)) ||
|
||||
(transaction.category?.toLowerCase().includes(query)) ||
|
||||
(transaction.paymentMethod?.toLowerCase().includes(query))
|
||||
);
|
||||
} catch (e) {
|
||||
console.error('검색어 필터링 중 오류:', e, transaction);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`검색어 필터링 결과: ${filtered.length}개 항목`);
|
||||
return filtered;
|
||||
};
|
||||
|
||||
/**
|
||||
* 총 지출 금액 계산 - 개선된 버전
|
||||
*/
|
||||
export const calculateTotalExpenses = (transactions: Transaction[]): number => {
|
||||
try {
|
||||
// 유효한 트랜잭션만 필터링 (undefined, null 제외)
|
||||
const validTransactions = transactions.filter(t => t && typeof t.amount !== 'undefined');
|
||||
console.log(`유효한 트랜잭션 수: ${validTransactions.length}/${transactions.length}`);
|
||||
const [year, month] = selectedMonth.split('-').map(Number);
|
||||
|
||||
// 디버깅용 로그
|
||||
if (validTransactions.length > 0) {
|
||||
console.log('첫 번째 트랜잭션 정보:', {
|
||||
title: validTransactions[0].title,
|
||||
amount: validTransactions[0].amount,
|
||||
type: validTransactions[0].type
|
||||
});
|
||||
}
|
||||
const filtered = transactions.filter(transaction => {
|
||||
const date = parseTransactionDate(transaction.date);
|
||||
|
||||
if (!date) {
|
||||
console.warn(`날짜를 파싱할 수 없음: ${transaction.date}, 트랜잭션 ID: ${transaction.id}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
const transactionYear = date.getFullYear();
|
||||
const transactionMonth = date.getMonth() + 1; // JavaScript 월은 0부터 시작하므로 +1
|
||||
|
||||
const match = transactionYear === year && transactionMonth === month;
|
||||
|
||||
if (match) {
|
||||
console.log(`트랜잭션 매칭: ${transaction.id}, 제목: ${transaction.title}, 날짜: ${transaction.date}`);
|
||||
}
|
||||
|
||||
return match;
|
||||
});
|
||||
|
||||
const total = validTransactions.reduce((sum, t) => {
|
||||
// 유효한 숫자인지 확인하고 기본값 처리
|
||||
const amount = typeof t.amount === 'number' ? t.amount :
|
||||
parseInt(t.amount as any) || 0;
|
||||
return sum + amount;
|
||||
}, 0);
|
||||
|
||||
console.log(`총 지출 계산: ${total}원 (${validTransactions.length}개 항목)`);
|
||||
return total;
|
||||
} catch (e) {
|
||||
console.error('총 지출 계산 중 오류:', e);
|
||||
return 0;
|
||||
console.log(`월별 필터링 결과: ${filtered.length}개 트랜잭션`);
|
||||
return filtered;
|
||||
} catch (error) {
|
||||
console.error('월별 필터링 중 오류:', error);
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 트랜잭션을 검색어로 필터링
|
||||
*/
|
||||
export const filterTransactionsByQuery = (transactions: Transaction[], searchQuery: string): Transaction[] => {
|
||||
if (!searchQuery || searchQuery.trim() === '') {
|
||||
return transactions;
|
||||
}
|
||||
|
||||
const normalizedQuery = searchQuery.toLowerCase().trim();
|
||||
|
||||
return transactions.filter(transaction => {
|
||||
const titleMatch = transaction.title.toLowerCase().includes(normalizedQuery);
|
||||
const categoryMatch = transaction.category.toLowerCase().includes(normalizedQuery);
|
||||
const amountMatch = transaction.amount.toString().includes(normalizedQuery);
|
||||
|
||||
return titleMatch || categoryMatch || amountMatch;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 총 지출 계산 (지출 타입만 포함)
|
||||
*/
|
||||
export const calculateTotalExpenses = (transactions: Transaction[]): number => {
|
||||
if (!transactions || transactions.length === 0) {
|
||||
console.log('계산할 트랜잭션이 없습니다.');
|
||||
return 0;
|
||||
}
|
||||
|
||||
console.log(`총 지출 계산 시작: 트랜잭션 ${transactions.length}개`);
|
||||
|
||||
// 지출 타입만 필터링하고 합산
|
||||
const expenses = transactions
|
||||
.filter(t => t.type === 'expense')
|
||||
.reduce((sum, transaction) => {
|
||||
const amount = Number(transaction.amount);
|
||||
if (isNaN(amount)) {
|
||||
console.warn(`유효하지 않은 금액: ${transaction.amount}, 트랜잭션 ID: ${transaction.id}`);
|
||||
return sum;
|
||||
}
|
||||
return sum + amount;
|
||||
}, 0);
|
||||
|
||||
console.log(`총 지출 계산 결과: ${expenses}원`);
|
||||
return expenses;
|
||||
};
|
||||
|
||||
/**
|
||||
* 트랜잭션을 날짜별로 그룹화
|
||||
*/
|
||||
export const groupTransactionsByDate = (transactions: Transaction[]): Record<string, Transaction[]> => {
|
||||
const groups: Record<string, Transaction[]> = {};
|
||||
|
||||
transactions.forEach(transaction => {
|
||||
const date = parseTransactionDate(transaction.date);
|
||||
if (!date) {
|
||||
console.warn(`날짜를 파싱할 수 없음: ${transaction.date}, 트랜잭션 ID: ${transaction.id}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const formattedDate = format(date, 'yyyy-MM-dd');
|
||||
|
||||
if (!groups[formattedDate]) {
|
||||
groups[formattedDate] = [];
|
||||
}
|
||||
|
||||
groups[formattedDate].push(transaction);
|
||||
});
|
||||
|
||||
return groups;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user