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:
@@ -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();
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user