Refactor useTransactions hook

Splits the useTransactions hook into smaller, more manageable files for improved code organization and maintainability. The original functionality of the hook remains unchanged.
This commit is contained in:
gpt-engineer-app[bot]
2025-03-16 09:03:38 +00:00
parent da9120ba61
commit 468bb79c9e
8 changed files with 485 additions and 303 deletions

View File

@@ -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<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"
});
} 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
};
};