From e3e29d6ebe807f7c7136c64ce6ac2ff8ef65abed Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Sun, 16 Mar 2025 10:03:35 +0000 Subject: [PATCH] Refactor budget sync logic Refactor budget sync logic into smaller modules. --- src/utils/sync/budget/downloadBudget.ts | 131 +++++++++++++++ src/utils/sync/budget/index.ts | 8 + src/utils/sync/budget/uploadBudget.ts | 116 ++++++++++++++ src/utils/sync/budgetSync.ts | 205 +----------------------- 4 files changed, 260 insertions(+), 200 deletions(-) create mode 100644 src/utils/sync/budget/downloadBudget.ts create mode 100644 src/utils/sync/budget/index.ts create mode 100644 src/utils/sync/budget/uploadBudget.ts diff --git a/src/utils/sync/budget/downloadBudget.ts b/src/utils/sync/budget/downloadBudget.ts new file mode 100644 index 0000000..fabb989 --- /dev/null +++ b/src/utils/sync/budget/downloadBudget.ts @@ -0,0 +1,131 @@ + +import { supabase } from '@/lib/supabase'; +import { isSyncEnabled } from '../syncSettings'; + +/** + * 서버에서 예산 데이터 다운로드 + */ +export const downloadBudgets = async (userId: string): Promise => { + if (!isSyncEnabled()) return; + + try { + console.log('서버에서 예산 데이터 다운로드 시작'); + + // 예산 데이터 및 카테고리 예산 데이터 가져오기 + const [budgetData, categoryData] = await Promise.all([ + fetchBudgetData(userId), + fetchCategoryBudgetData(userId) + ]); + + // 예산 데이터 처리 + if (budgetData) { + await processBudgetData(budgetData); + } + + // 카테고리 예산 데이터 처리 + if (categoryData && categoryData.length > 0) { + await processCategoryBudgetData(categoryData); + } + + console.log('예산 데이터 다운로드 완료'); + } catch (error) { + console.error('예산 데이터 다운로드 실패:', error); + throw error; + } +}; + +/** + * 예산 데이터 조회 + */ +async function fetchBudgetData(userId: string) { + const { data, error } = await supabase + .from('budgets') + .select('*') + .eq('user_id', userId) + .maybeSingle(); // 사용자당 하나의 예산 데이터만 존재 + + if (error && error.code !== 'PGRST116') { + console.error('예산 데이터 조회 실패:', error); + throw error; + } + + return data; +} + +/** + * 카테고리 예산 데이터 조회 + */ +async function fetchCategoryBudgetData(userId: string) { + const { data, error } = await supabase + .from('category_budgets') + .select('*') + .eq('user_id', userId); + + if (error) { + console.error('카테고리 예산 조회 실패:', error); + throw error; + } + + return data; +} + +/** + * 예산 데이터 처리 및 로컬 저장 + */ +async function processBudgetData(budgetData: any) { + console.log('서버에서 예산 데이터 수신:', budgetData); + + // 기존 로컬 데이터 가져오기 + const localBudgetDataStr = localStorage.getItem('budgetData'); + let localBudgetData = localBudgetDataStr ? JSON.parse(localBudgetDataStr) : { + daily: { targetAmount: 0, spentAmount: 0, remainingAmount: 0 }, + weekly: { targetAmount: 0, spentAmount: 0, remainingAmount: 0 }, + monthly: { targetAmount: 0, spentAmount: 0, remainingAmount: 0 } + }; + + // 서버 데이터로 업데이트 (지출 금액은 유지) + const updatedBudgetData = { + daily: { + targetAmount: budgetData.daily_target, + spentAmount: localBudgetData.daily.spentAmount, + remainingAmount: budgetData.daily_target - localBudgetData.daily.spentAmount + }, + weekly: { + targetAmount: budgetData.weekly_target, + spentAmount: localBudgetData.weekly.spentAmount, + remainingAmount: budgetData.weekly_target - localBudgetData.weekly.spentAmount + }, + monthly: { + targetAmount: budgetData.monthly_target, + spentAmount: localBudgetData.monthly.spentAmount, + remainingAmount: budgetData.monthly_target - localBudgetData.monthly.spentAmount + } + }; + + // 로컬 스토리지에 저장 + localStorage.setItem('budgetData', JSON.stringify(updatedBudgetData)); + console.log('예산 데이터 로컬 저장 완료'); + + // 이벤트 발생시켜 UI 업데이트 + window.dispatchEvent(new Event('budgetDataUpdated')); +} + +/** + * 카테고리 예산 데이터 처리 및 로컬 저장 + */ +async function processCategoryBudgetData(categoryData: any[]) { + console.log(`${categoryData.length}개의 카테고리 예산 수신`); + + // 카테고리 예산 로컬 형식으로 변환 + const localCategoryBudgets = categoryData.reduce((acc, curr) => { + acc[curr.category] = curr.amount; + return acc; + }, {} as Record); + + // 로컬 스토리지에 저장 + localStorage.setItem('categoryBudgets', JSON.stringify(localCategoryBudgets)); + console.log('카테고리 예산 로컬 저장 완료'); + + // 이벤트 발생시켜 UI 업데이트 + window.dispatchEvent(new Event('categoryBudgetsUpdated')); +} diff --git a/src/utils/sync/budget/index.ts b/src/utils/sync/budget/index.ts new file mode 100644 index 0000000..82279ef --- /dev/null +++ b/src/utils/sync/budget/index.ts @@ -0,0 +1,8 @@ + +import { uploadBudgets } from './uploadBudget'; +import { downloadBudgets } from './downloadBudget'; + +export { + uploadBudgets, + downloadBudgets +}; diff --git a/src/utils/sync/budget/uploadBudget.ts b/src/utils/sync/budget/uploadBudget.ts new file mode 100644 index 0000000..6aa821a --- /dev/null +++ b/src/utils/sync/budget/uploadBudget.ts @@ -0,0 +1,116 @@ + +import { supabase } from '@/lib/supabase'; +import { isSyncEnabled } from '../syncSettings'; + +/** + * 예산 데이터를 서버에 업로드 + */ +export const uploadBudgets = async (userId: string): Promise => { + if (!isSyncEnabled()) return; + + try { + const budgetData = localStorage.getItem('budgetData'); + const categoryBudgets = localStorage.getItem('categoryBudgets'); + + console.log('예산 데이터 업로드 시작'); + + // 예산 데이터 업로드 + if (budgetData) { + await uploadBudgetData(userId, JSON.parse(budgetData)); + } + + // 카테고리 예산 업로드 + if (categoryBudgets) { + await uploadCategoryBudgets(userId, JSON.parse(categoryBudgets)); + } + + console.log('예산 데이터 업로드 완료'); + } catch (error) { + console.error('예산 데이터 업로드 실패:', error); + throw error; + } +}; + +/** + * 일반 예산 데이터 업로드 + */ +async function uploadBudgetData(userId: string, parsedBudgetData: any): Promise { + // 기존 예산 데이터 확인 + const { data: existingBudgets, error: fetchError } = await supabase + .from('budgets') + .select('*') + .eq('user_id', userId); + + if (fetchError) { + console.error('기존 예산 데이터 조회 실패:', fetchError); + } + + // 업데이트 또는 삽입 결정 + if (existingBudgets && existingBudgets.length > 0) { + // 기존 데이터 업데이트 + const { error } = await supabase + .from('budgets') + .update({ + daily_target: parsedBudgetData.daily.targetAmount, + weekly_target: parsedBudgetData.weekly.targetAmount, + monthly_target: parsedBudgetData.monthly.targetAmount, + updated_at: new Date().toISOString() + }) + .eq('user_id', userId); + + if (error) { + console.error('예산 데이터 업데이트 실패:', error); + throw error; + } + } else { + // 새 데이터 삽입 + const { error } = await supabase + .from('budgets') + .insert({ + user_id: userId, + daily_target: parsedBudgetData.daily.targetAmount, + weekly_target: parsedBudgetData.weekly.targetAmount, + monthly_target: parsedBudgetData.monthly.targetAmount + }); + + if (error) { + console.error('예산 데이터 삽입 실패:', error); + throw error; + } + } +} + +/** + * 카테고리 예산 데이터 업로드 + */ +async function uploadCategoryBudgets(userId: string, parsedCategoryBudgets: Record): Promise { + // 기존 카테고리 예산 삭제 + const { error: deleteError } = await supabase + .from('category_budgets') + .delete() + .eq('user_id', userId); + + if (deleteError) { + console.error('기존 카테고리 예산 삭제 실패:', deleteError); + } + + // 카테고리별 예산 데이터 변환 및 삽입 + const categoryEntries = Object.entries(parsedCategoryBudgets).map( + ([category, amount]) => ({ + user_id: userId, + category, + amount + }) + ); + + if (categoryEntries.length > 0) { + const { error } = await supabase + .from('category_budgets') + .insert(categoryEntries); + + if (error) { + console.error('카테고리 예산 삽입 실패:', error); + throw error; + } + } +} diff --git a/src/utils/sync/budgetSync.ts b/src/utils/sync/budgetSync.ts index a4240cc..8e786b5 100644 --- a/src/utils/sync/budgetSync.ts +++ b/src/utils/sync/budgetSync.ts @@ -1,203 +1,8 @@ -import { supabase } from '@/lib/supabase'; -import { isSyncEnabled } from './syncSettings'; -import { toast } from '@/hooks/useToast.wrapper'; +// 새로운 예산 동기화 모듈을 내보내는 파일 +import { uploadBudgets, downloadBudgets } from './budget'; -/** - * Upload budget data from local storage to Supabase - */ -export const uploadBudgets = async (userId: string): Promise => { - if (!isSyncEnabled()) return; - - try { - const budgetData = localStorage.getItem('budgetData'); - const categoryBudgets = localStorage.getItem('categoryBudgets'); - - console.log('예산 데이터 업로드 시작'); - - // 예산 데이터 업로드 - if (budgetData) { - const parsedBudgetData = JSON.parse(budgetData); - - // 기존 예산 데이터 확인 - const { data: existingBudgets, error: fetchError } = await supabase - .from('budgets') - .select('*') - .eq('user_id', userId); - - if (fetchError) { - console.error('기존 예산 데이터 조회 실패:', fetchError); - } - - // 업데이트 또는 삽입 결정 - if (existingBudgets && existingBudgets.length > 0) { - // 기존 데이터 업데이트 - const { error } = await supabase - .from('budgets') - .update({ - daily_target: parsedBudgetData.daily.targetAmount, - weekly_target: parsedBudgetData.weekly.targetAmount, - monthly_target: parsedBudgetData.monthly.targetAmount, - updated_at: new Date().toISOString() - }) - .eq('user_id', userId); - - if (error) { - console.error('예산 데이터 업데이트 실패:', error); - throw error; - } - } else { - // 새 데이터 삽입 - const { error } = await supabase - .from('budgets') - .insert({ - user_id: userId, - daily_target: parsedBudgetData.daily.targetAmount, - weekly_target: parsedBudgetData.weekly.targetAmount, - monthly_target: parsedBudgetData.monthly.targetAmount - }); - - if (error) { - console.error('예산 데이터 삽입 실패:', error); - throw error; - } - } - } - - // 카테고리 예산 업로드 - if (categoryBudgets) { - const parsedCategoryBudgets = JSON.parse(categoryBudgets); - - // 기존 카테고리 예산 삭제 - const { error: deleteError } = await supabase - .from('category_budgets') - .delete() - .eq('user_id', userId); - - if (deleteError) { - console.error('기존 카테고리 예산 삭제 실패:', deleteError); - } - - // 카테고리별 예산 데이터 변환 및 삽입 - const categoryEntries = Object.entries(parsedCategoryBudgets).map( - ([category, amount]) => ({ - user_id: userId, - category, - amount - }) - ); - - if (categoryEntries.length > 0) { - const { error } = await supabase - .from('category_budgets') - .insert(categoryEntries); - - if (error) { - console.error('카테고리 예산 삽입 실패:', error); - throw error; - } - } - } - - console.log('예산 데이터 업로드 완료'); - } catch (error) { - console.error('예산 데이터 업로드 실패:', error); - throw error; - } -}; - -/** - * Download budget data from Supabase to local storage - */ -export const downloadBudgets = async (userId: string): Promise => { - if (!isSyncEnabled()) return; - - try { - console.log('서버에서 예산 데이터 다운로드 시작'); - - // 예산 데이터 가져오기 - const { data: budgetData, error: budgetError } = await supabase - .from('budgets') - .select('*') - .eq('user_id', userId) - .maybeSingle(); // 사용자당 하나의 예산 데이터만 존재 - - if (budgetError && budgetError.code !== 'PGRST116') { - console.error('예산 데이터 조회 실패:', budgetError); - throw budgetError; - } - - // 카테고리 예산 가져오기 - const { data: categoryData, error: categoryError } = await supabase - .from('category_budgets') - .select('*') - .eq('user_id', userId); - - if (categoryError) { - console.error('카테고리 예산 조회 실패:', categoryError); - throw categoryError; - } - - // 서버에서 받은 예산 데이터가 있으면 로컬에 저장 - if (budgetData) { - console.log('서버에서 예산 데이터 수신:', budgetData); - - // 기존 로컬 데이터 가져오기 - const localBudgetDataStr = localStorage.getItem('budgetData'); - let localBudgetData = localBudgetDataStr ? JSON.parse(localBudgetDataStr) : { - daily: { targetAmount: 0, spentAmount: 0, remainingAmount: 0 }, - weekly: { targetAmount: 0, spentAmount: 0, remainingAmount: 0 }, - monthly: { targetAmount: 0, spentAmount: 0, remainingAmount: 0 } - }; - - // 서버 데이터로 업데이트 (지출 금액은 유지) - const updatedBudgetData = { - daily: { - targetAmount: budgetData.daily_target, - spentAmount: localBudgetData.daily.spentAmount, - remainingAmount: budgetData.daily_target - localBudgetData.daily.spentAmount - }, - weekly: { - targetAmount: budgetData.weekly_target, - spentAmount: localBudgetData.weekly.spentAmount, - remainingAmount: budgetData.weekly_target - localBudgetData.weekly.spentAmount - }, - monthly: { - targetAmount: budgetData.monthly_target, - spentAmount: localBudgetData.monthly.spentAmount, - remainingAmount: budgetData.monthly_target - localBudgetData.monthly.spentAmount - } - }; - - // 로컬 스토리지에 저장 - localStorage.setItem('budgetData', JSON.stringify(updatedBudgetData)); - console.log('예산 데이터 로컬 저장 완료'); - - // 이벤트 발생시켜 UI 업데이트 - window.dispatchEvent(new Event('budgetDataUpdated')); - } - - // 서버에서 받은 카테고리 예산 데이터가 있으면 로컬에 저장 - if (categoryData && categoryData.length > 0) { - console.log(`${categoryData.length}개의 카테고리 예산 수신`); - - // 카테고리 예산 로컬 형식으로 변환 - const localCategoryBudgets = categoryData.reduce((acc, curr) => { - acc[curr.category] = curr.amount; - return acc; - }, {} as Record); - - // 로컬 스토리지에 저장 - localStorage.setItem('categoryBudgets', JSON.stringify(localCategoryBudgets)); - console.log('카테고리 예산 로컬 저장 완료'); - - // 이벤트 발생시켜 UI 업데이트 - window.dispatchEvent(new Event('categoryBudgetsUpdated')); - } - - console.log('예산 데이터 다운로드 완료'); - } catch (error) { - console.error('예산 데이터 다운로드 실패:', error); - throw error; - } +export { + uploadBudgets, + downloadBudgets };