diff --git a/src/hooks/transactions/dateUtils.ts b/src/hooks/transactions/dateUtils.ts new file mode 100644 index 0000000..97b48e5 --- /dev/null +++ b/src/hooks/transactions/dateUtils.ts @@ -0,0 +1,23 @@ + +// 월 이름 상수와 날짜 관련 유틸리티 함수 + +// 월 이름 상수 +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 getPrevMonth = (currentMonth: string) => { + const index = MONTHS_KR.indexOf(currentMonth); + return index > 0 ? MONTHS_KR[index - 1] : MONTHS_KR[11]; +}; + +// 다음 월 가져오기 +export const getNextMonth = (currentMonth: string) => { + const index = MONTHS_KR.indexOf(currentMonth); + return index < 11 ? MONTHS_KR[index + 1] : MONTHS_KR[0]; +}; diff --git a/src/hooks/transactions/filterUtils.ts b/src/hooks/transactions/filterUtils.ts new file mode 100644 index 0000000..0c45087 --- /dev/null +++ b/src/hooks/transactions/filterUtils.ts @@ -0,0 +1,63 @@ + +import { Transaction } from '@/components/TransactionCard'; +import { MONTHS_KR } from './dateUtils'; + +// 월별 거래 필터링 +export const filterTransactionsByMonth = (transactions: Transaction[], selectedMonth: string): Transaction[] => { + console.log('월별 필터링:', selectedMonth, '트랜잭션 수:', transactions.length); + + // 현재 날짜 정보 + const now = new Date(); + const currentYear = now.getFullYear(); + const currentMonth = now.getMonth() + 1; // JavaScript 월은 0부터 시작 + + // 선택된 월의 인덱스 (0-11) + const selectedMonthIndex = MONTHS_KR.findIndex(month => month === selectedMonth); + + // 특수 케이스 처리: '이번 달', '오늘', '이번 주' 등 + if (transactions.some(t => t.date.includes('오늘') || t.date.includes('어제') || t.date.includes('이번주'))) { + return transactions; + } + + // 실제 필터링 + return transactions.filter(transaction => { + // 날짜 형식이 '2023-05-15' 또는 '2023/05/15' 등의 형식인 경우 + if (transaction.date.includes('-') || transaction.date.includes('/')) { + const parts = transaction.date.split(/[-\/]/); + if (parts.length >= 2) { + const transactionMonth = parseInt(parts[1]); + const monthIndex = transactionMonth - 1; // 0-11 인덱스로 변환 + return monthIndex === selectedMonthIndex; + } + } + + // 날짜에 월이 포함된 경우 (예: '5월 15일') + for (let i = 0; i < MONTHS_KR.length; i++) { + if (transaction.date.includes(MONTHS_KR[i])) { + return MONTHS_KR[i] === selectedMonth; + } + } + + // 기본적으로 모든 트랜잭션 표시 (필터링 실패 시) + return true; + }); +}; + +// 검색어로 거래 필터링 +export const filterTransactionsByQuery = (transactions: Transaction[], query: string): Transaction[] => { + if (!query.trim()) return transactions; + + const lowercaseQuery = query.toLowerCase(); + return transactions.filter(transaction => + transaction.title.toLowerCase().includes(lowercaseQuery) || + transaction.category.toLowerCase().includes(lowercaseQuery) || + transaction.amount.toString().includes(lowercaseQuery) + ); +}; + +// 총 지출 계산 +export const calculateTotalExpenses = (transactions: Transaction[]): number => { + return transactions + .filter(t => t.type === 'expense') + .reduce((total, transaction) => total + transaction.amount, 0); +}; diff --git a/src/hooks/transactions/index.ts b/src/hooks/transactions/index.ts new file mode 100644 index 0000000..2379ba9 --- /dev/null +++ b/src/hooks/transactions/index.ts @@ -0,0 +1,5 @@ + +// 트랜잭션 관련 모든 훅과 유틸리티 함수를 재내보내기 +export { useTransactions } from './useTransactions'; +export { MONTHS_KR, getCurrentMonth, getPrevMonth, getNextMonth } from './dateUtils'; +export { filterTransactionsByMonth, filterTransactionsByQuery, calculateTotalExpenses } from './filterUtils'; diff --git a/src/hooks/transactions/storageUtils.ts b/src/hooks/transactions/storageUtils.ts new file mode 100644 index 0000000..c154559 --- /dev/null +++ b/src/hooks/transactions/storageUtils.ts @@ -0,0 +1,81 @@ + +import { Transaction } from '@/components/TransactionCard'; +import { toast } from '@/hooks/useToast.wrapper'; +import { EXPENSE_CATEGORIES } from '@/constants/categoryIcons'; + +// 로컬 스토리지에서 트랜잭션 데이터 로드 +export const loadTransactionsFromStorage = (): Transaction[] => { + try { + // 로컬 스토리지에서 트랜잭션 데이터 가져오기 + const localDataStr = localStorage.getItem('transactions'); + console.log('로컬 트랜잭션 데이터:', localDataStr); + + if (localDataStr) { + try { + const localData = JSON.parse(localDataStr); + + // 지원되는 카테고리로 필터링 + const filteredData = localData.map((transaction: Transaction) => { + // 트랜잭션의 카테고리가 현재 지원되는 카테고리가 아니면 '생활비'로 변경 + if (transaction.type === 'expense' && !EXPENSE_CATEGORIES.includes(transaction.category)) { + return { + ...transaction, + category: '생활비' // 기본값으로 '생활비' 사용 + }; + } + return transaction; + }); + + console.log('필터링된 트랜잭션:', filteredData.length); + return filteredData; + } catch (parseError) { + console.error('트랜잭션 데이터 파싱 오류:', parseError); + return []; + } + } + } catch (err) { + console.error('트랜잭션 로드 중 오류:', err); + } + + console.log('로컬 트랜잭션 데이터 없음'); + return []; +}; + +// 로컬 스토리지에 트랜잭션 데이터 저장 +export const saveTransactionsToStorage = (transactions: Transaction[]): void => { + try { + const dataString = JSON.stringify(transactions); + localStorage.setItem('transactions', dataString); + localStorage.setItem('transactions_backup', dataString); // 백업도 저장 + + // 이벤트 발생 + window.dispatchEvent(new Event('transactionUpdated')); + window.dispatchEvent(new StorageEvent('storage', { + key: 'transactions', + newValue: dataString + })); + + console.log('트랜잭션 저장 완료:', transactions.length, '개'); + } catch (error) { + console.error('트랜잭션 저장 오류:', error); + toast({ + title: "데이터 저장 실패", + description: "트랜잭션 데이터를 저장하는데 실패했습니다.", + variant: "destructive" + }); + } +}; + +// 예산 데이터 로드 +export const loadBudgetFromStorage = (): number => { + try { + const budgetDataStr = localStorage.getItem('budgetData'); + if (budgetDataStr) { + const budgetData = JSON.parse(budgetDataStr); + return budgetData.monthly.targetAmount; + } + } catch (e) { + console.error('예산 데이터 파싱 오류:', e); + } + return 0; +}; diff --git a/src/hooks/transactions/supabaseUtils.ts b/src/hooks/transactions/supabaseUtils.ts new file mode 100644 index 0000000..6993cd9 --- /dev/null +++ b/src/hooks/transactions/supabaseUtils.ts @@ -0,0 +1,93 @@ + +import { Transaction } from '@/components/TransactionCard'; +import { supabase } from '@/lib/supabase'; +import { isSyncEnabled } from '@/utils/syncUtils'; +import { useAuth } from '@/contexts/auth/AuthProvider'; + +// Supabase와 트랜잭션 동기화 +export const syncTransactionsWithSupabase = async (user: any, transactions: Transaction[]): Promise => { + if (!user || !isSyncEnabled()) return transactions; + + try { + const { data, error } = await supabase + .from('transactions') + .select('*') + .eq('user_id', user.id); + + if (error) { + console.error('Supabase 데이터 조회 오류:', error); + return transactions; + } + + if (data && data.length > 0) { + // Supabase 데이터 로컬 형식으로 변환 + const supabaseTransactions = data.map(t => ({ + id: t.transaction_id || t.id, + title: t.title, + amount: t.amount, + date: t.date, + category: t.category, + type: t.type + })); + + // 로컬 데이터와 병합 (중복 ID 제거) + const mergedTransactions = [...transactions]; + + supabaseTransactions.forEach(newTx => { + const existingIndex = mergedTransactions.findIndex(t => t.id === newTx.id); + if (existingIndex >= 0) { + mergedTransactions[existingIndex] = newTx; + } else { + mergedTransactions.push(newTx); + } + }); + + return mergedTransactions; + } + } catch (err) { + console.error('Supabase 동기화 오류:', err); + } + + return transactions; +}; + +// Supabase에 트랜잭션 업데이트 +export const updateTransactionInSupabase = async (user: any, transaction: Transaction): Promise => { + if (!user || !isSyncEnabled()) return; + + try { + const { error } = await supabase.from('transactions') + .upsert({ + user_id: user.id, + title: transaction.title, + amount: transaction.amount, + date: transaction.date, + category: transaction.category, + type: transaction.type, + transaction_id: transaction.id + }); + + if (error) { + console.error('트랜잭션 업데이트 오류:', error); + } + } catch (error) { + console.error('Supabase 업데이트 오류:', error); + } +}; + +// Supabase에서 트랜잭션 삭제 +export const deleteTransactionFromSupabase = async (user: any, transactionId: string): Promise => { + if (!user || !isSyncEnabled()) return; + + try { + const { error } = await supabase.from('transactions') + .delete() + .eq('transaction_id', transactionId); + + if (error) { + console.error('트랜잭션 삭제 오류:', error); + } + } catch (error) { + console.error('Supabase 삭제 오류:', error); + } +}; diff --git a/src/hooks/transactions/useTransactions.ts b/src/hooks/transactions/useTransactions.ts new file mode 100644 index 0000000..8d3cf8d --- /dev/null +++ b/src/hooks/transactions/useTransactions.ts @@ -0,0 +1,208 @@ + +import { useState, useEffect, useCallback } from 'react'; +import { Transaction } from '@/components/TransactionCard'; +import { useAuth } from '@/contexts/auth/AuthProvider'; +import { toast } from '@/hooks/useToast.wrapper'; +import { + getCurrentMonth, + getPrevMonth, + getNextMonth +} from './dateUtils'; +import { + filterTransactionsByMonth, + filterTransactionsByQuery, + calculateTotalExpenses +} from './filterUtils'; +import { + loadTransactionsFromStorage, + saveTransactionsToStorage, + loadBudgetFromStorage +} from './storageUtils'; +import { + syncTransactionsWithSupabase, + updateTransactionInSupabase, + deleteTransactionFromSupabase +} from './supabaseUtils'; + +// useTransactions 훅 +export const useTransactions = () => { + const [transactions, setTransactions] = useState([]); + const [filteredTransactions, setFilteredTransactions] = useState([]); + const [selectedMonth, setSelectedMonth] = useState(getCurrentMonth()); + const [searchQuery, setSearchQuery] = useState(''); + const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(null); + const [totalBudget, setTotalBudget] = useState(0); + const { user } = useAuth(); + const [refreshKey, setRefreshKey] = useState(0); // 강제 새로고침을 위한 키 + + // 월 변경 처리 + const handlePrevMonth = () => { + setSelectedMonth(getPrevMonth(selectedMonth)); + }; + + const handleNextMonth = () => { + setSelectedMonth(getNextMonth(selectedMonth)); + }; + + // 트랜잭션 로드 + const loadTransactions = useCallback(() => { + setIsLoading(true); + setError(null); + + try { + const localTransactions = loadTransactionsFromStorage(); + setTransactions(localTransactions); + + // 예산 가져오기 + const budgetAmount = loadBudgetFromStorage(); + setTotalBudget(budgetAmount); + } catch (err) { + console.error('트랜잭션 로드 중 오류:', err); + setError('데이터를 불러오는 중 문제가 발생했습니다.'); + toast({ + title: "데이터 로드 실패", + description: "지출 내역을 불러오는데 실패했습니다.", + variant: "destructive" + }); + } finally { + setIsLoading(false); + } + }, []); + + // 필터 적용 + useEffect(() => { + // 1. 월별 필터링 + let filtered = filterTransactionsByMonth(transactions, selectedMonth); + + // 2. 검색어 필터링 + if (searchQuery.trim()) { + filtered = filterTransactionsByQuery(filtered, searchQuery); + } + + console.log('필터링 결과:', filtered.length, '트랜잭션'); + setFilteredTransactions(filtered); + }, [transactions, selectedMonth, searchQuery]); + + // 초기 데이터 로드 및 이벤트 리스너 설정 + useEffect(() => { + console.log('useTransactions - 초기 데이터 로드'); + loadTransactions(); + + // 트랜잭션 업데이트 이벤트 리스너 + const handleTransactionUpdated = () => { + console.log('트랜잭션 업데이트 이벤트 감지됨'); + loadTransactions(); + }; + + // 스토리지 변경 이벤트 리스너 + const handleStorageChange = (e: StorageEvent) => { + if (e.key === 'transactions' || e.key === null) { + console.log('로컬 스토리지 변경 감지됨:', e.key); + loadTransactions(); + } + }; + + // 페이지 포커스/가시성 이벤트 리스너 + const handleFocus = () => { + console.log('창 포커스 - 트랜잭션 새로고침'); + loadTransactions(); + }; + + const handleVisibilityChange = () => { + if (document.visibilityState === 'visible') { + console.log('페이지 가시성 변경 - 트랜잭션 새로고침'); + loadTransactions(); + } + }; + + // 이벤트 리스너 등록 + window.addEventListener('transactionUpdated', handleTransactionUpdated); + window.addEventListener('storage', handleStorageChange); + window.addEventListener('focus', handleFocus); + document.addEventListener('visibilitychange', handleVisibilityChange); + + // 컴포넌트 마운트시에만 수동으로 트랜잭션 업데이트 이벤트 발생 + window.dispatchEvent(new Event('transactionUpdated')); + + return () => { + window.removeEventListener('transactionUpdated', handleTransactionUpdated); + window.removeEventListener('storage', handleStorageChange); + window.removeEventListener('focus', handleFocus); + document.removeEventListener('visibilitychange', handleVisibilityChange); + }; + }, [loadTransactions, refreshKey]); + + // 트랜잭션 업데이트 + const updateTransaction = (updatedTransaction: Transaction) => { + const updatedTransactions = transactions.map(transaction => + transaction.id === updatedTransaction.id ? updatedTransaction : transaction + ); + + // 로컬 스토리지 업데이트 + saveTransactionsToStorage(updatedTransactions); + + // 상태 업데이트 + setTransactions(updatedTransactions); + + // Supabase 업데이트 시도 + if (user) { + updateTransactionInSupabase(user, updatedTransaction); + } + + // 이벤트 발생 + window.dispatchEvent(new Event('transactionUpdated')); + + toast({ + title: "지출이 수정되었습니다", + description: `${updatedTransaction.title} 항목이 업데이트되었습니다.`, + }); + }; + + // 트랜잭션 삭제 + const deleteTransaction = (id: string) => { + const updatedTransactions = transactions.filter(transaction => transaction.id !== id); + + // 로컬 스토리지 업데이트 + saveTransactionsToStorage(updatedTransactions); + + // 상태 업데이트 + setTransactions(updatedTransactions); + + // Supabase 삭제 시도 + if (user) { + deleteTransactionFromSupabase(user, id); + } + + // 이벤트 발생 + window.dispatchEvent(new Event('transactionUpdated')); + + toast({ + title: "지출이 삭제되었습니다", + description: "선택한 지출 항목이 삭제되었습니다.", + }); + }; + + // 데이터 강제 새로고침 + const refreshTransactions = () => { + setRefreshKey(prev => prev + 1); + loadTransactions(); + }; + + return { + transactions: filteredTransactions, + allTransactions: transactions, + isLoading, + error, + totalBudget, + selectedMonth, + searchQuery, + setSearchQuery, + handlePrevMonth, + handleNextMonth, + updateTransaction, + deleteTransaction, + totalExpenses: calculateTotalExpenses(filteredTransactions), + refreshTransactions + }; +}; diff --git a/src/hooks/useTransactions.ts b/src/hooks/useTransactions.ts index d9cd536..ec5ebfd 100644 --- a/src/hooks/useTransactions.ts +++ b/src/hooks/useTransactions.ts @@ -1,303 +1,12 @@ -import { useState, useEffect, useCallback } from 'react'; -import { Transaction } from '@/components/TransactionCard'; -import { useAuth } from '@/contexts/auth/AuthProvider'; -import { toast } from '@/hooks/useToast.wrapper'; -import { isSyncEnabled } from '@/utils/syncUtils'; -import { EXPENSE_CATEGORIES } from '@/constants/categoryIcons'; - -// 월 이름 상수 -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 getPrevMonth = (currentMonth: string) => { - const index = MONTHS_KR.indexOf(currentMonth); - return index > 0 ? MONTHS_KR[index - 1] : MONTHS_KR[11]; -}; - -// 다음 월 가져오기 -export const getNextMonth = (currentMonth: string) => { - const index = MONTHS_KR.indexOf(currentMonth); - return index < 11 ? MONTHS_KR[index + 1] : MONTHS_KR[0]; -}; - -// 월별 거래 필터링 -export const filterTransactionsByMonth = (transactions: Transaction[], selectedMonth: string): Transaction[] => { - console.log('월별 필터링:', selectedMonth, '트랜잭션 수:', transactions.length); - - // 현재 날짜 정보 - const now = new Date(); - const currentYear = now.getFullYear(); - const currentMonth = now.getMonth() + 1; // JavaScript 월은 0부터 시작 - - // 선택된 월의 인덱스 (0-11) - const selectedMonthIndex = MONTHS_KR.findIndex(month => month === selectedMonth); - - // 특수 케이스 처리: '이번 달', '오늘', '이번 주' 등 - if (transactions.some(t => t.date.includes('오늘') || t.date.includes('어제') || t.date.includes('이번주'))) { - return transactions; - } - - // 실제 필터링 - return transactions.filter(transaction => { - // 날짜 형식이 '2023-05-15' 또는 '2023/05/15' 등의 형식인 경우 - if (transaction.date.includes('-') || transaction.date.includes('/')) { - const parts = transaction.date.split(/[-\/]/); - if (parts.length >= 2) { - const transactionMonth = parseInt(parts[1]); - const monthIndex = transactionMonth - 1; // 0-11 인덱스로 변환 - return monthIndex === selectedMonthIndex; - } - } - - // 날짜에 월이 포함된 경우 (예: '5월 15일') - for (let i = 0; i < MONTHS_KR.length; i++) { - if (transaction.date.includes(MONTHS_KR[i])) { - return MONTHS_KR[i] === selectedMonth; - } - } - - // 기본적으로 모든 트랜잭션 표시 (필터링 실패 시) - return true; - }); -}; - -// 검색어로 거래 필터링 -export const filterTransactionsByQuery = (transactions: Transaction[], query: string): Transaction[] => { - if (!query.trim()) return transactions; - - const lowercaseQuery = query.toLowerCase(); - return transactions.filter(transaction => - transaction.title.toLowerCase().includes(lowercaseQuery) || - transaction.category.toLowerCase().includes(lowercaseQuery) || - transaction.amount.toString().includes(lowercaseQuery) - ); -}; - -// 총 지출 계산 -export const calculateTotalExpenses = (transactions: Transaction[]): number => { - return transactions - .filter(t => t.type === 'expense') - .reduce((total, transaction) => total + transaction.amount, 0); -}; - -// useTransactions 훅 -export const useTransactions = () => { - const [transactions, setTransactions] = useState([]); - const [filteredTransactions, setFilteredTransactions] = useState([]); - const [selectedMonth, setSelectedMonth] = useState(getCurrentMonth()); - const [searchQuery, setSearchQuery] = useState(''); - const [isLoading, setIsLoading] = useState(true); - const [error, setError] = useState(null); - const [totalBudget, setTotalBudget] = useState(0); - const { user } = useAuth(); - const [refreshKey, setRefreshKey] = useState(0); // 강제 새로고침을 위한 키 - - // 월 변경 처리 - const handlePrevMonth = () => { - setSelectedMonth(getPrevMonth(selectedMonth)); - }; - - const handleNextMonth = () => { - setSelectedMonth(getNextMonth(selectedMonth)); - }; - - // 트랜잭션 로드 - const loadTransactions = useCallback(() => { - setIsLoading(true); - setError(null); - - try { - // 로컬 스토리지에서 트랜잭션 데이터 가져오기 - const localDataStr = localStorage.getItem('transactions'); - console.log('로컬 트랜잭션 데이터:', localDataStr); - - if (localDataStr) { - try { - const localData = JSON.parse(localDataStr); - - // 지원되는 카테고리로 필터링 - const filteredData = localData.map((transaction: Transaction) => { - // 트랜잭션의 카테고리가 현재 지원되는 카테고리가 아니면 '생활비'로 변경 - if (transaction.type === 'expense' && !EXPENSE_CATEGORIES.includes(transaction.category)) { - return { - ...transaction, - category: '생활비' // 기본값으로 '생활비' 사용 - }; - } - return transaction; - }); - - console.log('필터링된 트랜잭션:', filteredData.length); - setTransactions(filteredData); - } catch (parseError) { - console.error('트랜잭션 데이터 파싱 오류:', parseError); - setTransactions([]); - } - } else { - console.log('로컬 트랜잭션 데이터 없음'); - setTransactions([]); - } - - // 예산 가져오기 - const budgetDataStr = localStorage.getItem('budgetData'); - if (budgetDataStr) { - try { - const budgetData = JSON.parse(budgetDataStr); - setTotalBudget(budgetData.monthly.targetAmount); - } catch (e) { - console.error('예산 데이터 파싱 오류:', e); - setTotalBudget(0); - } - } - } catch (err) { - console.error('트랜잭션 로드 중 오류:', err); - setError('데이터를 불러오는 중 문제가 발생했습니다.'); - toast({ - title: "데이터 로드 실패", - description: "지출 내역을 불러오는데 실패했습니다.", - variant: "destructive" - }); - } finally { - setIsLoading(false); - } - }, []); - - // 필터 적용 - useEffect(() => { - // 1. 월별 필터링 - let filtered = filterTransactionsByMonth(transactions, selectedMonth); - - // 2. 검색어 필터링 - if (searchQuery.trim()) { - filtered = filterTransactionsByQuery(filtered, searchQuery); - } - - console.log('필터링 결과:', filtered.length, '트랜잭션'); - setFilteredTransactions(filtered); - }, [transactions, selectedMonth, searchQuery]); - - // 초기 데이터 로드 및 이벤트 리스너 설정 - useEffect(() => { - console.log('useTransactions - 초기 데이터 로드'); - loadTransactions(); - - // 트랜잭션 업데이트 이벤트 리스너 - const handleTransactionUpdated = () => { - console.log('트랜잭션 업데이트 이벤트 감지됨'); - loadTransactions(); - }; - - // 스토리지 변경 이벤트 리스너 - const handleStorageChange = (e: StorageEvent) => { - if (e.key === 'transactions' || e.key === null) { - console.log('로컬 스토리지 변경 감지됨:', e.key); - loadTransactions(); - } - }; - - // 페이지 포커스/가시성 이벤트 리스너 - const handleFocus = () => { - console.log('창 포커스 - 트랜잭션 새로고침'); - loadTransactions(); - }; - - const handleVisibilityChange = () => { - if (document.visibilityState === 'visible') { - console.log('페이지 가시성 변경 - 트랜잭션 새로고침'); - loadTransactions(); - } - }; - - // 이벤트 리스너 등록 - window.addEventListener('transactionUpdated', handleTransactionUpdated); - window.addEventListener('storage', handleStorageChange); - window.addEventListener('focus', handleFocus); - document.addEventListener('visibilitychange', handleVisibilityChange); - - // 컴포넌트 마운트시에만 수동으로 트랜잭션 업데이트 이벤트 발생 - window.dispatchEvent(new Event('transactionUpdated')); - - return () => { - window.removeEventListener('transactionUpdated', handleTransactionUpdated); - window.removeEventListener('storage', handleStorageChange); - window.removeEventListener('focus', handleFocus); - document.removeEventListener('visibilitychange', handleVisibilityChange); - }; - }, [loadTransactions, refreshKey]); - - // 트랜잭션 업데이트 - const updateTransaction = (updatedTransaction: Transaction) => { - const updatedTransactions = transactions.map(transaction => - transaction.id === updatedTransaction.id ? updatedTransaction : transaction - ); - - // 로컬 스토리지 업데이트 - localStorage.setItem('transactions', JSON.stringify(updatedTransactions)); - - // 백업도 업데이트 - localStorage.setItem('transactions_backup', JSON.stringify(updatedTransactions)); - - // 상태 업데이트 - setTransactions(updatedTransactions); - - // 이벤트 발생 - window.dispatchEvent(new Event('transactionUpdated')); - - toast({ - title: "지출이 수정되었습니다", - description: `${updatedTransaction.title} 항목이 업데이트되었습니다.`, - }); - }; - - // 트랜잭션 삭제 - const deleteTransaction = (id: string) => { - const updatedTransactions = transactions.filter(transaction => transaction.id !== id); - - // 로컬 스토리지 업데이트 - localStorage.setItem('transactions', JSON.stringify(updatedTransactions)); - - // 백업도 업데이트 - localStorage.setItem('transactions_backup', JSON.stringify(updatedTransactions)); - - // 상태 업데이트 - setTransactions(updatedTransactions); - - // 이벤트 발생 - window.dispatchEvent(new Event('transactionUpdated')); - - toast({ - title: "지출이 삭제되었습니다", - description: "선택한 지출 항목이 삭제되었습니다.", - }); - }; - - // 데이터 강제 새로고침 - const refreshTransactions = () => { - setRefreshKey(prev => prev + 1); - loadTransactions(); - }; - - return { - transactions: filteredTransactions, - allTransactions: transactions, - isLoading, - error, - totalBudget, - selectedMonth, - searchQuery, - setSearchQuery, - handlePrevMonth, - handleNextMonth, - updateTransaction, - deleteTransaction, - totalExpenses: calculateTotalExpenses(filteredTransactions), - refreshTransactions - }; -}; +// 이 파일은 이제 단순히 새로운 구조의 파일들을 재내보내기만 합니다 +export { + useTransactions, + MONTHS_KR, + getCurrentMonth, + getPrevMonth, + getNextMonth, + filterTransactionsByMonth, + filterTransactionsByQuery, + calculateTotalExpenses +} from './transactions'; diff --git a/src/pages/Transactions.tsx b/src/pages/Transactions.tsx index df191a3..9c6a96e 100644 --- a/src/pages/Transactions.tsx +++ b/src/pages/Transactions.tsx @@ -5,7 +5,7 @@ import TransactionCard from '@/components/TransactionCard'; import AddTransactionButton from '@/components/AddTransactionButton'; import { Calendar, Search, ChevronLeft, ChevronRight, Loader2 } from 'lucide-react'; import { formatCurrency } from '@/utils/formatters'; -import { useTransactions, MONTHS_KR } from '@/hooks/useTransactions'; +import { useTransactions, MONTHS_KR } from '@/hooks/transactions'; import { useBudget } from '@/contexts/BudgetContext'; const Transactions = () => {