Fix data loss on sync after reset

Addresses an issue where budget data was lost after a data reset, logout, and subsequent synchronization. The fix ensures budget data is correctly restored in such scenarios.
This commit is contained in:
gpt-engineer-app[bot]
2025-03-21 12:29:28 +00:00
parent f7eb7d5af7
commit befb29611b
4 changed files with 303 additions and 138 deletions

View File

@@ -1,47 +1,49 @@
import { SyncResult } from '@/utils/sync/data';
import { toast } from '@/hooks/useToast.wrapper';
import type { SyncResult } from '@/utils/syncUtils';
/**
* 동기화 결과 처리 함수
*/
// 동기화 결과 처리 함수
export const handleSyncResult = (result: SyncResult) => {
if (!result) {
toast({
title: "동기화 오류",
description: "동기화 결과를 처리할 수 없습니다.",
variant: "destructive"
});
return;
}
if (result.success) {
if (result.partial) {
// 부분 성공 처리
toast({
title: "부분 동기화 완료",
description: "일부 데이터만 동기화되었습니다. 다시 시도해보세요.",
variant: "default"
});
} else {
// 전체 성공 처리
if (result.uploadSuccess && result.downloadSuccess) {
// 양방향 동기화 성공
toast({
title: "동기화 완료",
description: "모든 데이터가 성공적으로 동기화되었습니다.",
variant: "default"
});
} else if (result.uploadSuccess) {
// 업로드만 성공
toast({
title: "동기화 완료",
description: "로컬 데이터가 클라우드에 업로드되었습니다.",
});
} else if (result.downloadSuccess) {
// 다운로드만 성공
toast({
title: "동기화 완료",
description: "클라우드 데이터가 기기에 다운로드되었습니다.",
});
}
// 상세 결과 로깅
console.log("동기화 세부 결과:", {
예산업로드: result.details?.budgetUpload ? '성공' : '실패',
예산다운로드: result.details?.budgetDownload ? '성공' : '실패',
트랜잭션업로드: result.details?.transactionUpload ? '성공' : '실패',
트랜잭션다운로드: result.details?.transactionDownload ? '성공' : '실패'
});
// 데이터 변경 이벤트 발생
window.dispatchEvent(new Event('budgetDataUpdated'));
window.dispatchEvent(new Event('categoryBudgetsUpdated'));
window.dispatchEvent(new Event('transactionUpdated'));
}
return true;
} else {
// 실패 처리
// 동기화 실패
console.error("동기화 실패 세부 결과:", result.details);
toast({
title: "동기화 실패",
description: "데이터 동기화에 실패했습니다. 나중에 다시 시도해세요.",
description: "데이터 동기화 중 문제가 발생했습니다. 다시 시도해세요.",
variant: "destructive"
});
return false;
}
};

View File

