diff --git a/src/contexts/budget/hooks/useBudgetDataState.ts b/src/contexts/budget/hooks/useBudgetDataState.ts new file mode 100644 index 0000000..e25239f --- /dev/null +++ b/src/contexts/budget/hooks/useBudgetDataState.ts @@ -0,0 +1,69 @@ + +import { useState, useEffect, useCallback } from 'react'; +import { BudgetData, BudgetPeriod } from '../types'; +import { + loadBudgetDataFromStorage, + saveBudgetDataToStorage, + clearAllBudgetData +} from '../storageUtils'; +import { toast } from '@/components/ui/use-toast'; +import { + calculateUpdatedBudgetData, + calculateSpentAmounts +} from '../budgetUtils'; + +// 예산 데이터 상태 관리 훅 +export const useBudgetDataState = (transactions: any[]) => { + const [budgetData, setBudgetData] = useState(loadBudgetDataFromStorage()); + const [selectedTab, setSelectedTab] = useState("daily"); + + // 지출 금액 업데이트 + useEffect(() => { + // 스토리지에서 데이터 로드 + const loadedBudgetData = loadBudgetDataFromStorage(); + + // 지출 금액 업데이트 + const updatedBudgetData = calculateSpentAmounts(transactions, loadedBudgetData); + + // 상태 및 스토리지 모두 업데이트 + setBudgetData(updatedBudgetData); + saveBudgetDataToStorage(updatedBudgetData); + + // 로컬 이벤트 발생 (다른 컴포넌트에서 변경 감지하도록) + window.dispatchEvent(new Event('budgetDataUpdated')); + }, [transactions]); + + // 예산 목표 업데이트 함수 + const handleBudgetGoalUpdate = useCallback(( + type: BudgetPeriod, + amount: number, + newCategoryBudgets?: Record + ) => { + // 월간 예산 직접 업데이트 (카테고리 예산이 없는 경우) + if (!newCategoryBudgets) { + const updatedBudgetData = calculateUpdatedBudgetData(budgetData, type, amount); + setBudgetData(updatedBudgetData); + saveBudgetDataToStorage(updatedBudgetData); + + toast({ + title: "목표 업데이트 완료", + description: `${type === 'daily' ? '일일' : type === 'weekly' ? '주간' : '월간'} 목표가 ${amount.toLocaleString()}원으로 설정되었습니다.` + }); + } + }, [budgetData]); + + // 예산 데이터 초기화 함수 + const resetBudgetData = useCallback(() => { + clearAllBudgetData(); + setBudgetData(loadBudgetDataFromStorage()); + console.log('예산 데이터가 초기화되었습니다.'); + }, []); + + return { + budgetData, + selectedTab, + setSelectedTab, + handleBudgetGoalUpdate, + resetBudgetData + }; +}; diff --git a/src/contexts/budget/hooks/useCategoryBudgetState.ts b/src/contexts/budget/hooks/useCategoryBudgetState.ts new file mode 100644 index 0000000..e5113b6 --- /dev/null +++ b/src/contexts/budget/hooks/useCategoryBudgetState.ts @@ -0,0 +1,37 @@ + +import { useState, useEffect, useCallback } from 'react'; +import { + loadCategoryBudgetsFromStorage, + saveCategoryBudgetsToStorage, + clearAllCategoryBudgets +} from '../storageUtils'; + +// 카테고리 예산 상태 관리 훅 +export const useCategoryBudgetState = () => { + const [categoryBudgets, setCategoryBudgets] = useState>( + loadCategoryBudgetsFromStorage() + ); + + // 카테고리 예산 업데이트 함수 + const updateCategoryBudgets = useCallback((newCategoryBudgets: Record) => { + setCategoryBudgets(newCategoryBudgets); + saveCategoryBudgetsToStorage(newCategoryBudgets); + + // 로컬 이벤트 발생 (다른 컴포넌트에서 변경 감지하도록) + window.dispatchEvent(new Event('categoryBudgetsUpdated')); + }, []); + + // 카테고리 예산 초기화 함수 + const resetCategoryBudgets = useCallback(() => { + clearAllCategoryBudgets(); + setCategoryBudgets(loadCategoryBudgetsFromStorage()); + console.log('카테고리 예산이 초기화되었습니다.'); + }, []); + + return { + categoryBudgets, + setCategoryBudgets, + updateCategoryBudgets, + resetCategoryBudgets + }; +}; diff --git a/src/contexts/budget/hooks/useCategorySpending.ts b/src/contexts/budget/hooks/useCategorySpending.ts new file mode 100644 index 0000000..27d7648 --- /dev/null +++ b/src/contexts/budget/hooks/useCategorySpending.ts @@ -0,0 +1,17 @@ + +import { useCallback } from 'react'; +import { Transaction } from '../types'; +import { calculateCategorySpending } from '../budgetUtils'; + +// 카테고리별 지출 계산 훅 +export const useCategorySpending = ( + transactions: Transaction[], + categoryBudgets: Record +) => { + // 카테고리별 지출 계산 + const getCategorySpending = useCallback(() => { + return calculateCategorySpending(transactions, categoryBudgets); + }, [transactions, categoryBudgets]); + + return { getCategorySpending }; +}; diff --git a/src/contexts/budget/hooks/useTransactionState.ts b/src/contexts/budget/hooks/useTransactionState.ts new file mode 100644 index 0000000..31cce47 --- /dev/null +++ b/src/contexts/budget/hooks/useTransactionState.ts @@ -0,0 +1,85 @@ + +import { useState, useEffect, useCallback } from 'react'; +import { Transaction } from '../types'; +import { + loadTransactionsFromStorage, + saveTransactionsToStorage, + clearAllTransactions +} from '../storageUtils'; +import { toast } from '@/components/ui/use-toast'; + +// 트랜잭션 상태 관리 훅 +export const useTransactionState = () => { + const [transactions, setTransactions] = useState([]); + + // 초기 트랜잭션 로드 + useEffect(() => { + const loadTransactions = () => { + const storedTransactions = loadTransactionsFromStorage(); + setTransactions(storedTransactions); + }; + + loadTransactions(); + + // 이벤트 리스너를 추가하여 다른 컴포넌트에서 변경 시 업데이트 + window.addEventListener('storage', loadTransactions); + return () => { + window.removeEventListener('storage', loadTransactions); + }; + }, []); + + // 트랜잭션 추가 함수 + const addTransaction = useCallback((newTransaction: Transaction) => { + setTransactions(prev => { + const updated = [newTransaction, ...prev]; + saveTransactionsToStorage(updated); + return updated; + }); + }, []); + + // 트랜잭션 업데이트 함수 + const updateTransaction = useCallback((updatedTransaction: Transaction) => { + setTransactions(prev => { + const updated = prev.map(transaction => + transaction.id === updatedTransaction.id ? updatedTransaction : transaction + ); + saveTransactionsToStorage(updated); + return updated; + }); + + // 로컬 이벤트 발생 (다른 컴포넌트에서 변경 감지하도록) + window.dispatchEvent(new Event('transactionUpdated')); + }, []); + + // 트랜잭션 삭제 함수 + const deleteTransaction = useCallback((transactionId: string) => { + setTransactions(prev => { + const updated = prev.filter(transaction => transaction.id !== transactionId); + saveTransactionsToStorage(updated); + return updated; + }); + + toast({ + title: "지출이 삭제되었습니다", + description: "지출 항목이 성공적으로 삭제되었습니다.", + }); + + // 로컬 이벤트 발생 (다른 컴포넌트에서 변경 감지하도록) + window.dispatchEvent(new Event('transactionDeleted')); + }, []); + + // 트랜잭션 초기화 함수 + const resetTransactions = useCallback(() => { + clearAllTransactions(); + setTransactions([]); + console.log('모든 트랜잭션이 초기화되었습니다.'); + }, []); + + return { + transactions, + addTransaction, + updateTransaction, + deleteTransaction, + resetTransactions + }; +}; diff --git a/src/contexts/budget/index.ts b/src/contexts/budget/index.ts index 0140c4d..896260e 100644 --- a/src/contexts/budget/index.ts +++ b/src/contexts/budget/index.ts @@ -1,4 +1,11 @@ -export * from './types'; export * from './BudgetContext'; +export * from './types'; export * from './budgetUtils'; +export * from './storageUtils'; + +// Export hooks +export * from './hooks/useTransactionState'; +export * from './hooks/useBudgetDataState'; +export * from './hooks/useCategoryBudgetState'; +export * from './hooks/useCategorySpending'; diff --git a/src/contexts/budget/useBudgetState.ts b/src/contexts/budget/useBudgetState.ts index c0c9ba1..9cc5eb2 100644 --- a/src/contexts/budget/useBudgetState.ts +++ b/src/contexts/budget/useBudgetState.ts @@ -1,82 +1,43 @@ -import { useState, useEffect, useCallback } from 'react'; -import { BudgetData, BudgetPeriod, Transaction } from './types'; +import { useCallback, useEffect } from 'react'; +import { BudgetPeriod } from './types'; import { toast } from '@/components/ui/use-toast'; -import { - loadTransactionsFromStorage, - saveTransactionsToStorage, - loadCategoryBudgetsFromStorage, - saveCategoryBudgetsToStorage, - loadBudgetDataFromStorage, - saveBudgetDataToStorage, +import { useTransactionState } from './hooks/useTransactionState'; +import { useCategoryBudgetState } from './hooks/useCategoryBudgetState'; +import { useBudgetDataState } from './hooks/useBudgetDataState'; +import { useCategorySpending } from './hooks/useCategorySpending'; +import { clearAllTransactions, clearAllCategoryBudgets, clearAllBudgetData } from './storageUtils'; -import { - calculateCategorySpending, - calculateSpentAmounts, - calculateUpdatedBudgetData -} from './budgetUtils'; export const useBudgetState = () => { - // 상태 초기화 - const [selectedTab, setSelectedTab] = useState("daily"); - const [transactions, setTransactions] = useState([]); - const [categoryBudgets, setCategoryBudgets] = useState>(loadCategoryBudgetsFromStorage()); - const [budgetData, setBudgetData] = useState(loadBudgetDataFromStorage()); - - // 데이터 리셋 함수 - const resetBudgetData = useCallback(() => { - console.log('BudgetContext에서 데이터 리셋 시작'); - - // 로컬 스토리지 초기화 - clearAllTransactions(); - clearAllCategoryBudgets(); - clearAllBudgetData(); - - // 메모리내 상태 초기화 - setTransactions([]); - setCategoryBudgets(loadCategoryBudgetsFromStorage()); - setBudgetData(loadBudgetDataFromStorage()); - - console.log('BudgetContext에서 데이터 리셋 완료'); - }, []); - - // 트랜잭션 로드 - useEffect(() => { - const loadTransactions = () => { - const storedTransactions = loadTransactionsFromStorage(); - setTransactions(storedTransactions); - }; - - loadTransactions(); - - // 지출 내역이 변경될 때마다 업데이트되도록 이벤트 리스너를 추가합니다 - window.addEventListener('storage', loadTransactions); - return () => { - window.removeEventListener('storage', loadTransactions); - }; - }, []); - - // 지출 계산 및 업데이트 - useEffect(() => { - // 스토리지에서 데이터 로드 - const loadedBudgetData = loadBudgetDataFromStorage(); - - // 지출 금액 업데이트 - const updatedBudgetData = calculateSpentAmounts(transactions, loadedBudgetData); - - // 상태 및 스토리지 모두 업데이트 - setBudgetData(updatedBudgetData); - saveBudgetDataToStorage(updatedBudgetData); - - // 트랜잭션 변경 내용도 저장 - saveTransactionsToStorage(transactions); - - // 로컬 이벤트 발생 (다른 컴포넌트에서 변경 감지하도록) - window.dispatchEvent(new Event('budgetDataUpdated')); - }, [transactions]); + // 각 상태 관리 훅 사용 + const { + transactions, + addTransaction, + updateTransaction, + deleteTransaction, + resetTransactions + } = useTransactionState(); + + const { + categoryBudgets, + setCategoryBudgets, + updateCategoryBudgets, + resetCategoryBudgets + } = useCategoryBudgetState(); + + const { + budgetData, + selectedTab, + setSelectedTab, + handleBudgetGoalUpdate, + resetBudgetData: resetBudgetDataInternal + } = useBudgetDataState(transactions); + + const { getCategorySpending } = useCategorySpending(transactions, categoryBudgets); // 카테고리별 예산 및 지출 계산 useEffect(() => { @@ -84,61 +45,49 @@ export const useBudgetState = () => { const totalDailyBudget = Math.round(totalMonthlyBudget / 30); const totalWeeklyBudget = Math.round(totalMonthlyBudget / 4.3); - setBudgetData(prev => { - const updatedBudgetData = { - daily: { - targetAmount: totalDailyBudget, - spentAmount: prev.daily.spentAmount, - remainingAmount: totalDailyBudget - prev.daily.spentAmount - }, - weekly: { - targetAmount: totalWeeklyBudget, - spentAmount: prev.weekly.spentAmount, - remainingAmount: totalWeeklyBudget - prev.weekly.spentAmount - }, - monthly: { - targetAmount: totalMonthlyBudget, - spentAmount: prev.monthly.spentAmount, - remainingAmount: totalMonthlyBudget - prev.monthly.spentAmount - } - }; - - // 저장 과정 강화 - 예산 데이터 저장 - try { - saveBudgetDataToStorage(updatedBudgetData); - console.log('예산 데이터가 저장되었습니다:', updatedBudgetData); - } catch (error) { - console.error('예산 데이터 저장 중 오류:', error); + const updatedBudgetData = { + daily: { + targetAmount: totalDailyBudget, + spentAmount: budgetData.daily.spentAmount, + remainingAmount: totalDailyBudget - budgetData.daily.spentAmount + }, + weekly: { + targetAmount: totalWeeklyBudget, + spentAmount: budgetData.weekly.spentAmount, + remainingAmount: totalWeeklyBudget - budgetData.weekly.spentAmount + }, + monthly: { + targetAmount: totalMonthlyBudget, + spentAmount: budgetData.monthly.spentAmount, + remainingAmount: totalMonthlyBudget - budgetData.monthly.spentAmount } - - return updatedBudgetData; - }); - - // 저장 과정 강화 - 카테고리 예산 저장 - try { - saveCategoryBudgetsToStorage(categoryBudgets); - console.log('카테고리 예산이 저장되었습니다:', categoryBudgets); - } catch (error) { - console.error('카테고리 예산 저장 중 오류:', error); - } + }; // 로컬 이벤트 발생 (다른 컴포넌트에서 변경 감지하도록) window.dispatchEvent(new Event('categoryBudgetsUpdated')); - }, [categoryBudgets]); + }, [categoryBudgets, budgetData]); - // 카테고리별 지출 계산 - const getCategorySpending = () => { - return calculateCategorySpending(transactions, categoryBudgets); - }; + // 모든 데이터 리셋 함수 + const resetBudgetData = useCallback(() => { + console.log('BudgetContext에서 데이터 리셋 시작'); + + // 로컬 스토리지 초기화 + resetTransactions(); + resetCategoryBudgets(); + resetBudgetDataInternal(); + + console.log('BudgetContext에서 데이터 리셋 완료'); + }, [resetTransactions, resetCategoryBudgets, resetBudgetDataInternal]); - // 예산 목표 업데이트 함수 - const handleBudgetGoalUpdate = (type: BudgetPeriod, amount: number, newCategoryBudgets?: Record) => { + // 확장된 예산 목표 업데이트 함수 + const extendedBudgetGoalUpdate = ( + type: BudgetPeriod, + amount: number, + newCategoryBudgets?: Record + ) => { // 카테고리 예산이 직접 업데이트된 경우 if (newCategoryBudgets) { - setCategoryBudgets(newCategoryBudgets); - - // 저장 과정 추가 - saveCategoryBudgetsToStorage(newCategoryBudgets); + updateCategoryBudgets(newCategoryBudgets); toast({ title: "카테고리 예산 업데이트 완료", @@ -157,48 +106,11 @@ export const useBudgetState = () => { updatedCategoryBudgets[category] = Math.round(categoryBudgets[category] * ratio); }); - setCategoryBudgets(updatedCategoryBudgets); - saveCategoryBudgetsToStorage(updatedCategoryBudgets); + updateCategoryBudgets(updatedCategoryBudgets); } else { // 일일이나 주간 예산이 직접 업데이트되는 경우 - const updatedBudgetData = calculateUpdatedBudgetData(budgetData, type, amount); - setBudgetData(updatedBudgetData); - saveBudgetDataToStorage(updatedBudgetData); + handleBudgetGoalUpdate(type, amount); } - - toast({ - title: "목표 업데이트 완료", - description: `${type === 'daily' ? '일일' : type === 'weekly' ? '주간' : '월간'} 목표가 ${amount.toLocaleString()}원으로 설정되었습니다.` - }); - }; - - // 트랜잭션 추가 함수 추가 - const addTransaction = (newTransaction: Transaction) => { - setTransactions(prev => { - const updated = [newTransaction, ...prev]; - saveTransactionsToStorage(updated); - return updated; - }); - }; - - // 트랜잭션 업데이트 처리 - const updateTransaction = (updatedTransaction: Transaction) => { - setTransactions(prev => { - const updated = prev.map(transaction => - transaction.id === updatedTransaction.id ? updatedTransaction : transaction - ); - saveTransactionsToStorage(updated); - return updated; - }); - }; - - // 트랜잭션 삭제 함수 추가 - const deleteTransaction = (transactionId: string) => { - setTransactions(prev => { - const updated = prev.filter(transaction => transaction.id !== transactionId); - saveTransactionsToStorage(updated); - return updated; - }); }; return { @@ -210,7 +122,7 @@ export const useBudgetState = () => { addTransaction, updateTransaction, deleteTransaction, - handleBudgetGoalUpdate, + handleBudgetGoalUpdate: extendedBudgetGoalUpdate, getCategorySpending, resetBudgetData };