diff --git a/src/hooks/sync/useManualSync.ts b/src/hooks/sync/useManualSync.ts index e6bc37c..cd038d2 100644 --- a/src/hooks/sync/useManualSync.ts +++ b/src/hooks/sync/useManualSync.ts @@ -31,8 +31,11 @@ export const useManualSync = (user: any) => { try { setSyncing(true); - // 안전한 동기화 함수 사용 - const result = await trySyncAllData(userId); + // 안전한 동기화 함수 사용 (setSyncState를 더미 함수로 제공) + const result = await trySyncAllData(userId, (state) => { + // 상태 업데이트 콜백 (필요에 따라 처리) + console.log('동기화 상태 업데이트:', state); + }); handleSyncResult(result); setLastSyncTime(getLastSyncTime()); diff --git a/src/hooks/sync/useSyncToggle.ts b/src/hooks/sync/useSyncToggle.ts index 287c489..794d192 100644 --- a/src/hooks/sync/useSyncToggle.ts +++ b/src/hooks/sync/useSyncToggle.ts @@ -120,8 +120,11 @@ const performSync = async (userId: string) => { if (!userId) return; try { - // 안전한 동기화 함수 사용 - const result = await trySyncAllData(userId); + // 안전한 동기화 함수 사용 (setSyncState를 더미 함수로 제공) + const result = await trySyncAllData(userId, (state) => { + // 상태 업데이트 콜백 (필요에 따라 처리) + console.log('동기화 상태 업데이트:', state); + }); return result; } catch (error) { console.error('동기화 오류:', error); diff --git a/src/utils/sync/config.ts b/src/utils/sync/config.ts index fe7a28a..97f8bb7 100644 --- a/src/utils/sync/config.ts +++ b/src/utils/sync/config.ts @@ -1,52 +1,28 @@ -import { supabase } from '@/lib/supabase'; - /** - * 동기화 기능이 활성화되어 있는지 확인 - */ -export const isSyncEnabled = (): boolean => { - try { - return localStorage.getItem('syncEnabled') === 'true'; - } catch (error) { - console.error('동기화 설정 확인 중 오류:', error); - return false; - } -}; - -/** - * 동기화 설정 업데이트 + * 동기화 기능 활성화 여부를 localStorage에 저장 + * @param enabled 활성화 여부 */ export const setSyncEnabled = (enabled: boolean): void => { - try { - localStorage.setItem('syncEnabled', enabled.toString()); - } catch (error) { - console.error('동기화 설정 업데이트 중 오류:', error); - } + localStorage.setItem('syncEnabled', enabled ? 'true' : 'false'); + // 이벤트 발생하여 다른 컴포넌트에 변경 알림 + window.dispatchEvent(new Event('syncEnabledChanged')); }; /** - * 동기화 설정 초기화 + * 동기화 기능이 현재 활성화되어 있는지 확인 + * @returns 활성화 여부 + */ +export const isSyncEnabled = (): boolean => { + return localStorage.getItem('syncEnabled') === 'true'; +}; + +/** + * 동기화 설정 초기화 */ export const initSyncSettings = (): void => { - try { - if (localStorage.getItem('syncEnabled') === null) { - localStorage.setItem('syncEnabled', 'false'); - } - - // Supabase 연결 테스트 - (async () => { - try { - const { data, error } = await supabase.auth.getUser(); - if (error) { - console.warn('Supabase 연결 테스트 실패:', error.message); - } else { - console.log('Supabase 연결 테스트 성공:', data.user ? '사용자 로그인됨' : '사용자 로그인 필요'); - } - } catch (testError) { - console.error('Supabase 연결 테스트 중 예외 발생:', testError); - } - })(); - } catch (error) { - console.error('동기화 설정 초기화 중 오류:', error); + // 동기화 기능이 설정되지 않은 경우 기본값으로 설정 + if (localStorage.getItem('syncEnabled') === null) { + localStorage.setItem('syncEnabled', 'false'); } }; diff --git a/src/utils/syncUtils.ts b/src/utils/syncUtils.ts index e39ccc1..5e11b59 100644 --- a/src/utils/syncUtils.ts +++ b/src/utils/syncUtils.ts @@ -1,22 +1,16 @@ -import { downloadTransactions, uploadTransactions } from './sync/transaction'; -import { downloadBudgets, uploadBudgets } from './sync/budget'; -import { isSyncEnabled, getLastSyncTime, setLastSyncTime } from './sync/syncSettings'; -import { RetryOptions } from './network/types'; -import { withRetry } from './network/retry'; -import { - getNetworkStatus, - setNetworkStatus, - startNetworkMonitoring, - stopNetworkMonitoring, - onNetworkStatusChange, - NetworkStatus, - addToSyncQueue, - processPendingSyncQueue -} from './networkUtils'; -// Export all utility functions to maintain the same public API +import { isSyncEnabled, getNetworkStatus, setNetworkStatus, startNetworkMonitoring, + stopNetworkMonitoring, onNetworkStatusChange, addToSyncQueue, + processPendingSyncQueue, getLastSyncTime, setLastSyncTime } from './sync/syncSettings'; +import { trySyncAllData } from './sync/data'; +import { SyncResult } from './sync/data'; +import { clearCloudData } from './sync/clearCloudData'; +import { setSyncEnabled } from './sync/config'; + +// 모든 유틸리티 함수를 동일한 공개 API로 유지하기 위해 내보내기 export { isSyncEnabled, + setSyncEnabled, getNetworkStatus, setNetworkStatus, startNetworkMonitoring, @@ -25,258 +19,8 @@ export { addToSyncQueue, processPendingSyncQueue, getLastSyncTime, - setLastSyncTime -}; - -/** - * 동기화 상태 인터페이스 - */ -export interface SyncState { - status: 'idle' | 'syncing' | 'success' | 'error' | 'partial'; - message?: string; - lastSyncTime?: string; -} - -// 현재 동기화 상태 -let syncState: SyncState = { - status: 'idle', - message: undefined, - lastSyncTime: getLastSyncTime() -}; - -/** - * 동기화 상태 초기화 - */ -export const initSyncState = async (): Promise => { - syncState = { - status: 'idle', - message: undefined, - lastSyncTime: getLastSyncTime() - }; - - // 네트워크 모니터링 시작 - startNetworkMonitoring(); - - // 네트워크 상태 변경 리스너 등록 - onNetworkStatusChange((status) => { - syncState.status = status === 'online' ? 'idle' : 'error'; - // 상태 변경 이벤트 발생 - window.dispatchEvent(new CustomEvent('syncStateChange', { detail: { ...syncState } })); - - // 온라인 상태로 변경되면 보류 중인 동기화 작업 처리 - if (status === 'online') { - processPendingSyncQueue(); - console.log('[네트워크 상태 변경] 온라인 상태로 변경되었습니다.'); - } else { - console.log('[네트워크 상태 변경] 오프라인 상태로 변경되었습니다.'); - } - }); - - console.log('[동기화] 상태 초기화 완료', syncState); -}; - -/** - * 현재 동기화 상태 가져오기 - */ -export const getSyncState = (): SyncState => { - return { ...syncState }; -}; - -/** - * 동기화 상태 업데이트 - */ -const updateSyncState = (updates: Partial): void => { - syncState = { ...syncState, ...updates }; - // 상태 변경 이벤트 발생 - window.dispatchEvent(new CustomEvent('syncStateChange', { detail: { ...syncState } })); -}; - -/** - * 동기화 상태 변경 이벤트 리스너 등록 - */ -export const onSyncStateChange = (callback: (state: SyncState) => void): () => void => { - const handler = (event: Event) => { - const customEvent = event as CustomEvent; - callback(customEvent.detail); - }; - - window.addEventListener('syncStateChange', handler); - - // 구독 해제 함수 반환 - return () => { - window.removeEventListener('syncStateChange', handler); - }; -}; - -/** - * 데이터 동기화를 위한 재시도 로직이 포함된 유틸리티 함수 - * @param fn 실행할 함수 - * @param options 재시도 옵션 - */ -// export const withRetry = async ( -// fn: () => Promise, -// options: RetryOptions -// ): Promise => { -// const { entityType, maxRetries = 3, retryDelay = 1500 } = options; -// let lastError: Error | unknown = null; - -// for (let attempt = 1; attempt <= maxRetries; attempt++) { -// try { -// if (attempt > 1) { -// console.log(`[동기화] ${entityType} 재시도 중... (${attempt}/${maxRetries})`); -// } - -// const result = await fn(); -// if (attempt > 1) { -// console.log(`[동기화] ${entityType} 재시도 성공! (${attempt}/${maxRetries})`); -// } -// return result; -// } catch (error) { -// lastError = error; -// console.error(`[동기화] ${entityType} 실패 (시도 ${attempt}/${maxRetries}):`, error); - -// // 자세한 오류 정보 로깅 -// try { -// console.error(`[동기화] 오류 상세 정보:`, JSON.stringify(error, null, 2)); -// } catch (jsonError) { -// console.error(`[동기화] 오류 객체를 JSON으로 변환할 수 없음:`, error); -// } - -// if (attempt < maxRetries) { -// console.log(`[동기화] ${retryDelay}ms 후 재시도...`); -// await new Promise(resolve => setTimeout(resolve, retryDelay)); -// } -// } -// } - -// console.error(`[동기화] ${entityType} 최대 재시도 횟수(${maxRetries}) 초과, 실패`); -// throw lastError; -// }; - -/** - * 모든 데이터를 동기화하는 함수 - * 다운로드 및 업로드 작업을 순차적으로 수행 - */ -export const trySyncAllData = async ( - userId: string, - setSyncState: (state: SyncState) => void -): Promise => { - if (!userId) { - console.error('[동기화] 사용자 ID가 없어 동기화를 진행할 수 없습니다.'); - return; - } - - try { - setSyncState({ status: 'syncing', message: '동기화 중...' }); - - // 네트워크 연결 확인 - if (!navigator.onLine) { - setSyncState({ status: 'error', message: '네트워크 연결이 없습니다.' }); - return; - } - - console.log('[동기화] 데이터 동기화 시작'); - const startTime = Date.now(); - - // 1. 다운로드 작업 (서버 → 로컬) - try { - console.log('[동기화] 다운로드 작업 시작'); - - // 트랜잭션 다운로드 - await withRetry( - () => downloadTransactions(userId), - { entityType: '트랜잭션 다운로드', maxRetries: 3, retryDelay: 1500 } - ); - - // 예산 다운로드 - await withRetry( - () => downloadBudgets(userId), - { entityType: '예산 다운로드', maxRetries: 3, retryDelay: 1500 } - ); - - console.log('[동기화] 다운로드 작업 완료'); - } catch (downloadError) { - console.error('[동기화] 다운로드 작업 실패:', downloadError); - setSyncState({ - status: 'error', - message: `다운로드 중 오류가 발생했습니다: ${downloadError instanceof Error ? downloadError.message : '알 수 없는 오류'}` - }); - return; // 다운로드 실패 시 업로드 작업 진행하지 않음 - } - - // 2. 업로드 작업 (로컬 → 서버) - try { - console.log('[동기화] 업로드 작업 시작'); - - // 트랜잭션 업로드 - await withRetry( - () => uploadTransactions(userId), - { entityType: '트랜잭션 업로드', maxRetries: 3, retryDelay: 1500 } - ); - - // 예산 업로드 - await withRetry( - () => uploadBudgets(userId), - { entityType: '예산 업로드', maxRetries: 3, retryDelay: 1500 } - ); - - console.log('[동기화] 업로드 작업 완료'); - } catch (uploadError) { - console.error('[동기화] 업로드 작업 실패:', uploadError); - // 업로드 실패 시에도 일부 데이터는 동기화되었을 수 있으므로 부분 성공으로 처리 - setSyncState({ - status: 'partial', - message: `일부 데이터 동기화 중 오류가 발생했습니다: ${uploadError instanceof Error ? uploadError.message : '알 수 없는 오류'}` - }); - return; - } - - // 동기화 완료 시간 기록 - const endTime = Date.now(); - const syncDuration = (endTime - startTime) / 1000; // 초 단위 - - console.log(`[동기화] 모든 데이터 동기화 완료 (${syncDuration.toFixed(2)}초 소요)`); - setSyncState({ - status: 'success', - message: `동기화 완료 (${syncDuration.toFixed(1)}초)` - }); - - // 동기화 완료 시간 저장 - setLastSyncTime(new Date().toISOString()); - - } catch (error) { - console.error('[동기화] 동기화 중 예상치 못한 오류:', error); - - // 자세한 오류 정보 로깅 - try { - console.error('[동기화] 오류 상세 정보:', JSON.stringify(error, null, 2)); - } catch (jsonError) { - console.error('[동기화] 오류 객체를 JSON으로 변환할 수 없음:', error); - } - - setSyncState({ - status: 'error', - message: `동기화 중 오류가 발생했습니다: ${error instanceof Error ? error.message : '알 수 없는 오류'}` - }); - } -}; - -/** - * 오프라인 모드에서 작업 큐에 추가하는 함수 - */ -export const queueSyncOperation = ( - type: 'upload' | 'download' | 'delete', - entityType: 'transaction' | 'budget' | 'categoryBudget', - data: Record -): void => { - addToSyncQueue({ type, entityType, data }); - console.log(`[동기화] 작업 큐에 추가: ${type} ${entityType}`); -}; - -/** - * 동기화 시스템 종료 - */ -export const shutdownSyncSystem = (): void => { - stopNetworkMonitoring(); - console.log('[동기화] 시스템 종료'); + setLastSyncTime, + trySyncAllData, + SyncResult, + clearCloudData };