@@ -1,3 +1,4 @@
import { supabase } from '@/lib/supabase';
import { isSyncEnabled } from '../syncSettings';
import {
@@ -20,10 +21,16 @@ export const uploadBudgets = async (userId: string): Promise<void> => {
// 예산 데이터 업로드
if (budgetDataStr) {
const budgetData = JSON.parse(budgetDataStr);
await uploadBudgetData(userId, budgetData);
// 월간 예산이 0보다 클 때만 업로드
if (budgetData.monthly && budgetData.monthly.targetAmount > 0) {
console.log('유효한 월간 예산 발견:', budgetData.monthly.targetAmount);
await uploadBudgetData(userId, budgetData);
// 업로드 성공 후 수정 추적 정보 초기화
clearModifiedBudget();
} else {
console.log('월간 예산이 0 이하거나 없어서 업로드 건너뜀');
}
} else {
console.log('업로드할 예산 데이터가 없음');
}
@@ -31,10 +38,17 @@ export const uploadBudgets = async (userId: string): Promise<void> => {
// 카테고리 예산 업로드
if (categoryBudgetsStr) {
const categoryBudgets = JSON.parse(categoryBudgetsStr);
await uploadCategoryBudgets(userId, categoryBudgets);
// 총 카테고리 예산이 0보다 클 때만 업로드
const totalCategoryBudget = Object.values(categoryBudgets).reduce((sum: number, val: number) => sum + val, 0);
if (totalCategoryBudget > 0) {
console.log('유효한 카테고리 예산 발견:', totalCategoryBudget);
await uploadCategoryBudgets(userId, categoryBudgets);
// 업로드 성공 후 수정 추적 정보 초기화
clearModifiedCategoryBudgets();
} else {
console.log('카테고리 예산이 모두 0이어서 업로드 건너뜀');
}
} else {
console.log('업로드할 카테고리 예산이 없음');
}
@@ -73,13 +87,24 @@ async function uploadBudgetData(userId: string, parsedBudgetData: Record<string,
// 월간 타겟 금액 가져오기
const monthlyTarget = parsedBudgetData.monthly.targetAmount;
// 예산이 0 이하면 업로드하지 않음
if (monthlyTarget <= 0) {
console.log('월간 예산이 0 이하여서 업로드 건너뜀:', monthlyTarget);
return;
}
console.log('업로드할 월간 예산:', monthlyTarget);
// 현재 타임스탬프
const currentTimestamp = new Date().toISOString();
// 업데이트 또는 삽입 결정
// 가능한 경우 서버 데이터와 비교하여 필요한 경우만 업데이트
if (existingBudgets && existingBudgets.length > 0) {
const existingBudget = existingBudgets[0];
// 새 예산이 기존 예산보다 클 때만 업데이트
if (monthlyTarget > existingBudget.total_budget) {
console.log(`새 예산(${monthlyTarget})이 기존 예산(${existingBudget.total_budget})보다 큼, 업데이트 실행`);
// 기존 데이터 업데이트
const { error } = await supabase
.from('budgets')
@@ -87,7 +112,7 @@ async function uploadBudgetData(userId: string, parsedBudgetData: Record<string,
total_budget: monthlyTarget,
updated_at: currentTimestamp
})
.eq('id', existingBudgets[0].id);
.eq('id', existingBudget.id);
if (error) {
console.error('예산 데이터 업데이트 실패:', error);
@@ -95,6 +120,9 @@ async function uploadBudgetData(userId: string, parsedBudgetData: Record<string,
}
console.log('예산 데이터 업데이트 성공');
} else {
console.log(`새 예산(${monthlyTarget})이 기존 예산(${existingBudget.total_budget})보다 작거나 같음, 업데이트 건너뜀`);
}
} else {
// 새 데이터 삽입
const { error } = await supabase
@@ -123,6 +151,40 @@ async function uploadBudgetData(userId: string, parsedBudgetData: Record<string,
async function uploadCategoryBudgets(userId: string, parsedCategoryBudgets: Record<string, number>): Promise<void> {
console.log('카테고리 예산 업로드:', parsedCategoryBudgets);
// 기존 카테고리 예산 확인
const { data: existingCategoryBudgets, error: fetchError } = await supabase
.from('category_budgets')
.select('*')
.eq('user_id', userId);
if (fetchError) {
console.error('기존 카테고리 예산 조회 실패:', fetchError);
throw fetchError;
}
// 기존 카테고리 예산의 총액 계산
let existingTotal = 0;
if (existingCategoryBudgets && existingCategoryBudgets.length > 0) {
existingTotal = existingCategoryBudgets.reduce((sum, item) => sum + item.amount, 0);
}
// 새 카테고리 예산의 총액 계산
const newTotal = Object.values(parsedCategoryBudgets).reduce((sum: number, val: number) => sum + val, 0);
// 새 카테고리 예산 총액이 기존 카테고리 예산 총액보다 작거나 같으면 업로드 건너뜀
if (newTotal <= existingTotal && existingTotal > 0) {
console.log(`새 카테고리 예산 총액(${newTotal})이 기존 예산 총액(${existingTotal})보다 작거나 같음, 업로드 건너뜀`);
return;
}
// 새 카테고리 예산 총액이 0이면 업로드 건너뜀
if (newTotal <= 0) {
console.log('새 카테고리 예산 총액이 0이하여서 업로드 건너뜀');
return;
}
console.log(`새 카테고리 예산 총액(${newTotal})이 기존 예산 총액(${existingTotal})보다 큼, 업로드 실행`);
// 기존 카테고리 예산 삭제
const { error: deleteError } = await supabase
.from('category_budgets')

View File

@@ -40,28 +40,7 @@ export const syncAllData = async (userId: string): Promise<SyncResult> => {
try {
console.log('데이터 동기화 시작 - 사용자 ID:', userId);
// 여기서는 업로드를 먼저 시도합니다 (로컬 데이터 보존을 위해)
try {
// 예산 데이터 업로드
await uploadBudgets(userId);
result.details!.budgetUpload = true;
console.log('예산 업로드 성공');
// 트랜잭션 데이터 업로드
await uploadTransactions(userId);
result.details!.transactionUpload = true;
console.log('트랜잭션 업로드 성공');
// 업로드 성공 설정
result.uploadSuccess = true;
} catch (uploadError) {
console.error('데이터 업로드 실패:', uploadError);
result.uploadSuccess = false;
}
// 그 다음 다운로드 시도
try {
// 서버에 데이터가 없는 경우를 확인하기 위해 먼저 데이터 유무 검사
// 서버에 데이터가 있는지 먼저 확인
const { data: budgetData } = await supabase
.from('budgets')
.select('count')
@@ -74,28 +53,73 @@ export const syncAllData = async (userId: string): Promise<SyncResult> => {
.eq('user_id', userId)
.single();
// 서버에 데이터가 없지만 로컬에 데이터가 있는 경우, 다운로드를 건너뜀
const serverHasData = (budgetData?.count || 0) > 0 || (transactionsData?.count || 0) > 0;
const serverHasBudgetData = (budgetData?.count || 0) > 0;
const serverHasTransactionData = (transactionsData?.count || 0) > 0;
const serverHasData = serverHasBudgetData || serverHasTransactionData;
if (!serverHasData && (backupBudgetData || backupTransactions)) {
console.log('서버에 데이터가 없고 로컬 데이터가 있어 다운로드 건너뜀');
result.downloadSuccess = true;
result.details!.budgetDownload = true;
result.details!.transactionDownload = true;
} else {
// 로컬 데이터 존재 여부 확인
const localHasBudgetData = backupBudgetData && JSON.parse(backupBudgetData).monthly.targetAmount > 0;
const localHasTransactionData = backupTransactions && JSON.parse(backupTransactions).length > 0;
const localHasData = localHasBudgetData || localHasTransactionData;
console.log('데이터 존재 여부:', {
: {
예산: serverHasBudgetData,
거래: serverHasTransactionData
},
: {
예산: localHasBudgetData,
거래: localHasTransactionData
}
});
// 서버에 데이터가 없고 로컬에 데이터가 있으면 업로드 먼저
if (!serverHasData && localHasData) {
console.log('서버에 데이터가 없고 로컬에 데이터가 있어 업로드 우선 실행');
try {
// 예산 데이터 업로드
if (localHasBudgetData) {
await uploadBudgets(userId);
result.details!.budgetUpload = true;
console.log('예산 업로드 성공');
}
// 트랜잭션 데이터 업로드
if (localHasTransactionData) {
await uploadTransactions(userId);
result.details!.transactionUpload = true;
console.log('트랜잭션 업로드 성공');
}
// 업로드 성공 설정
result.uploadSuccess = true;
} catch (uploadError) {
console.error('데이터 업로드 실패:', uploadError);
result.uploadSuccess = false;
}
}
// 서버에 데이터가 있으면 다운로드 먼저 (기존 로직)
else if (serverHasData) {
console.log('서버에 데이터가 있어 다운로드 우선 실행');
try {
// 예산 데이터 다운로드
if (serverHasBudgetData) {
await downloadBudgets(userId);
result.details!.budgetDownload = true;
console.log('예산 다운로드 성공');
}
// 트랜잭션 데이터 다운로드
if (serverHasTransactionData) {
await downloadTransactions(userId);
result.details!.transactionDownload = true;
console.log('트랜잭션 다운로드 성공');
}
// 다운로드 성공 설정
result.downloadSuccess = true;
}
} catch (downloadError) {
console.error('데이터 다운로드 실패:', downloadError);
result.downloadSuccess = false;
@@ -117,6 +141,44 @@ export const syncAllData = async (userId: string): Promise<SyncResult> => {
window.dispatchEvent(new Event('transactionUpdated'));
}
// 다운로드 후 로컬 데이터 있으면 추가 업로드 시도
if (result.downloadSuccess && localHasData) {
console.log('다운로드 후 로컬 데이터 업로드 시도');
try {
// 예산 데이터 업로드
if (localHasBudgetData) {
await uploadBudgets(userId);
result.details!.budgetUpload = true;
console.log('예산 업로드 성공');
}
// 트랜잭션 데이터 업로드
if (localHasTransactionData) {
await uploadTransactions(userId);
result.details!.transactionUpload = true;
console.log('트랜잭션 업로드 성공');
}
// 업로드 성공 설정
result.uploadSuccess = true;
} catch (uploadError) {
console.error('데이터 업로드 실패:', uploadError);
result.uploadSuccess = false;
}
}
}
// 양쪽 다 데이터가 없는 경우
else {
console.log('서버와 로컬 모두 데이터가 없어 동기화 건너뜀');
result.uploadSuccess = true;
result.downloadSuccess = true;
result.details!.budgetUpload = true;
result.details!.budgetDownload = true;
result.details!.transactionUpload = true;
result.details!.transactionDownload = true;
}
// 부분 성공 여부 설정
result.partial = (result.uploadSuccess || result.downloadSuccess) && !(result.uploadSuccess && result.downloadSuccess);

View File

@@ -35,6 +35,12 @@ export const downloadBudgets = async (userId: string): Promise<void> => {
if (budgetData && budgetData.length > 0) {
const monthlyBudget = budgetData[0].total_budget;
// 예산이 0이거나 음수면 처리하지 않음
if (monthlyBudget <= 0) {
console.log('서버에 저장된 예산이 0이하여서 다운로드 건너뜀:', monthlyBudget);
return;
}
// 기존 예산 데이터 가져오기
let budgetDataObj = {
daily: { targetAmount: 0, spentAmount: 0, remainingAmount: 0 },
@@ -61,6 +67,11 @@ export const downloadBudgets = async (userId: string): Promise<void> => {
console.error('로컬 예산 데이터 파싱 오류:', e);
}
// 서버의 예산이 로컬 예산보다 클 때에만 로컬 예산 덮어쓰기
if (monthlyBudget > budgetDataObj.monthly.targetAmount) {
console.log('서버 예산이 로컬 예산보다 큼. 로컬 예산 업데이트:',
`서버: ${monthlyBudget}, 로컬: ${budgetDataObj.monthly.targetAmount}`);
// 월간 예산만 업데이트
budgetDataObj.monthly.targetAmount = monthlyBudget;
@@ -81,6 +92,10 @@ export const downloadBudgets = async (userId: string): Promise<void> => {
// 업데이트된 예산 데이터 저장
localStorage.setItem('budgetData', JSON.stringify(budgetDataObj));
console.log('월간 예산 다운로드 및 저장 완료:', monthlyBudget);
} else {
console.log('로컬 예산이 서버 예산보다 크거나 같음. 예산 유지:',
`로컬: ${budgetDataObj.monthly.targetAmount}, 서버: ${monthlyBudget}`);
}
} else {
console.log('서버에 저장된 월간 예산 데이터가 없습니다.');
}
@@ -97,16 +112,40 @@ export const downloadBudgets = async (userId: string): Promise<void> => {
}
if (categoryData && categoryData.length > 0) {
// 현재 로컬 카테고리 예산 확인
let currentCategoryBudgets: Record<string, number> = {};
try {
const storedCategoryBudgets = localStorage.getItem('categoryBudgets');
if (storedCategoryBudgets) {
currentCategoryBudgets = JSON.parse(storedCategoryBudgets);
}
} catch (e) {
console.error('로컬 카테고리 예산 파싱 오류:', e);
}
// 카테고리 예산 객체 구성
const categoryBudgets: Record<string, number> = {};
const serverCategoryBudgets: Record<string, number> = {};
categoryData.forEach(item => {
categoryBudgets[item.category] = item.amount;
serverCategoryBudgets[item.category] = item.amount;
});
// 서버 카테고리 예산 총액 계산
const serverTotal = Object.values(serverCategoryBudgets).reduce((sum, val) => sum + val, 0);
const localTotal = Object.values(currentCategoryBudgets).reduce((sum, val) => sum + val, 0);
// 서버 카테고리 예산이 로컬보다 클 때만 업데이트
if (serverTotal > localTotal) {
console.log('서버 카테고리 예산이 로컬보다 큼. 로컬 업데이트:',
`서버: ${serverTotal}, 로컬: ${localTotal}`);
// 카테고리 예산 저장
localStorage.setItem('categoryBudgets', JSON.stringify(categoryBudgets));
console.log('카테고리 예산 다운로드 및 저장 완료:', categoryBudgets);
localStorage.setItem('categoryBudgets', JSON.stringify(serverCategoryBudgets));
console.log('카테고리 예산 다운로드 및 저장 완료:', serverCategoryBudgets);
} else {
console.log('로컬 카테고리 예산이 서버보다 크거나 같음. 유지:',
`로컬: ${localTotal}, 서버: ${serverTotal}`);
}
} else {
console.log('서버에 저장된 카테고리 예산 데이터가 없습니다.');
}