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:
@@ -1,232 +1,24 @@
|
||||
|
||||
import { BudgetData, BudgetPeriod, CategoryBudget, Transaction } from './types';
|
||||
import { EXPENSE_CATEGORIES } from '@/constants/categoryIcons';
|
||||
// 분리된 유틸리티 파일들에서 함수와 상수를 재내보내기
|
||||
export {
|
||||
DEFAULT_CATEGORY_BUDGETS,
|
||||
DEFAULT_MONTHLY_BUDGET,
|
||||
getInitialBudgetData
|
||||
} from './utils/constants';
|
||||
|
||||
// 기본 데이터 상수 (기본값을 0으로 설정)
|
||||
export const DEFAULT_CATEGORY_BUDGETS: Record<string, number> = {
|
||||
음식: 0,
|
||||
쇼핑: 0,
|
||||
교통: 0,
|
||||
기타: 0
|
||||
};
|
||||
export {
|
||||
calculateCategorySpending
|
||||
} from './utils/categoryUtils';
|
||||
|
||||
export const DEFAULT_MONTHLY_BUDGET = 0;
|
||||
export {
|
||||
calculateUpdatedBudgetData
|
||||
} from './utils/budgetCalculation';
|
||||
|
||||
// 카테고리별 지출 계산
|
||||
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
|
||||
}));
|
||||
};
|
||||
export {
|
||||
calculateSpentAmounts
|
||||
} from './utils/spendingCalculation';
|
||||
|
||||
// 예산 데이터 업데이트 계산 - 수정된 함수
|
||||
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;
|
||||
};
|
||||
|
||||
// 지출액 계산 (일일, 주간, 월간) - 문제 수정
|
||||
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;
|
||||
};
|
||||
|
||||
// 초기 예산 데이터 생성
|
||||
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
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// 스토리지에서 안전하게 예산 데이터 가져오기
|
||||
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 {
|
||||
safelyLoadBudgetData,
|
||||
safeStorage
|
||||
} from './utils/storageUtils';
|
||||
|
||||
@@ -1,50 +1,17 @@
|
||||
|
||||
import { BudgetData } from '../types';
|
||||
import { getInitialBudgetData } from '../budgetUtils';
|
||||
import { toast } from '@/hooks/useToast.wrapper'; // 래퍼 사용
|
||||
import { getInitialBudgetData, safelyLoadBudgetData, safeStorage } from '../budgetUtils';
|
||||
import { toast } from '@/hooks/useToast.wrapper';
|
||||
|
||||
/**
|
||||
* 예산 데이터 불러오기
|
||||
*/
|
||||
export const loadBudgetDataFromStorage = (): BudgetData => {
|
||||
try {
|
||||
const storedBudgetData = localStorage.getItem('budgetData');
|
||||
if (storedBudgetData) {
|
||||
try {
|
||||
const parsed = JSON.parse(storedBudgetData);
|
||||
console.log('예산 데이터 로드 완료', parsed);
|
||||
|
||||
// 데이터 유효성 검사 추가
|
||||
if (!parsed || !parsed.monthly || !parsed.daily || !parsed.weekly) {
|
||||
console.warn('불완전한 예산 데이터, 백업에서 복구 시도');
|
||||
throw new Error('잘못된 형식의 예산 데이터');
|
||||
}
|
||||
|
||||
return parsed;
|
||||
} catch (error) {
|
||||
console.error('예산 데이터 파싱 오류:', error);
|
||||
|
||||
// 백업에서 복구 시도
|
||||
const backupData = localStorage.getItem('budgetData_backup');
|
||||
if (backupData) {
|
||||
try {
|
||||
const parsed = JSON.parse(backupData);
|
||||
console.log('백업에서 예산 데이터 복구 완료', parsed);
|
||||
|
||||
// 백업 데이터 유효성 검사
|
||||
if (!parsed || !parsed.monthly || !parsed.daily || !parsed.weekly) {
|
||||
console.warn('백업 예산 데이터도 불완전함, 새 데이터 생성');
|
||||
throw new Error('백업 데이터도 잘못된 형식');
|
||||
}
|
||||
|
||||
localStorage.setItem('budgetData', backupData); // 메인 스토리지에 복구
|
||||
return parsed;
|
||||
} catch (backupError) {
|
||||
console.error('백업 데이터 복구 실패:', backupError);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 새로운 safelyLoadBudgetData 함수 사용
|
||||
const budgetData = safelyLoadBudgetData();
|
||||
console.log('예산 데이터 로드 완료', budgetData);
|
||||
return budgetData;
|
||||
} catch (error) {
|
||||
console.error('예산 데이터 로드 중 오류:', error);
|
||||
}
|
||||
@@ -73,9 +40,8 @@ export const saveBudgetDataToStorage = (budgetData: BudgetData): void => {
|
||||
// 이전 예산과 비교하여 변경 여부 확인
|
||||
let hasChanged = true;
|
||||
try {
|
||||
const oldDataString = localStorage.getItem('budgetData');
|
||||
if (oldDataString) {
|
||||
const oldData = JSON.parse(oldDataString);
|
||||
const oldData = safeStorage.get('budgetData');
|
||||
if (oldData) {
|
||||
// 월간 예산이 동일하면 변경되지 않은 것으로 판단
|
||||
hasChanged = oldData.monthly.targetAmount !== budgetData.monthly.targetAmount;
|
||||
}
|
||||
@@ -84,12 +50,12 @@ export const saveBudgetDataToStorage = (budgetData: BudgetData): void => {
|
||||
}
|
||||
|
||||
// 로컬 스토리지에 저장
|
||||
localStorage.setItem('budgetData', dataString);
|
||||
safeStorage.set('budgetData', budgetData);
|
||||
console.log('예산 데이터 저장 완료', budgetData);
|
||||
|
||||
// 중요: 즉시 자동 백업 (데이터 손실 방지)
|
||||
localStorage.setItem('budgetData_backup', dataString);
|
||||
localStorage.setItem('lastBudgetSaveTime', new Date().toISOString());
|
||||
safeStorage.set('budgetData_backup', budgetData);
|
||||
safeStorage.set('lastBudgetSaveTime', new Date().toISOString());
|
||||
|
||||
// 이벤트 발생 (단일 이벤트로 통합)
|
||||
const event = new CustomEvent('budgetChanged', {
|
||||
@@ -121,14 +87,13 @@ export const saveBudgetDataToStorage = (budgetData: BudgetData): void => {
|
||||
*/
|
||||
export const clearAllBudgetData = (): void => {
|
||||
try {
|
||||
localStorage.removeItem('budgetData');
|
||||
localStorage.removeItem('budgetData_backup');
|
||||
safeStorage.remove('budgetData');
|
||||
safeStorage.remove('budgetData_backup');
|
||||
|
||||
// 기본값으로 재설정
|
||||
const initialData = getInitialBudgetData();
|
||||
const dataString = JSON.stringify(initialData);
|
||||
localStorage.setItem('budgetData', dataString);
|
||||
localStorage.setItem('budgetData_backup', dataString);
|
||||
safeStorage.set('budgetData', initialData);
|
||||
safeStorage.set('budgetData_backup', initialData);
|
||||
|
||||
console.log('예산 데이터가 초기화되었습니다.');
|
||||
|
||||
@@ -136,7 +101,7 @@ export const clearAllBudgetData = (): void => {
|
||||
window.dispatchEvent(new Event('budgetDataUpdated'));
|
||||
window.dispatchEvent(new StorageEvent('storage', {
|
||||
key: 'budgetData',
|
||||
newValue: dataString
|
||||
newValue: JSON.stringify(initialData)
|
||||
}));
|
||||
|
||||
// 토스트 알림 (사용자가 직접 초기화한 경우만)
|
||||
|
||||
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