Refactor budgetUtils.ts
Refactor budgetUtils.ts to improve code organization and maintainability by splitting the logic into multiple files. The functionality remains the same.
This commit is contained in:
78
src/contexts/budget/utils/budgetCalculation.ts
Normal file
78
src/contexts/budget/utils/budgetCalculation.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
|
||||
import { BudgetData, BudgetPeriod } from '../types';
|
||||
import { getInitialBudgetData } from './constants';
|
||||
|
||||
// 예산 데이터 업데이트 계산
|
||||
export const calculateUpdatedBudgetData = (
|
||||
prevBudgetData: BudgetData,
|
||||
type: BudgetPeriod,
|
||||
amount: number
|
||||
): BudgetData => {
|
||||
console.log(`예산 업데이트 계산 시작: 타입=${type}, 금액=${amount}`);
|
||||
|
||||
// 값이 없거나 유효하지 않은 경우 로깅
|
||||
if (!prevBudgetData) {
|
||||
console.error('이전 예산 데이터가 없습니다. 기본값 사용.');
|
||||
prevBudgetData = getInitialBudgetData();
|
||||
}
|
||||
|
||||
// 선택된 타입에 따라 다른 타입의 예산도 자동으로 계산
|
||||
let monthlyAmount: number, weeklyAmount: number, dailyAmount: number;
|
||||
|
||||
if (type === 'monthly') {
|
||||
// 월간 예산이 직접 입력된 경우
|
||||
monthlyAmount = amount;
|
||||
// 월 30일 기준 (실제 사용 시 값 확인)
|
||||
dailyAmount = Math.round(monthlyAmount / 30);
|
||||
// 월 4.3주 기준 (실제 사용 시 값 확인)
|
||||
weeklyAmount = Math.round(monthlyAmount / 4.3);
|
||||
} else if (type === 'weekly') {
|
||||
// 주간 예산이 직접 입력된 경우
|
||||
weeklyAmount = amount;
|
||||
monthlyAmount = Math.round(weeklyAmount * 4.3);
|
||||
dailyAmount = Math.round(weeklyAmount / 7);
|
||||
} else { // 'daily'
|
||||
// 일일 예산이 직접 입력된 경우
|
||||
dailyAmount = amount;
|
||||
weeklyAmount = Math.round(dailyAmount * 7);
|
||||
monthlyAmount = Math.round(dailyAmount * 30);
|
||||
}
|
||||
|
||||
// 모든 금액이 최소한 0 이상이 되도록 보장
|
||||
monthlyAmount = Math.max(0, monthlyAmount);
|
||||
weeklyAmount = Math.max(0, weeklyAmount);
|
||||
dailyAmount = Math.max(0, dailyAmount);
|
||||
|
||||
console.log(`최종 예산 계산 결과: 월간=${monthlyAmount}원, 주간=${weeklyAmount}원, 일일=${dailyAmount}원`);
|
||||
|
||||
// 로그에 이전 예산 데이터 출력
|
||||
console.log("이전 예산 데이터:", JSON.stringify(prevBudgetData));
|
||||
|
||||
// 이전 지출 데이터 보존
|
||||
const dailySpent = prevBudgetData.daily?.spentAmount || 0;
|
||||
const weeklySpent = prevBudgetData.weekly?.spentAmount || 0;
|
||||
const monthlySpent = prevBudgetData.monthly?.spentAmount || 0;
|
||||
|
||||
// 새 예산 데이터 생성 (spentAmount는 이전 값 유지)
|
||||
const updatedBudgetData = {
|
||||
daily: {
|
||||
targetAmount: dailyAmount,
|
||||
spentAmount: dailySpent,
|
||||
remainingAmount: Math.max(0, dailyAmount - dailySpent)
|
||||
},
|
||||
weekly: {
|
||||
targetAmount: weeklyAmount,
|
||||
spentAmount: weeklySpent,
|
||||
remainingAmount: Math.max(0, weeklyAmount - weeklySpent)
|
||||
},
|
||||
monthly: {
|
||||
targetAmount: monthlyAmount,
|
||||
spentAmount: monthlySpent,
|
||||
remainingAmount: Math.max(0, monthlyAmount - monthlySpent)
|
||||
}
|
||||
};
|
||||
|
||||
console.log("새 예산 데이터:", JSON.stringify(updatedBudgetData));
|
||||
|
||||
return updatedBudgetData;
|
||||
};
|
||||
48
src/contexts/budget/utils/categoryUtils.ts
Normal file
48
src/contexts/budget/utils/categoryUtils.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
|
||||
import { CategoryBudget, Transaction } from '../types';
|
||||
import { EXPENSE_CATEGORIES } from '@/constants/categoryIcons';
|
||||
|
||||
// 카테고리별 지출 계산
|
||||
export const calculateCategorySpending = (
|
||||
transactions: Transaction[],
|
||||
categoryBudgets: Record<string, number>
|
||||
): CategoryBudget[] => {
|
||||
const expenseTransactions = transactions.filter(t => t.type === 'expense');
|
||||
const categorySpending: Record<string, number> = {};
|
||||
|
||||
// 모든 카테고리에 대해 초기값 0 설정
|
||||
Object.keys(categoryBudgets).forEach(category => {
|
||||
// 정의된 카테고리만 유지
|
||||
if (EXPENSE_CATEGORIES.includes(category)) {
|
||||
categorySpending[category] = 0;
|
||||
}
|
||||
});
|
||||
|
||||
// 지원되는 카테고리가 없을 경우 기본값 설정
|
||||
if (Object.keys(categorySpending).length === 0) {
|
||||
EXPENSE_CATEGORIES.forEach(category => {
|
||||
categorySpending[category] = 0;
|
||||
});
|
||||
}
|
||||
|
||||
expenseTransactions.forEach(t => {
|
||||
if (t.category in categorySpending) {
|
||||
categorySpending[t.category] += t.amount;
|
||||
} else if (EXPENSE_CATEGORIES.includes(t.category)) {
|
||||
// 지원되는 카테고리이지만 초기화되지 않은 경우
|
||||
categorySpending[t.category] = t.amount;
|
||||
} else if (t.category === '교통비') {
|
||||
// 예전 카테고리명 '교통비'를 '교통'으로 매핑
|
||||
categorySpending['교통'] = (categorySpending['교통'] || 0) + t.amount;
|
||||
} else {
|
||||
// 지원되지 않는 카테고리는 '기타'로 집계
|
||||
categorySpending['기타'] = (categorySpending['기타'] || 0) + t.amount;
|
||||
}
|
||||
});
|
||||
|
||||
return EXPENSE_CATEGORIES.map(category => ({
|
||||
title: category,
|
||||
current: categorySpending[category] || 0,
|
||||
total: categoryBudgets[category] || 0
|
||||
}));
|
||||
};
|
||||
33
src/contexts/budget/utils/constants.ts
Normal file
33
src/contexts/budget/utils/constants.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
|
||||
import { BudgetData } from '../types';
|
||||
|
||||
// 기본 데이터 상수 (기본값을 0으로 설정)
|
||||
export const DEFAULT_CATEGORY_BUDGETS: Record<string, number> = {
|
||||
음식: 0,
|
||||
쇼핑: 0,
|
||||
교통: 0,
|
||||
기타: 0
|
||||
};
|
||||
|
||||
export const DEFAULT_MONTHLY_BUDGET = 0;
|
||||
|
||||
// 초기 예산 데이터 생성
|
||||
export const getInitialBudgetData = (): BudgetData => {
|
||||
return {
|
||||
daily: {
|
||||
targetAmount: 0,
|
||||
spentAmount: 0,
|
||||
remainingAmount: 0
|
||||
},
|
||||
weekly: {
|
||||
targetAmount: 0,
|
||||
spentAmount: 0,
|
||||
remainingAmount: 0
|
||||
},
|
||||
monthly: {
|
||||
targetAmount: 0,
|
||||
spentAmount: 0,
|
||||
remainingAmount: 0
|
||||
}
|
||||
};
|
||||
};
|
||||
58
src/contexts/budget/utils/spendingCalculation.ts
Normal file
58
src/contexts/budget/utils/spendingCalculation.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
|
||||
import { BudgetData, Transaction } from '../types';
|
||||
|
||||
// 지출액 계산 (일일, 주간, 월간)
|
||||
export const calculateSpentAmounts = (
|
||||
transactions: Transaction[],
|
||||
prevBudgetData: BudgetData
|
||||
): BudgetData => {
|
||||
console.log("지출액 계산 시작, 트랜잭션 수:", transactions.length);
|
||||
|
||||
// 지출 거래 필터링
|
||||
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('어제') || 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);
|
||||
|
||||
// 기존 예산 목표 유지 (없으면 기본값 0)
|
||||
const dailyTarget = prevBudgetData?.daily?.targetAmount || 0;
|
||||
const weeklyTarget = prevBudgetData?.weekly?.targetAmount || 0;
|
||||
const monthlyTarget = prevBudgetData?.monthly?.targetAmount || 0;
|
||||
|
||||
// 예산 데이터 업데이트
|
||||
const updatedBudget = {
|
||||
daily: {
|
||||
targetAmount: dailyTarget,
|
||||
spentAmount: dailySpent,
|
||||
remainingAmount: Math.max(0, dailyTarget - dailySpent)
|
||||
},
|
||||
weekly: {
|
||||
targetAmount: weeklyTarget,
|
||||
spentAmount: weeklySpent,
|
||||
remainingAmount: Math.max(0, weeklyTarget - weeklySpent)
|
||||
},
|
||||
monthly: {
|
||||
targetAmount: monthlyTarget,
|
||||
spentAmount: monthlySpent,
|
||||
remainingAmount: Math.max(0, monthlyTarget - monthlySpent)
|
||||
}
|
||||
};
|
||||
|
||||
console.log("지출액 계산 결과:", updatedBudget);
|
||||
|
||||
return updatedBudget;
|
||||
};
|
||||
65
src/contexts/budget/utils/storageUtils.ts
Normal file
65
src/contexts/budget/utils/storageUtils.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
|
||||
import { BudgetData } from '../types';
|
||||
import { getInitialBudgetData } from './constants';
|
||||
import { toast } from '@/hooks/useToast.wrapper';
|
||||
|
||||
// 스토리지에서 안전하게 예산 데이터 가져오기
|
||||
export const safelyLoadBudgetData = (defaultData: BudgetData = getInitialBudgetData()): BudgetData => {
|
||||
try {
|
||||
const budgetDataStr = localStorage.getItem('budgetData');
|
||||
if (budgetDataStr) {
|
||||
const parsed = JSON.parse(budgetDataStr);
|
||||
|
||||
// 데이터 구조 검증 (daily, weekly, monthly 키 존재 확인)
|
||||
if (parsed && parsed.daily && parsed.weekly && parsed.monthly) {
|
||||
return parsed;
|
||||
} else {
|
||||
console.warn('저장된 예산 데이터 구조가 유효하지 않습니다. 기본값 사용.');
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('예산 데이터 로드 오류:', error);
|
||||
}
|
||||
|
||||
// 오류 발생 또는 데이터 없음 시 기본값 반환
|
||||
return defaultData;
|
||||
};
|
||||
|
||||
// 안전한 스토리지 접근
|
||||
export const safeStorage = {
|
||||
get: (key: string, defaultValue: any = null): any => {
|
||||
try {
|
||||
const value = localStorage.getItem(key);
|
||||
if (value === null) return defaultValue;
|
||||
return JSON.parse(value);
|
||||
} catch (error) {
|
||||
console.error(`스토리지 읽기 오류 (${key}):`, error);
|
||||
return defaultValue;
|
||||
}
|
||||
},
|
||||
|
||||
set: (key: string, value: any): boolean => {
|
||||
try {
|
||||
localStorage.setItem(key, JSON.stringify(value));
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error(`스토리지 쓰기 오류 (${key}):`, error);
|
||||
toast({
|
||||
title: "저장 오류",
|
||||
description: "데이터를 저장하는 중 문제가 발생했습니다.",
|
||||
variant: "destructive"
|
||||
});
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
remove: (key: string): boolean => {
|
||||
try {
|
||||
localStorage.removeItem(key);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error(`스토리지 삭제 오류 (${key}):`, error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user