From 8c403f976186181fa760e36acd9d4c2cab53310b Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Sat, 15 Mar 2025 11:08:35 +0000 Subject: [PATCH] Refactor BudgetContext file Refactor BudgetContext.tsx into smaller components and hooks to improve code readability and maintainability. --- src/contexts/BudgetContext.tsx | 326 +------------------------- src/contexts/budget/BudgetContext.tsx | 27 +++ src/contexts/budget/budgetUtils.ts | 144 ++++++++++++ src/contexts/budget/index.ts | 4 + src/contexts/budget/storageUtils.ts | 45 ++++ src/contexts/budget/types.ts | 43 ++++ src/contexts/budget/useBudgetState.ts | 138 +++++++++++ 7 files changed, 404 insertions(+), 323 deletions(-) create mode 100644 src/contexts/budget/BudgetContext.tsx create mode 100644 src/contexts/budget/budgetUtils.ts create mode 100644 src/contexts/budget/index.ts create mode 100644 src/contexts/budget/storageUtils.ts create mode 100644 src/contexts/budget/types.ts create mode 100644 src/contexts/budget/useBudgetState.ts diff --git a/src/contexts/BudgetContext.tsx b/src/contexts/BudgetContext.tsx index f470183..4eb06a1 100644 --- a/src/contexts/BudgetContext.tsx +++ b/src/contexts/BudgetContext.tsx @@ -1,325 +1,5 @@ -import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react'; -import { Transaction } from '@/components/TransactionCard'; -import { DEFAULT_CATEGORY_BUDGETS, DEFAULT_MONTHLY_BUDGET, EXPENSE_CATEGORIES } from '@/constants/categoryIcons'; -import { toast } from '@/components/ui/use-toast'; +import { BudgetProvider, useBudget } from './budget'; -// 예산 데이터 타입 정의 -export type BudgetPeriod = 'daily' | 'weekly' | 'monthly'; - -export interface BudgetPeriodData { - targetAmount: number; - spentAmount: number; - remainingAmount: number; -} - -export interface BudgetData { - daily: BudgetPeriodData; - weekly: BudgetPeriodData; - monthly: BudgetPeriodData; -} - -export interface CategoryBudget { - title: string; - current: number; - total: number; -} - -// Context 타입 정의 -interface BudgetContextType { - transactions: Transaction[]; - categoryBudgets: Record; - budgetData: BudgetData; - selectedTab: BudgetPeriod; - setSelectedTab: (tab: BudgetPeriod) => void; - updateTransaction: (updatedTransaction: Transaction) => void; - handleBudgetGoalUpdate: (type: BudgetPeriod, amount: number, newCategoryBudgets?: Record) => void; - getCategorySpending: () => CategoryBudget[]; -} - -// Context 생성 -const BudgetContext = createContext(undefined); - -// Context Provider 컴포넌트 -export const BudgetProvider: React.FC<{ children: ReactNode }> = ({ children }) => { - const [selectedTab, setSelectedTab] = useState("daily"); - const [transactions, setTransactions] = useState([]); - const [categoryBudgets, setCategoryBudgets] = useState>(DEFAULT_CATEGORY_BUDGETS); - const [budgetData, setBudgetData] = useState({ - daily: { - targetAmount: Math.round(DEFAULT_MONTHLY_BUDGET / 30), - spentAmount: 0, - remainingAmount: Math.round(DEFAULT_MONTHLY_BUDGET / 30) - }, - weekly: { - targetAmount: Math.round(DEFAULT_MONTHLY_BUDGET / 4.3), - spentAmount: 0, - remainingAmount: Math.round(DEFAULT_MONTHLY_BUDGET / 4.3) - }, - monthly: { - targetAmount: DEFAULT_MONTHLY_BUDGET, - spentAmount: 0, - remainingAmount: DEFAULT_MONTHLY_BUDGET - } - }); - - // 로컬 스토리지에서 거래 내역을 불러옵니다 - useEffect(() => { - const loadTransactions = () => { - const storedTransactions = localStorage.getItem('transactions'); - if (storedTransactions) { - setTransactions(JSON.parse(storedTransactions)); - } - }; - - const loadBudgets = () => { - const storedCategoryBudgets = localStorage.getItem('categoryBudgets'); - if (storedCategoryBudgets) { - setCategoryBudgets(JSON.parse(storedCategoryBudgets)); - } else { - localStorage.setItem('categoryBudgets', JSON.stringify(DEFAULT_CATEGORY_BUDGETS)); - } - - const storedBudgetData = localStorage.getItem('budgetData'); - if (storedBudgetData) { - setBudgetData(JSON.parse(storedBudgetData)); - } else { - localStorage.setItem('budgetData', JSON.stringify(budgetData)); - } - }; - - loadTransactions(); - loadBudgets(); - - // 지출 내역이 변경될 때마다 업데이트되도록 이벤트 리스너를 추가합니다 - window.addEventListener('storage', loadTransactions); - return () => { - window.removeEventListener('storage', loadTransactions); - }; - }, []); - - // 거래 내역이 업데이트될 때마다 지출액을 다시 계산합니다 - useEffect(() => { - const calculateSpentAmounts = () => { - // 오늘 날짜의 지출만 필터링 - const today = new Date(); - const todayString = today.toLocaleDateString(); - - // 지출 거래 필터링 - const expenseTransactions = transactions.filter(t => t.type === 'expense'); - - // 오늘 지출 계산 - const todayExpenses = expenseTransactions.filter(t => { - if (t.date.includes('오늘')) return true; - return false; - }); - const dailySpent = todayExpenses.reduce((sum, t) => sum + t.amount, 0); - - // 이번 주 지출 계산 (지난 7일) - const weekStart = new Date(); - weekStart.setDate(today.getDate() - 7); - const weeklyExpenses = expenseTransactions.filter(t => { - // 간단하게 '오늘'이나 '어제'가 포함된 거래는 이번 주로 간주 - if (t.date.includes('오늘') || t.date.includes('어제')) return true; - // 그 외에는 이번 달의 거래로 간주 (실제로는 날짜를 제대로 파싱해야 함) - return true; - }); - const weeklySpent = weeklyExpenses.reduce((sum, t) => sum + t.amount, 0); - - // 이번 달 총 지출 계산 - const monthlySpent = expenseTransactions.reduce((sum, t) => sum + t.amount, 0); - - // 예산 데이터 업데이트 - setBudgetData(prev => { - const updatedBudgetData = { - daily: { - ...prev.daily, - spentAmount: dailySpent, - remainingAmount: Math.max(0, prev.daily.targetAmount - dailySpent) - }, - weekly: { - ...prev.weekly, - spentAmount: weeklySpent, - remainingAmount: Math.max(0, prev.weekly.targetAmount - weeklySpent) - }, - monthly: { - ...prev.monthly, - spentAmount: monthlySpent, - remainingAmount: Math.max(0, prev.monthly.targetAmount - monthlySpent) - } - }; - - // 업데이트된 예산 데이터 저장 - localStorage.setItem('budgetData', JSON.stringify(updatedBudgetData)); - return updatedBudgetData; - }); - }; - - calculateSpentAmounts(); - }, [transactions]); - - // 카테고리별 예산 및 지출 계산 - useEffect(() => { - const totalMonthlyBudget = Object.values(categoryBudgets).reduce((sum, value) => sum + value, 0); - 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 - } - }; - - // 업데이트된 예산 데이터 저장 - localStorage.setItem('budgetData', JSON.stringify(updatedBudgetData)); - return updatedBudgetData; - }); - - // 카테고리 예산 저장 - localStorage.setItem('categoryBudgets', JSON.stringify(categoryBudgets)); - }, [categoryBudgets]); - - // 카테고리별 지출 계산 - const getCategorySpending = () => { - const expenseTransactions = transactions.filter(t => t.type === 'expense'); - const categorySpending: Record = {}; - - // 모든 카테고리에 대해 초기값 0 설정 - EXPENSE_CATEGORIES.forEach(category => { - categorySpending[category] = 0; - }); - - expenseTransactions.forEach(t => { - if (t.category in categorySpending) { - categorySpending[t.category] += t.amount; - } - }); - - return Object.keys(categoryBudgets).map(category => ({ - title: category, - current: categorySpending[category] || 0, - total: categoryBudgets[category] - })); - }; - - // 예산 목표 업데이트 함수 - const handleBudgetGoalUpdate = (type: BudgetPeriod, amount: number, newCategoryBudgets?: Record) => { - // 카테고리 예산이 직접 업데이트된 경우 - if (newCategoryBudgets) { - setCategoryBudgets(newCategoryBudgets); - return; // 카테고리 예산이 변경되면 useEffect에서 자동으로 budgetData가 업데이트됩니다 - } - - // 월간 예산을 업데이트하고 일일, 주간도 자동 계산 - if (type === 'monthly') { - const dailyAmount = Math.round(amount / 30); - const weeklyAmount = Math.round(amount / 4.3); - - // 월간 총 예산이 변경되면 카테고리별 예산도 비례하여 조정 - const ratio = amount / budgetData.monthly.targetAmount; - const updatedCategoryBudgets: Record = {}; - - Object.keys(categoryBudgets).forEach(category => { - updatedCategoryBudgets[category] = Math.round(categoryBudgets[category] * ratio); - }); - - setCategoryBudgets(updatedCategoryBudgets); - - setBudgetData(prev => { - const updatedBudgetData = { - daily: { - targetAmount: dailyAmount, - spentAmount: prev.daily.spentAmount, - remainingAmount: Math.max(0, dailyAmount - prev.daily.spentAmount) - }, - weekly: { - targetAmount: weeklyAmount, - spentAmount: prev.weekly.spentAmount, - remainingAmount: Math.max(0, weeklyAmount - prev.weekly.spentAmount) - }, - monthly: { - targetAmount: amount, - spentAmount: prev.monthly.spentAmount, - remainingAmount: Math.max(0, amount - prev.monthly.spentAmount) - } - }; - - // 업데이트된 예산 데이터 저장 - localStorage.setItem('budgetData', JSON.stringify(updatedBudgetData)); - return updatedBudgetData; - }); - } else { - // 일일이나 주간 예산이 직접 업데이트되는 경우 - setBudgetData(prev => { - const remainingAmount = Math.max(0, amount - prev[type].spentAmount); - const updatedBudgetData = { - ...prev, - [type]: { - ...prev[type], - targetAmount: amount, - remainingAmount: remainingAmount - } - }; - - // 업데이트된 예산 데이터 저장 - localStorage.setItem('budgetData', JSON.stringify(updatedBudgetData)); - return updatedBudgetData; - }); - } - - toast({ - title: "목표 업데이트 완료", - description: `${type === 'daily' ? '일일' : type === 'weekly' ? '주간' : '월간'} 목표가 ${amount.toLocaleString()}원으로 설정되었습니다.` - }); - }; - - // 트랜잭션 업데이트 처리 - const updateTransaction = (updatedTransaction: Transaction) => { - setTransactions(prev => { - const updated = prev.map(transaction => - transaction.id === updatedTransaction.id ? updatedTransaction : transaction - ); - localStorage.setItem('transactions', JSON.stringify(updated)); - return updated; - }); - }; - - const contextValue: BudgetContextType = { - transactions, - categoryBudgets, - budgetData, - selectedTab, - setSelectedTab, - updateTransaction, - handleBudgetGoalUpdate, - getCategorySpending - }; - - return ( - - {children} - - ); -}; - -// Context 사용을 위한 Hook -export const useBudget = () => { - const context = useContext(BudgetContext); - if (!context) { - throw new Error('useBudget must be used within a BudgetProvider'); - } - return context; -}; +export { BudgetProvider, useBudget }; +export type { BudgetPeriod } from './budget'; diff --git a/src/contexts/budget/BudgetContext.tsx b/src/contexts/budget/BudgetContext.tsx new file mode 100644 index 0000000..a8c12ee --- /dev/null +++ b/src/contexts/budget/BudgetContext.tsx @@ -0,0 +1,27 @@ + +import React, { createContext, useContext, ReactNode } from 'react'; +import { BudgetContextType } from './types'; +import { useBudgetState } from './useBudgetState'; + +// Context 생성 +const BudgetContext = createContext(undefined); + +// Context Provider 컴포넌트 +export const BudgetProvider: React.FC<{ children: ReactNode }> = ({ children }) => { + const budgetState = useBudgetState(); + + return ( + + {children} + + ); +}; + +// Context 사용을 위한 Hook +export const useBudget = () => { + const context = useContext(BudgetContext); + if (!context) { + throw new Error('useBudget must be used within a BudgetProvider'); + } + return context; +}; diff --git a/src/contexts/budget/budgetUtils.ts b/src/contexts/budget/budgetUtils.ts new file mode 100644 index 0000000..31f304e --- /dev/null +++ b/src/contexts/budget/budgetUtils.ts @@ -0,0 +1,144 @@ + +import { BudgetData, BudgetPeriod, CategoryBudget, Transaction } from './types'; +import { EXPENSE_CATEGORIES } from '@/constants/categoryIcons'; + +// 기본 데이터 상수 +export const DEFAULT_CATEGORY_BUDGETS: Record = { + 식비: 400000, + 생활비: 600000, + 교통비: 200000 +}; + +export const DEFAULT_MONTHLY_BUDGET = 1200000; + +// 카테고리별 지출 계산 +export const calculateCategorySpending = ( + transactions: Transaction[], + categoryBudgets: Record +): CategoryBudget[] => { + const expenseTransactions = transactions.filter(t => t.type === 'expense'); + const categorySpending: Record = {}; + + // 모든 카테고리에 대해 초기값 0 설정 + Object.keys(categoryBudgets).forEach(category => { + categorySpending[category] = 0; + }); + + expenseTransactions.forEach(t => { + if (t.category in categorySpending) { + categorySpending[t.category] += t.amount; + } + }); + + return Object.keys(categoryBudgets).map(category => ({ + title: category, + current: categorySpending[category] || 0, + total: categoryBudgets[category] + })); +}; + +// 예산 데이터 업데이트 계산 +export const calculateUpdatedBudgetData = ( + prevBudgetData: BudgetData, + type: BudgetPeriod, + amount: number +): BudgetData => { + if (type === 'monthly') { + const dailyAmount = Math.round(amount / 30); + const weeklyAmount = Math.round(amount / 4.3); + + return { + daily: { + targetAmount: dailyAmount, + spentAmount: prevBudgetData.daily.spentAmount, + remainingAmount: Math.max(0, dailyAmount - prevBudgetData.daily.spentAmount) + }, + weekly: { + targetAmount: weeklyAmount, + spentAmount: prevBudgetData.weekly.spentAmount, + remainingAmount: Math.max(0, weeklyAmount - prevBudgetData.weekly.spentAmount) + }, + monthly: { + targetAmount: amount, + spentAmount: prevBudgetData.monthly.spentAmount, + remainingAmount: Math.max(0, amount - prevBudgetData.monthly.spentAmount) + } + }; + } else { + const remainingAmount = Math.max(0, amount - prevBudgetData[type].spentAmount); + return { + ...prevBudgetData, + [type]: { + ...prevBudgetData[type], + targetAmount: amount, + remainingAmount: remainingAmount + } + }; + } +}; + +// 지출액 계산 (일일, 주간, 월간) +export const calculateSpentAmounts = ( + transactions: Transaction[], + prevBudgetData: BudgetData +): BudgetData => { + // 지출 거래 필터링 + const expenseTransactions = transactions.filter(t => t.type === 'expense'); + + // 오늘 지출 계산 + const todayExpenses = expenseTransactions.filter(t => { + if (t.date.includes('오늘')) return true; + return false; + }); + const dailySpent = todayExpenses.reduce((sum, t) => sum + t.amount, 0); + + // 이번 주 지출 계산 (단순화된 버전) + const weeklyExpenses = expenseTransactions.filter(t => { + if (t.date.includes('오늘') || t.date.includes('어제')) return true; + return true; + }); + const weeklySpent = weeklyExpenses.reduce((sum, t) => sum + t.amount, 0); + + // 이번 달 총 지출 계산 + const monthlySpent = expenseTransactions.reduce((sum, t) => sum + t.amount, 0); + + // 예산 데이터 업데이트 + return { + daily: { + ...prevBudgetData.daily, + spentAmount: dailySpent, + remainingAmount: Math.max(0, prevBudgetData.daily.targetAmount - dailySpent) + }, + weekly: { + ...prevBudgetData.weekly, + spentAmount: weeklySpent, + remainingAmount: Math.max(0, prevBudgetData.weekly.targetAmount - weeklySpent) + }, + monthly: { + ...prevBudgetData.monthly, + spentAmount: monthlySpent, + remainingAmount: Math.max(0, prevBudgetData.monthly.targetAmount - monthlySpent) + } + }; +}; + +// 초기 예산 데이터 생성 +export const getInitialBudgetData = (): BudgetData => { + return { + daily: { + targetAmount: Math.round(DEFAULT_MONTHLY_BUDGET / 30), + spentAmount: 0, + remainingAmount: Math.round(DEFAULT_MONTHLY_BUDGET / 30) + }, + weekly: { + targetAmount: Math.round(DEFAULT_MONTHLY_BUDGET / 4.3), + spentAmount: 0, + remainingAmount: Math.round(DEFAULT_MONTHLY_BUDGET / 4.3) + }, + monthly: { + targetAmount: DEFAULT_MONTHLY_BUDGET, + spentAmount: 0, + remainingAmount: DEFAULT_MONTHLY_BUDGET + } + }; +}; diff --git a/src/contexts/budget/index.ts b/src/contexts/budget/index.ts new file mode 100644 index 0000000..0140c4d --- /dev/null +++ b/src/contexts/budget/index.ts @@ -0,0 +1,4 @@ + +export * from './types'; +export * from './BudgetContext'; +export * from './budgetUtils'; diff --git a/src/contexts/budget/storageUtils.ts b/src/contexts/budget/storageUtils.ts new file mode 100644 index 0000000..e1019fa --- /dev/null +++ b/src/contexts/budget/storageUtils.ts @@ -0,0 +1,45 @@ + +import { BudgetData, Transaction } from './types'; +import { DEFAULT_CATEGORY_BUDGETS, getInitialBudgetData } from './budgetUtils'; + +// 로컬 스토리지에서 트랜잭션 불러오기 +export const loadTransactionsFromStorage = (): Transaction[] => { + const storedTransactions = localStorage.getItem('transactions'); + if (storedTransactions) { + return JSON.parse(storedTransactions); + } + return []; +}; + +// 트랜잭션 저장 +export const saveTransactionsToStorage = (transactions: Transaction[]): void => { + localStorage.setItem('transactions', JSON.stringify(transactions)); +}; + +// 카테고리 예산 불러오기 +export const loadCategoryBudgetsFromStorage = (): Record => { + const storedCategoryBudgets = localStorage.getItem('categoryBudgets'); + if (storedCategoryBudgets) { + return JSON.parse(storedCategoryBudgets); + } + return DEFAULT_CATEGORY_BUDGETS; +}; + +// 카테고리 예산 저장 +export const saveCategoryBudgetsToStorage = (categoryBudgets: Record): void => { + localStorage.setItem('categoryBudgets', JSON.stringify(categoryBudgets)); +}; + +// 예산 데이터 불러오기 +export const loadBudgetDataFromStorage = (): BudgetData => { + const storedBudgetData = localStorage.getItem('budgetData'); + if (storedBudgetData) { + return JSON.parse(storedBudgetData); + } + return getInitialBudgetData(); +}; + +// 예산 데이터 저장 +export const saveBudgetDataToStorage = (budgetData: BudgetData): void => { + localStorage.setItem('budgetData', JSON.stringify(budgetData)); +}; diff --git a/src/contexts/budget/types.ts b/src/contexts/budget/types.ts new file mode 100644 index 0000000..e937801 --- /dev/null +++ b/src/contexts/budget/types.ts @@ -0,0 +1,43 @@ + +// 예산 관련 타입 정의 +export type BudgetPeriod = 'daily' | 'weekly' | 'monthly'; + +export interface BudgetPeriodData { + targetAmount: number; + spentAmount: number; + remainingAmount: number; +} + +export interface BudgetData { + daily: BudgetPeriodData; + weekly: BudgetPeriodData; + monthly: BudgetPeriodData; +} + +export interface CategoryBudget { + title: string; + current: number; + total: number; +} + +// BudgetContext 타입 정의 +export interface BudgetContextType { + transactions: Transaction[]; + categoryBudgets: Record; + budgetData: BudgetData; + selectedTab: BudgetPeriod; + setSelectedTab: (tab: BudgetPeriod) => void; + updateTransaction: (updatedTransaction: Transaction) => void; + handleBudgetGoalUpdate: (type: BudgetPeriod, amount: number, newCategoryBudgets?: Record) => void; + getCategorySpending: () => CategoryBudget[]; +} + +// Transaction 타입 (기존 TransactionCard에서 가져옴) +export interface Transaction { + id: string; + title: string; + amount: number; + date: string; + category: string; + type: 'income' | 'expense'; +} diff --git a/src/contexts/budget/useBudgetState.ts b/src/contexts/budget/useBudgetState.ts new file mode 100644 index 0000000..449409f --- /dev/null +++ b/src/contexts/budget/useBudgetState.ts @@ -0,0 +1,138 @@ + +import { useState, useEffect } from 'react'; +import { BudgetData, BudgetPeriod, Transaction } from './types'; +import { toast } from '@/components/ui/use-toast'; +import { + loadTransactionsFromStorage, + saveTransactionsToStorage, + loadCategoryBudgetsFromStorage, + saveCategoryBudgetsToStorage, + loadBudgetDataFromStorage, + saveBudgetDataToStorage +} 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()); + + // 트랜잭션 로드 + useEffect(() => { + const loadTransactions = () => { + const storedTransactions = loadTransactionsFromStorage(); + setTransactions(storedTransactions); + }; + + loadTransactions(); + + // 지출 내역이 변경될 때마다 업데이트되도록 이벤트 리스너를 추가합니다 + window.addEventListener('storage', loadTransactions); + return () => { + window.removeEventListener('storage', loadTransactions); + }; + }, []); + + // 지출 계산 및 업데이트 + useEffect(() => { + const updatedBudgetData = calculateSpentAmounts(transactions, budgetData); + setBudgetData(updatedBudgetData); + saveBudgetDataToStorage(updatedBudgetData); + }, [transactions]); + + // 카테고리별 예산 및 지출 계산 + useEffect(() => { + const totalMonthlyBudget = Object.values(categoryBudgets).reduce((sum, value) => sum + value, 0); + 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 + } + }; + + saveBudgetDataToStorage(updatedBudgetData); + return updatedBudgetData; + }); + + saveCategoryBudgetsToStorage(categoryBudgets); + }, [categoryBudgets]); + + // 카테고리별 지출 계산 + const getCategorySpending = () => { + return calculateCategorySpending(transactions, categoryBudgets); + }; + + // 예산 목표 업데이트 함수 + const handleBudgetGoalUpdate = (type: BudgetPeriod, amount: number, newCategoryBudgets?: Record) => { + // 카테고리 예산이 직접 업데이트된 경우 + if (newCategoryBudgets) { + setCategoryBudgets(newCategoryBudgets); + return; // 카테고리 예산이 변경되면 useEffect에서 자동으로 budgetData가 업데이트됩니다 + } + + // 월간 예산을 업데이트하고 일일, 주간도 자동 계산 + if (type === 'monthly') { + const ratio = amount / budgetData.monthly.targetAmount; + const updatedCategoryBudgets: Record = {}; + + Object.keys(categoryBudgets).forEach(category => { + updatedCategoryBudgets[category] = Math.round(categoryBudgets[category] * ratio); + }); + + setCategoryBudgets(updatedCategoryBudgets); + } else { + // 일일이나 주간 예산이 직접 업데이트되는 경우 + const updatedBudgetData = calculateUpdatedBudgetData(budgetData, type, amount); + setBudgetData(updatedBudgetData); + saveBudgetDataToStorage(updatedBudgetData); + } + + toast({ + title: "목표 업데이트 완료", + description: `${type === 'daily' ? '일일' : type === 'weekly' ? '주간' : '월간'} 목표가 ${amount.toLocaleString()}원으로 설정되었습니다.` + }); + }; + + // 트랜잭션 업데이트 처리 + const updateTransaction = (updatedTransaction: Transaction) => { + setTransactions(prev => { + const updated = prev.map(transaction => + transaction.id === updatedTransaction.id ? updatedTransaction : transaction + ); + saveTransactionsToStorage(updated); + return updated; + }); + }; + + return { + transactions, + categoryBudgets, + budgetData, + selectedTab, + setSelectedTab, + updateTransaction, + handleBudgetGoalUpdate, + getCategorySpending + }; +};