Refactor useTransactions hook

Refactor the useTransactions hook into smaller, more manageable files to improve code organization and maintainability. All existing functionality is preserved.
This commit is contained in:
gpt-engineer-app[bot]
2025-03-16 09:39:56 +00:00
parent 662cacbc99
commit d45e1bbf78
7 changed files with 410 additions and 215 deletions

View File

@@ -1,218 +1,10 @@
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';
import { Loader2 } from 'lucide-react';
// useTransactions
import { useTransactionsCore } from './useTransactionsCore';
/**
* 메인 트랜잭션 훅
* useTransactionsCore를 통해 모든 트랜잭션 관련 기능에 접근합니다.
*/
export const useTransactions = () => {
const [transactions, setTransactions] = useState<Transaction[]>([]);
const [filteredTransactions, setFilteredTransactions] = useState<Transaction[]>([]);
const [selectedMonth, setSelectedMonth] = useState(getCurrentMonth());
const [searchQuery, setSearchQuery] = useState('');
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(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",
duration: 4000
});
} finally {
// 로딩 상태를 약간 지연시켜 UI 업데이트가 원활하게 이루어지도록 함
setTimeout(() => setIsLoading(false), 300);
}
}, []);
// 필터 적용
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'));
// 약간의 지연을 두고 토스트 표시
setTimeout(() => {
toast({
title: "지출이 수정되었습니다",
description: `${updatedTransaction.title} 항목이 업데이트되었습니다.`,
duration: 3000
});
}, 100);
};
// 트랜잭션 삭제
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'));
// 약간의 지연을 두고 토스트 표시
setTimeout(() => {
toast({
title: "지출이 삭제되었습니다",
description: "선택한 지출 항목이 삭제되었습니다.",
duration: 3000
});
}, 100);
};
// 데이터 강제 새로고침
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
};
return useTransactionsCore();
};