diff --git a/src/components/transactions/TransactionsHeader.tsx b/src/components/transactions/TransactionsHeader.tsx index cf5c3eb..8e241b3 100644 --- a/src/components/transactions/TransactionsHeader.tsx +++ b/src/components/transactions/TransactionsHeader.tsx @@ -24,6 +24,11 @@ const TransactionsHeader: React.FC = ({ totalExpenses, isDisabled }) => { + console.log('TransactionsHeader 렌더링:', { selectedMonth, totalExpenses }); + + // 예산 정보가 없는 경우 기본값 사용 + const targetAmount = budgetData?.monthly?.targetAmount || 0; + return (

지출 내역

@@ -70,7 +75,7 @@ const TransactionsHeader: React.FC = ({

총 예산

- {formatCurrency(budgetData?.monthly?.targetAmount || 0)} + {formatCurrency(targetAmount)}

diff --git a/src/contexts/budget/storage/transactionStorage.ts b/src/contexts/budget/storage/transactionStorage.ts index 27f1be2..843e8a4 100644 --- a/src/contexts/budget/storage/transactionStorage.ts +++ b/src/contexts/budget/storage/transactionStorage.ts @@ -10,24 +10,41 @@ export const loadTransactionsFromStorage = (): Transaction[] => { // 메인 스토리지에서 먼저 시도 const storedTransactions = localStorage.getItem('transactions'); if (storedTransactions) { - const parsedData = JSON.parse(storedTransactions); - console.log('트랜잭션 로드 완료, 항목 수:', parsedData.length); - return parsedData; + try { + const parsedData = JSON.parse(storedTransactions); + console.log('트랜잭션 로드 완료, 항목 수:', parsedData.length); + + // 트랜잭션 데이터 유효성 검사 + if (Array.isArray(parsedData)) { + return parsedData; + } else { + console.error('트랜잭션 데이터가 배열이 아닙니다:', typeof parsedData); + return []; + } + } catch (e) { + console.error('트랜잭션 데이터 파싱 오류:', e); + } } // 백업에서 시도 const backupTransactions = localStorage.getItem('transactions_backup'); if (backupTransactions) { - const parsedBackup = JSON.parse(backupTransactions); - console.log('백업에서 트랜잭션 복구, 항목 수:', parsedBackup.length); - // 메인 스토리지도 복구 - localStorage.setItem('transactions', backupTransactions); - return parsedBackup; + try { + const parsedBackup = JSON.parse(backupTransactions); + console.log('백업에서 트랜잭션 복구, 항목 수:', parsedBackup.length); + // 메인 스토리지도 복구 + localStorage.setItem('transactions', backupTransactions); + return parsedBackup; + } catch (e) { + console.error('백업 트랜잭션 데이터 파싱 오류:', e); + } } } catch (error) { - console.error('트랜잭션 데이터 파싱 오류:', error); + console.error('트랜잭션 데이터 로드 중 오류:', error); } - // 데이터가 없을 경우 빈 배열 반환 (샘플 데이터 생성하지 않음) + + // 데이터가 없을 경우 빈 배열 반환 + console.log('트랜잭션 데이터 없음, 빈 배열 반환'); return []; }; @@ -36,6 +53,8 @@ export const loadTransactionsFromStorage = (): Transaction[] => { */ export const saveTransactionsToStorage = (transactions: Transaction[]): void => { try { + console.log('트랜잭션 저장 시작, 항목 수:', transactions.length); + // 먼저 문자열로 변환 const dataString = JSON.stringify(transactions); @@ -49,6 +68,9 @@ export const saveTransactionsToStorage = (transactions: Transaction[]): void => // 스토리지 이벤트 수동 트리거 (동일 창에서도 감지하기 위함) try { window.dispatchEvent(new Event('transactionUpdated')); + window.dispatchEvent(new CustomEvent('transactionChanged', { + detail: { type: 'save', count: transactions.length } + })); window.dispatchEvent(new StorageEvent('storage', { key: 'transactions', newValue: dataString @@ -88,6 +110,9 @@ export const clearAllTransactions = (): void => { // 스토리지 이벤트 수동 트리거 window.dispatchEvent(new Event('transactionUpdated')); + window.dispatchEvent(new CustomEvent('transactionChanged', { + detail: { type: 'clear' } + })); window.dispatchEvent(new StorageEvent('storage', { key: 'transactions', newValue: emptyData diff --git a/src/hooks/transactions/dateUtils.ts b/src/hooks/transactions/dateUtils.ts index 97b48e5..511f031 100644 --- a/src/hooks/transactions/dateUtils.ts +++ b/src/hooks/transactions/dateUtils.ts @@ -1,23 +1,49 @@ +/** + * 한글 월 이름 배열 + */ +export const MONTHS_KR = [ + '1월', '2월', '3월', '4월', '5월', '6월', + '7월', '8월', '9월', '10월', '11월', '12월' +]; -// 월 이름 상수와 날짜 관련 유틸리티 함수 - -// 월 이름 상수 -export const MONTHS_KR = ['1월', '2월', '3월', '4월', '5월', '6월', '7월', '8월', '9월', '10월', '11월', '12월']; - -// 현재 월 가져오기 -export const getCurrentMonth = () => { - const today = new Date(); - return MONTHS_KR[today.getMonth()]; +/** + * 현재 월 가져오기 + */ +export const getCurrentMonth = (): string => { + const now = new Date(); + const month = now.getMonth(); // 0-indexed + const monthNumber = now.getMonth() + 1; // 1-indexed + return `${MONTHS_KR[month]} ${monthNumber}`; }; -// 이전 월 가져오기 -export const getPrevMonth = (currentMonth: string) => { - const index = MONTHS_KR.indexOf(currentMonth); - return index > 0 ? MONTHS_KR[index - 1] : MONTHS_KR[11]; +/** + * 이전 월 가져오기 + */ +export const getPrevMonth = (currentMonth: string): string => { + const parts = currentMonth.split(' '); + const currentMonthIdx = MONTHS_KR.findIndex(m => m === parts[0]); + + if (currentMonthIdx === 0) { + // 1월인 경우 12월로 변경 + return `${MONTHS_KR[11]} 12`; + } else { + const prevMonthIdx = currentMonthIdx - 1; + return `${MONTHS_KR[prevMonthIdx]} ${prevMonthIdx + 1}`; + } }; -// 다음 월 가져오기 -export const getNextMonth = (currentMonth: string) => { - const index = MONTHS_KR.indexOf(currentMonth); - return index < 11 ? MONTHS_KR[index + 1] : MONTHS_KR[0]; +/** + * 다음 월 가져오기 + */ +export const getNextMonth = (currentMonth: string): string => { + const parts = currentMonth.split(' '); + const currentMonthIdx = MONTHS_KR.findIndex(m => m === parts[0]); + + if (currentMonthIdx === 11) { + // 12월인 경우 1월로 변경 + return `${MONTHS_KR[0]} 1`; + } else { + const nextMonthIdx = currentMonthIdx + 1; + return `${MONTHS_KR[nextMonthIdx]} ${nextMonthIdx + 1}`; + } }; diff --git a/src/hooks/transactions/filterOperations/useFilterApplication.ts b/src/hooks/transactions/filterOperations/useFilterApplication.ts index 8e0a1e5..59c1eb0 100644 --- a/src/hooks/transactions/filterOperations/useFilterApplication.ts +++ b/src/hooks/transactions/filterOperations/useFilterApplication.ts @@ -17,47 +17,57 @@ export const useFilterApplication = ({ // 거래 필터링 함수 const filterTransactions = useCallback(() => { try { - // 현재 연도 가져오기 - const currentYear = new Date().getFullYear(); + console.log('필터링 시작, 전체 트랜잭션:', transactions.length); + console.log('선택된 월:', selectedMonth); - // 선택된 월에 대한 데이터 필터링 - const [selectedMonthName, selectedMonthNumber] = selectedMonth.split(' '); - const monthToFilter = parseInt(selectedMonthNumber); + // 선택된 월 정보 파싱 + const monthInfo = selectedMonth.split(' '); + const selectedMonthName = monthInfo[0]; // 월별 필터링 let filtered = transactions.filter(transaction => { if (!transaction.date) return false; - // 직접 저장된 date 문자열에서 날짜 추출 시도 + console.log(`트랜잭션 날짜 확인: "${transaction.date}", 타입: ${typeof transaction.date}`); + + // 다양한 날짜 형식 처리 + if (transaction.date.includes(selectedMonthName)) { + return true; // 선택된 월 이름이 포함된 경우 + } + + if (transaction.date.includes('오늘')) { + // 오늘 날짜가 해당 월인지 확인 + const today = new Date(); + const currentMonth = today.getMonth() + 1; // 0부터 시작하므로 +1 + const monthNumber = parseInt(monthInfo[1] || '0'); + return currentMonth === monthNumber; + } + + // 다른 형식의 날짜도 시도 try { - if (transaction.date.includes('오늘')) { - // '오늘, HH:MM' 형식인 경우 현재 월로 간주 - const today = new Date(); - return today.getMonth() + 1 === monthToFilter; - } else if (transaction.date.includes('년')) { - // 'YYYY년 MM월 DD일' 형식인 경우 - const monthPart = transaction.date.split('년')[1]?.trim().split('월')[0]; - if (monthPart) { - return parseInt(monthPart) === monthToFilter; - } - return false; - } else { - // ISO 문자열 또는 다른 표준 형식으로 저장된 경우 - const date = new Date(transaction.date); - if (!isNaN(date.getTime())) { - return date.getMonth() + 1 === monthToFilter; - } - return false; + // ISO 형식이 아닌 경우 처리 + if (transaction.date.includes('년') || transaction.date.includes('월')) { + return transaction.date.includes(selectedMonthName); + } + + // 표준 날짜 문자열 처리 시도 + const date = new Date(transaction.date); + if (!isNaN(date.getTime())) { + const transactionMonth = date.getMonth() + 1; + const monthNumber = parseInt(monthInfo[1] || '0'); + return transactionMonth === monthNumber; } } catch (e) { - console.error('날짜 파싱 오류:', e, transaction.date); - return false; + console.error('날짜 파싱 오류:', e); } + + // 기본적으로 모든 트랜잭션 포함 + return true; }); - console.log(`월별 필터링: ${selectedMonth} 트랜잭션 수: ${filtered.length}`); + console.log(`월별 필터링 결과: ${filtered.length} 트랜잭션`); - // 검색어에 따른 필터링 (추가) + // 검색어에 따른 필터링 if (searchQuery.trim()) { const searchLower = searchQuery.toLowerCase(); filtered = filtered.filter(transaction => @@ -65,14 +75,14 @@ export const useFilterApplication = ({ transaction.category.toLowerCase().includes(searchLower) || transaction.amount.toString().includes(searchQuery) ); + console.log(`검색어 필터링 결과: ${filtered.length} 트랜잭션`); } - // 필터링된 거래 설정 + // 결과 설정 setFilteredTransactions(filtered); - console.log(`필터링 결과: ${filtered.length} 트랜잭션`); + console.log('최종 필터링 결과:', filtered); } catch (error) { console.error('거래 필터링 중 오류:', error); - // 오류 발생 시 빈 배열 설정 setFilteredTransactions([]); } }, [transactions, selectedMonth, searchQuery, setFilteredTransactions]); diff --git a/src/hooks/transactions/filterOperations/useMonthSelection.ts b/src/hooks/transactions/filterOperations/useMonthSelection.ts index afb3f8b..51046c7 100644 --- a/src/hooks/transactions/filterOperations/useMonthSelection.ts +++ b/src/hooks/transactions/filterOperations/useMonthSelection.ts @@ -1,26 +1,31 @@ +import { useCallback } from 'react'; import { getPrevMonth, getNextMonth } from '../dateUtils'; /** * 월 선택 관련 훅 - * 이전/다음 월 선택 기능을 제공합니다. + * 이전/다음 월 이동 기능을 제공합니다. */ export const useMonthSelection = ({ - selectedMonth, - setSelectedMonth -}: { + selectedMonth, + setSelectedMonth +}: { selectedMonth: string; setSelectedMonth: (month: string) => void; }) => { - // 이전 월로 변경 - const handlePrevMonth = () => { - setSelectedMonth(getPrevMonth(selectedMonth)); - }; + // 이전 월로 이동 + const handlePrevMonth = useCallback(() => { + const prevMonth = getPrevMonth(selectedMonth); + console.log(`월 변경: ${selectedMonth} -> ${prevMonth}`); + setSelectedMonth(prevMonth); + }, [selectedMonth, setSelectedMonth]); - // 다음 월로 변경 - const handleNextMonth = () => { - setSelectedMonth(getNextMonth(selectedMonth)); - }; + // 다음 월로 이동 + const handleNextMonth = useCallback(() => { + const nextMonth = getNextMonth(selectedMonth); + console.log(`월 변경: ${selectedMonth} -> ${nextMonth}`); + setSelectedMonth(nextMonth); + }, [selectedMonth, setSelectedMonth]); return { handlePrevMonth, diff --git a/src/hooks/transactions/transactionOperations/deleteTransaction.ts b/src/hooks/transactions/transactionOperations/deleteTransaction.ts index d66b321..80e79ad 100644 --- a/src/hooks/transactions/transactionOperations/deleteTransaction.ts +++ b/src/hooks/transactions/transactionOperations/deleteTransaction.ts @@ -3,6 +3,7 @@ import { useCallback, useRef } from 'react'; import { Transaction } from '@/components/TransactionCard'; import { useAuth } from '@/contexts/auth/AuthProvider'; import { useDeleteTransactionCore } from './deleteOperation/deleteTransactionCore'; +import { toast } from '@/hooks/useToast.wrapper'; /** * 트랜잭션 삭제 기능 @@ -17,5 +18,42 @@ export const useDeleteTransaction = ( const { user } = useAuth(); // 핵심 삭제 로직 사용 - return useDeleteTransactionCore(transactions, setTransactions, user, pendingDeletionRef); + const deleteTransactionHandler = useDeleteTransactionCore(transactions, setTransactions, user, pendingDeletionRef); + + // 디버깅 추가 + const deleteTransaction = useCallback((id: string) => { + console.log('트랜잭션 삭제 시작:', id); + + try { + // 이미 삭제 중인지 확인 + if (pendingDeletionRef.current.has(id)) { + console.log('이미 삭제 중인 트랜잭션:', id); + return; + } + + // 트랜잭션이 존재하는지 확인 + const transactionExists = transactions.some(t => t.id === id); + if (!transactionExists) { + console.error('존재하지 않는 트랜잭션 ID:', id); + toast({ + title: "삭제 실패", + description: "존재하지 않는 트랜잭션입니다.", + variant: "destructive" + }); + return; + } + + // 삭제 실행 + return deleteTransactionHandler(id); + } catch (error) { + console.error('트랜잭션 삭제 오류:', error); + toast({ + title: "삭제 실패", + description: "트랜잭션 삭제 중 오류가 발생했습니다.", + variant: "destructive" + }); + } + }, [transactions, deleteTransactionHandler]); + + return deleteTransaction; };