From b96e0877b76ba76e225004d8d052f21a43dfd95b Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Tue, 18 Mar 2025 01:24:31 +0000 Subject: [PATCH] Refactor useSyncSettings hook Refactors the useSyncSettings hook into smaller, more manageable modules to improve code organization and maintainability. The functionality of the hook remains unchanged. --- src/hooks/sync/syncResultHandler.ts | 41 ++++++ src/hooks/sync/useManualSync.ts | 52 ++++++++ src/hooks/sync/useSyncStatus.ts | 31 +++++ src/hooks/sync/useSyncToggle.ts | 116 ++++++++++++++++ src/hooks/useSyncSettings.ts | 200 +--------------------------- 5 files changed, 246 insertions(+), 194 deletions(-) create mode 100644 src/hooks/sync/syncResultHandler.ts create mode 100644 src/hooks/sync/useManualSync.ts create mode 100644 src/hooks/sync/useSyncStatus.ts create mode 100644 src/hooks/sync/useSyncToggle.ts diff --git a/src/hooks/sync/syncResultHandler.ts b/src/hooks/sync/syncResultHandler.ts new file mode 100644 index 0000000..df54c8d --- /dev/null +++ b/src/hooks/sync/syncResultHandler.ts @@ -0,0 +1,41 @@ + +import { toast } from '@/hooks/useToast.wrapper'; +import { SyncResult } from '@/utils/syncUtils'; + +/** + * 동기화 결과 처리 함수 + */ +export const handleSyncResult = (result: SyncResult) => { + if (result.success) { + if (result.downloadSuccess && result.uploadSuccess) { + toast({ + title: "동기화 완료", + description: "모든 데이터가 클라우드에 동기화되었습니다.", + }); + } else if (result.downloadSuccess) { + toast({ + title: "다운로드만 성공", + description: "서버 데이터를 가져왔지만, 업로드에 실패했습니다.", + variant: "destructive" + }); + } else if (result.uploadSuccess) { + toast({ + title: "업로드만 성공", + description: "로컬 데이터를 업로드했지만, 다운로드에 실패했습니다.", + variant: "destructive" + }); + } else if (result.partial) { + toast({ + title: "동기화 일부 완료", + description: "일부 데이터만 동기화되었습니다. 다시 시도해보세요.", + variant: "destructive" + }); + } + } else { + toast({ + title: "일부 동기화 실패", + description: "일부 데이터 동기화 중 문제가 발생했습니다. 다시 시도해주세요.", + variant: "destructive" + }); + } +}; diff --git a/src/hooks/sync/useManualSync.ts b/src/hooks/sync/useManualSync.ts new file mode 100644 index 0000000..e6bc37c --- /dev/null +++ b/src/hooks/sync/useManualSync.ts @@ -0,0 +1,52 @@ + +import { useState } from 'react'; +import { toast } from '@/hooks/useToast.wrapper'; +import { trySyncAllData, SyncResult } from '@/utils/syncUtils'; +import { getLastSyncTime, setLastSyncTime } from '@/utils/syncUtils'; +import { handleSyncResult } from './syncResultHandler'; + +/** + * 수동 동기화 기능을 위한 커스텀 훅 + */ +export const useManualSync = (user: any) => { + const [syncing, setSyncing] = useState(false); + + // 수동 동기화 핸들러 + const handleManualSync = async () => { + if (!user) { + toast({ + title: "로그인 필요", + description: "데이터 동기화를 위해 로그인이 필요합니다.", + variant: "destructive" + }); + return; + } + + await performSync(user.id); + }; + + // 실제 동기화 수행 함수 + const performSync = async (userId: string) => { + if (!userId) return; + + try { + setSyncing(true); + // 안전한 동기화 함수 사용 + const result = await trySyncAllData(userId); + + handleSyncResult(result); + setLastSyncTime(getLastSyncTime()); + } catch (error) { + console.error('동기화 오류:', error); + toast({ + title: "동기화 오류", + description: "동기화 중 문제가 발생했습니다. 다시 시도해주세요.", + variant: "destructive" + }); + } finally { + setSyncing(false); + } + }; + + return { syncing, handleManualSync }; +}; diff --git a/src/hooks/sync/useSyncStatus.ts b/src/hooks/sync/useSyncStatus.ts new file mode 100644 index 0000000..3360f50 --- /dev/null +++ b/src/hooks/sync/useSyncStatus.ts @@ -0,0 +1,31 @@ + +import { useState, useEffect } from 'react'; +import { getLastSyncTime } from '@/utils/syncUtils'; + +/** + * 동기화 상태와 마지막 동기화 시간을 관리하는 커스텀 훅 + */ +export const useSyncStatus = () => { + const [lastSync, setLastSync] = useState(getLastSyncTime()); + + // 마지막 동기화 시간 정기적으로 업데이트 + useEffect(() => { + const intervalId = setInterval(() => { + setLastSync(getLastSyncTime()); + }, 10000); // 10초마다 업데이트 + + return () => clearInterval(intervalId); + }, []); + + // 마지막 동기화 시간 포맷팅 + const formatLastSyncTime = () => { + if (!lastSync) return "아직 동기화된 적 없음"; + + if (lastSync === '부분-다운로드') return "부분 동기화 (다운로드만)"; + + const date = new Date(lastSync); + return `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`; + }; + + return { lastSync, formatLastSyncTime }; +}; diff --git a/src/hooks/sync/useSyncToggle.ts b/src/hooks/sync/useSyncToggle.ts new file mode 100644 index 0000000..d75a81b --- /dev/null +++ b/src/hooks/sync/useSyncToggle.ts @@ -0,0 +1,116 @@ + +import { useState, useEffect } from 'react'; +import { useAuth } from '@/contexts/auth'; +import { toast } from '@/hooks/useToast.wrapper'; +import { + isSyncEnabled, + setSyncEnabled +} from '@/utils/syncUtils'; +import { trySyncAllData } from '@/utils/syncUtils'; + +/** + * 동기화 토글 기능을 위한 커스텀 훅 + */ +export const useSyncToggle = () => { + const [enabled, setEnabled] = useState(isSyncEnabled()); + const { user } = useAuth(); + + // 사용자 로그인 상태 변경 감지 + useEffect(() => { + // 사용자 로그인 상태에 따라 동기화 설정 업데이트 + const updateSyncState = () => { + if (!user && isSyncEnabled()) { + // 사용자가 로그아웃했고 동기화가 활성화되어 있으면 비활성화 + setSyncEnabled(false); + setEnabled(false); + console.log('로그아웃으로 인해 동기화 설정이 비활성화되었습니다.'); + } + + // 동기화 상태 업데이트 + setEnabled(isSyncEnabled()); + }; + + // 초기 호출 + updateSyncState(); + + // 인증 상태 변경 이벤트 리스너 + window.addEventListener('auth-state-changed', updateSyncState); + + return () => { + window.removeEventListener('auth-state-changed', updateSyncState); + }; + }, [user]); + + // 동기화 토글 핸들러 + const handleSyncToggle = async (checked: boolean) => { + if (!user && checked) { + toast({ + title: "로그인 필요", + description: "데이터 동기화를 위해 로그인이 필요합니다.", + variant: "destructive" + }); + return; + } + + // 현재 로컬 데이터 백업 + const budgetDataBackup = localStorage.getItem('budgetData'); + const categoryBudgetsBackup = localStorage.getItem('categoryBudgets'); + const transactionsBackup = localStorage.getItem('transactions'); + + console.log('동기화 설정 변경 전 로컬 데이터 백업:', { + budgetData: budgetDataBackup ? '있음' : '없음', + categoryBudgets: categoryBudgetsBackup ? '있음' : '없음', + transactions: transactionsBackup ? '있음' : '없음' + }); + + setEnabled(checked); + setSyncEnabled(checked); + + if (checked && user) { + try { + // 동기화 활성화 시 즉시 동기화 실행 + await performSync(user.id); + } catch (error) { + console.error('동기화 중 오류, 로컬 데이터 복원 시도:', error); + + // 오류 발생 시 백업 데이터 복원 + if (budgetDataBackup) { + localStorage.setItem('budgetData', budgetDataBackup); + } + if (categoryBudgetsBackup) { + localStorage.setItem('categoryBudgets', categoryBudgetsBackup); + } + if (transactionsBackup) { + localStorage.setItem('transactions', transactionsBackup); + } + + // 이벤트 발생시켜 UI 업데이트 + window.dispatchEvent(new Event('budgetDataUpdated')); + window.dispatchEvent(new Event('categoryBudgetsUpdated')); + window.dispatchEvent(new Event('transactionUpdated')); + + toast({ + title: "동기화 오류", + description: "동기화 중 문제가 발생하여 로컬 데이터가 복원되었습니다.", + variant: "destructive" + }); + } + } + }; + + return { enabled, setEnabled, handleSyncToggle }; +}; + +// 실제 동기화 수행 함수 +const performSync = async (userId: string) => { + if (!userId) return; + + try { + // 안전한 동기화 함수 사용 + const result = await trySyncAllData(userId); + return result; + } catch (error) { + console.error('동기화 오류:', error); + throw error; + } +}; diff --git a/src/hooks/useSyncSettings.ts b/src/hooks/useSyncSettings.ts index 2554f09..34ed383 100644 --- a/src/hooks/useSyncSettings.ts +++ b/src/hooks/useSyncSettings.ts @@ -1,206 +1,18 @@ import { useState, useEffect } from 'react'; import { useAuth } from '@/contexts/auth'; -import { toast } from '@/hooks/useToast.wrapper'; -import { - isSyncEnabled, - setSyncEnabled, - getLastSyncTime, - trySyncAllData, - SyncResult -} from '@/utils/syncUtils'; +import { useSyncToggle } from './sync/useSyncToggle'; +import { useManualSync } from './sync/useManualSync'; +import { useSyncStatus } from './sync/useSyncStatus'; /** * 동기화 설정 관리를 위한 커스텀 훅 */ export const useSyncSettings = () => { - const [enabled, setEnabled] = useState(isSyncEnabled()); - const [syncing, setSyncing] = useState(false); - const [lastSync, setLastSync] = useState(getLastSyncTime()); const { user } = useAuth(); - - // 사용자 로그인 상태 변경 감지 - useEffect(() => { - // 사용자 로그인 상태에 따라 동기화 설정 업데이트 - const updateSyncState = () => { - if (!user && isSyncEnabled()) { - // 사용자가 로그아웃했고 동기화가 활성화되어 있으면 비활성화 - setSyncEnabled(false); - setEnabled(false); - setLastSync(null); - console.log('로그아웃으로 인해 동기화 설정이 비활성화되었습니다.'); - } - - // 동기화 상태 업데이트 - setEnabled(isSyncEnabled()); - setLastSync(getLastSyncTime()); - }; - - // 초기 호출 - updateSyncState(); - - // 인증 상태 변경 이벤트 리스너 - window.addEventListener('auth-state-changed', updateSyncState); - - return () => { - window.removeEventListener('auth-state-changed', updateSyncState); - }; - }, [user]); - - // 마지막 동기화 시간 정기적으로 업데이트 - useEffect(() => { - const intervalId = setInterval(() => { - setLastSync(getLastSyncTime()); - }, 10000); // 10초마다 업데이트 - - return () => clearInterval(intervalId); - }, []); - - // 동기화 토글 핸들러 - const handleSyncToggle = async (checked: boolean) => { - if (!user && checked) { - toast({ - title: "로그인 필요", - description: "데이터 동기화를 위해 로그인이 필요합니다.", - variant: "destructive" - }); - return; - } - - // 현재 로컬 데이터 백업 - const budgetDataBackup = localStorage.getItem('budgetData'); - const categoryBudgetsBackup = localStorage.getItem('categoryBudgets'); - const transactionsBackup = localStorage.getItem('transactions'); - - console.log('동기화 설정 변경 전 로컬 데이터 백업:', { - budgetData: budgetDataBackup ? '있음' : '없음', - categoryBudgets: categoryBudgetsBackup ? '있음' : '없음', - transactions: transactionsBackup ? '있음' : '없음' - }); - - setEnabled(checked); - setSyncEnabled(checked); - - if (checked && user) { - try { - // 동기화 활성화 시 즉시 동기화 실행 - await performSync(); - } catch (error) { - console.error('동기화 중 오류, 로컬 데이터 복원 시도:', error); - - // 오류 발생 시 백업 데이터 복원 - if (budgetDataBackup) { - localStorage.setItem('budgetData', budgetDataBackup); - } - if (categoryBudgetsBackup) { - localStorage.setItem('categoryBudgets', categoryBudgetsBackup); - } - if (transactionsBackup) { - localStorage.setItem('transactions', transactionsBackup); - } - - // 이벤트 발생시켜 UI 업데이트 - window.dispatchEvent(new Event('budgetDataUpdated')); - window.dispatchEvent(new Event('categoryBudgetsUpdated')); - window.dispatchEvent(new Event('transactionUpdated')); - - toast({ - title: "동기화 오류", - description: "동기화 중 문제가 발생하여 로컬 데이터가 복원되었습니다.", - variant: "destructive" - }); - } - } - }; - - // 수동 동기화 핸들러 - const handleManualSync = async () => { - if (!user) { - toast({ - title: "로그인 필요", - description: "데이터 동기화를 위해 로그인이 필요합니다.", - variant: "destructive" - }); - return; - } - - await performSync(); - }; - - // 실제 동기화 수행 함수 - const performSync = async () => { - if (!user) return; - - try { - setSyncing(true); - // 안전한 동기화 함수 사용 - const result = await trySyncAllData(user.id); - - handleSyncResult(result); - setLastSync(getLastSyncTime()); - } catch (error) { - console.error('동기화 오류:', error); - toast({ - title: "동기화 오류", - description: "동기화 중 문제가 발생했습니다. 다시 시도해주세요.", - variant: "destructive" - }); - - // 심각한 오류 발생 시 동기화 비활성화 - if (!enabled) { - setEnabled(false); - setSyncEnabled(false); - } - } finally { - setSyncing(false); - } - }; - - // 동기화 결과 처리 함수 - const handleSyncResult = (result: SyncResult) => { - if (result.success) { - if (result.downloadSuccess && result.uploadSuccess) { - toast({ - title: "동기화 완료", - description: "모든 데이터가 클라우드에 동기화되었습니다.", - }); - } else if (result.downloadSuccess) { - toast({ - title: "다운로드만 성공", - description: "서버 데이터를 가져왔지만, 업로드에 실패했습니다.", - variant: "destructive" - }); - } else if (result.uploadSuccess) { - toast({ - title: "업로드만 성공", - description: "로컬 데이터를 업로드했지만, 다운로드에 실패했습니다.", - variant: "destructive" - }); - } else if (result.partial) { - toast({ - title: "동기화 일부 완료", - description: "일부 데이터만 동기화되었습니다. 다시 시도해보세요.", - variant: "destructive" - }); - } - } else { - toast({ - title: "일부 동기화 실패", - description: "일부 데이터 동기화 중 문제가 발생했습니다. 다시 시도해주세요.", - variant: "destructive" - }); - } - }; - - // 마지막 동기화 시간 포맷팅 - const formatLastSyncTime = () => { - if (!lastSync) return "아직 동기화된 적 없음"; - - if (lastSync === '부분-다운로드') return "부분 동기화 (다운로드만)"; - - const date = new Date(lastSync); - return `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`; - }; + const { enabled, setEnabled, handleSyncToggle } = useSyncToggle(); + const { syncing, handleManualSync } = useManualSync(user); + const { lastSync, formatLastSyncTime } = useSyncStatus(); return { enabled,