diff --git a/src/components/home/IndexContent.tsx b/src/components/home/IndexContent.tsx
new file mode 100644
index 0000000..282fd08
--- /dev/null
+++ b/src/components/home/IndexContent.tsx
@@ -0,0 +1,58 @@
+
+import React from 'react';
+import Header from '@/components/Header';
+import HomeContent from '@/components/home/HomeContent';
+import { useBudget } from '@/contexts/budget/BudgetContext';
+import { BudgetData } from '@/contexts/budget/types';
+
+// 기본 예산 데이터 (빈 객체 대신 사용할 더미 데이터)
+const defaultBudgetData: BudgetData = {
+ daily: {
+ targetAmount: 0,
+ spentAmount: 0,
+ remainingAmount: 0
+ },
+ weekly: {
+ targetAmount: 0,
+ spentAmount: 0,
+ remainingAmount: 0
+ },
+ monthly: {
+ targetAmount: 0,
+ spentAmount: 0,
+ remainingAmount: 0
+ }
+};
+
+/**
+ * 인덱스 페이지의 주요 내용을 담당하는 컴포넌트
+ */
+const IndexContent: React.FC = () => {
+ const {
+ transactions,
+ budgetData,
+ selectedTab,
+ setSelectedTab,
+ handleBudgetGoalUpdate,
+ updateTransaction,
+ getCategorySpending
+ } = useBudget();
+
+ return (
+
+
+
+
+
+ );
+};
+
+export default IndexContent;
diff --git a/src/contexts/budget/budgetUtils.ts b/src/contexts/budget/budgetUtils.ts
index 07cb20f..3259846 100644
--- a/src/contexts/budget/budgetUtils.ts
+++ b/src/contexts/budget/budgetUtils.ts
@@ -21,6 +21,31 @@ export const getInitialBudgetData = (): BudgetData => ({
}
});
+// 기본 카테고리 예산 값 (수출)
+export const DEFAULT_CATEGORY_BUDGETS = {};
+
+// 안전한 스토리지 처리 (수출)
+export const safeStorage = {
+ setItem: (key: string, value: any) => {
+ try {
+ localStorage.setItem(key, JSON.stringify(value));
+ return true;
+ } catch (err) {
+ console.error(`스토리지 저장 오류 (${key}):`, err);
+ return false;
+ }
+ },
+ getItem: (key: string, defaultValue: any = null) => {
+ try {
+ const item = localStorage.getItem(key);
+ return item ? JSON.parse(item) : defaultValue;
+ } catch (err) {
+ console.error(`스토리지 로드 오류 (${key}):`, err);
+ return defaultValue;
+ }
+ }
+};
+
// 예산 데이터 스토리지에서 로드
export const safelyLoadBudgetData = (): BudgetData => {
try {
diff --git a/src/hooks/useAppFocusEvents.tsx b/src/hooks/useAppFocusEvents.tsx
new file mode 100644
index 0000000..4621509
--- /dev/null
+++ b/src/hooks/useAppFocusEvents.tsx
@@ -0,0 +1,76 @@
+
+import { useEffect } from 'react';
+
+/**
+ * 앱이 포커스를 얻었을 때나 가시성이 변경될 때 데이터를 새로고침하는 커스텀 훅
+ */
+export const useAppFocusEvents = () => {
+ useEffect(() => {
+ const handleFocus = () => {
+ try {
+ console.log('창이 포커스를 얻음 - 데이터 새로고침');
+ // 이미 리프레시 중인지 확인하는 플래그
+ if (sessionStorage.getItem('isRefreshing') === 'true') {
+ console.log('이미 리프레시 진행 중, 중복 실행 방지');
+ return;
+ }
+
+ try {
+ sessionStorage.setItem('isRefreshing', 'true');
+
+ // 이벤트 발생시켜 데이터 새로고침
+ window.dispatchEvent(new Event('storage'));
+ window.dispatchEvent(new Event('transactionUpdated'));
+ window.dispatchEvent(new Event('budgetDataUpdated'));
+ window.dispatchEvent(new Event('categoryBudgetsUpdated'));
+
+ // 리프레시 완료 표시 (300ms 후에 플래그 해제)
+ setTimeout(() => {
+ sessionStorage.setItem('isRefreshing', 'false');
+ }, 300);
+ } catch (e) {
+ console.error('이벤트 발생 오류:', e);
+ sessionStorage.setItem('isRefreshing', 'false');
+ }
+ } catch (error) {
+ console.error('포커스 이벤트 처리 중 오류:', error);
+ }
+ };
+
+ // 포커스 이벤트
+ window.addEventListener('focus', handleFocus);
+
+ // 가시성 변경 이벤트 (백그라운드에서 전경으로 돌아올 때)
+ const handleVisibilityChange = () => {
+ try {
+ if (document.visibilityState === 'visible') {
+ console.log('페이지가 다시 보임 - 데이터 새로고침');
+ handleFocus();
+ }
+ } catch (error) {
+ console.error('가시성 이벤트 처리 중 오류:', error);
+ }
+ };
+
+ document.addEventListener('visibilitychange', handleVisibilityChange);
+
+ // 정기적인 데이터 새로고침 (60초마다로 변경 - 너무 빈번한 리프레시 방지)
+ const refreshInterval = setInterval(() => {
+ try {
+ if (document.visibilityState === 'visible' &&
+ sessionStorage.getItem('isRefreshing') !== 'true') {
+ console.log('정기 새로고침 - 데이터 업데이트');
+ handleFocus();
+ }
+ } catch (error) {
+ console.error('정기 새로고침 처리 중 오류:', error);
+ }
+ }, 60000); // 60초마다
+
+ return () => {
+ window.removeEventListener('focus', handleFocus);
+ document.removeEventListener('visibilitychange', handleVisibilityChange);
+ clearInterval(refreshInterval);
+ };
+ }, []);
+};
diff --git a/src/hooks/useInitialDataLoading.tsx b/src/hooks/useInitialDataLoading.tsx
new file mode 100644
index 0000000..ab54d05
--- /dev/null
+++ b/src/hooks/useInitialDataLoading.tsx
@@ -0,0 +1,57 @@
+
+import { useEffect } from 'react';
+
+/**
+ * 앱 첫 실행 시 로컬스토리지 데이터를 로드하는 커스텀 훅
+ */
+export const useInitialDataLoading = () => {
+ useEffect(() => {
+ try {
+ console.log('Index 페이지 마운트, 데이터 확인 중...');
+
+ // 페이지 첫 마운트 시에만 실행되는 로직
+ const isFirstMount = sessionStorage.getItem('initialDataLoaded') !== 'true';
+
+ if (isFirstMount) {
+ try {
+ // 백업된 데이터 복구 확인 (메인 데이터가 없는 경우만)
+ if (!localStorage.getItem('budgetData')) {
+ const budgetBackup = localStorage.getItem('budgetData_backup');
+ if (budgetBackup) {
+ console.log('예산 데이터 백업에서 복구');
+ localStorage.setItem('budgetData', budgetBackup);
+ }
+ }
+
+ if (!localStorage.getItem('categoryBudgets')) {
+ const categoryBackup = localStorage.getItem('categoryBudgets_backup');
+ if (categoryBackup) {
+ console.log('카테고리 예산 백업에서 복구');
+ localStorage.setItem('categoryBudgets', categoryBackup);
+ }
+ }
+
+ if (!localStorage.getItem('transactions')) {
+ const transactionBackup = localStorage.getItem('transactions_backup');
+ if (transactionBackup) {
+ console.log('트랜잭션 백업에서 복구');
+ localStorage.setItem('transactions', transactionBackup);
+ }
+ }
+
+ // 한 번만 이벤트 발생
+ window.dispatchEvent(new Event('transactionUpdated'));
+ window.dispatchEvent(new Event('budgetDataUpdated'));
+ window.dispatchEvent(new Event('categoryBudgetsUpdated'));
+
+ // 초기 로드 완료 표시
+ sessionStorage.setItem('initialDataLoaded', 'true');
+ } catch (error) {
+ console.error('백업 복구 시도 중 오류:', error);
+ }
+ }
+ } catch (error) {
+ console.error('Index 페이지 초기화 중 오류:', error);
+ }
+ }, []); // 컴포넌트 마운트 시 한 번만 실행
+};
diff --git a/src/hooks/useWelcomeNotification.tsx b/src/hooks/useWelcomeNotification.tsx
new file mode 100644
index 0000000..f3d0fe6
--- /dev/null
+++ b/src/hooks/useWelcomeNotification.tsx
@@ -0,0 +1,35 @@
+
+import { useEffect } from 'react';
+import { useAuth } from '@/contexts/auth';
+import useNotifications from '@/hooks/useNotifications';
+
+/**
+ * 앱 초기화 후 환영 메시지 알림을 표시하는 커스텀 훅
+ */
+export const useWelcomeNotification = (isInitialized: boolean) => {
+ const { user } = useAuth();
+ const { addNotification } = useNotifications();
+
+ useEffect(() => {
+ try {
+ // 환영 메시지가 이미 표시되었는지 확인하는 키
+ const welcomeNotificationSent = sessionStorage.getItem('welcomeNotificationSent');
+
+ if (isInitialized && user && !welcomeNotificationSent) {
+ // 사용자 로그인 시 알림 예시 (한 번만 실행)
+ const timeoutId = setTimeout(() => {
+ addNotification(
+ '환영합니다!',
+ '젤리의 적자탈출에 오신 것을 환영합니다. 예산을 설정하고 지출을 기록해보세요.'
+ );
+ // 세션 스토리지에 환영 메시지 표시 여부 저장
+ sessionStorage.setItem('welcomeNotificationSent', 'true');
+ }, 2000);
+
+ return () => clearTimeout(timeoutId);
+ }
+ } catch (error) {
+ console.error('환영 메시지 알림 표시 중 오류:', error);
+ }
+ }, [isInitialized, user, addNotification]);
+};
diff --git a/src/pages/Index.tsx b/src/pages/Index.tsx
index 9d36034..eabb2fe 100644
--- a/src/pages/Index.tsx
+++ b/src/pages/Index.tsx
@@ -2,55 +2,28 @@
import React, { useEffect } from 'react';
import NavBar from '@/components/NavBar';
import AddTransactionButton from '@/components/AddTransactionButton';
-import Header from '@/components/Header';
import WelcomeDialog from '@/components/onboarding/WelcomeDialog';
-import HomeContent from '@/components/home/HomeContent';
+import IndexContent from '@/components/home/IndexContent';
import { useBudget } from '@/contexts/budget/BudgetContext';
-import { useAuth } from '@/contexts/auth';
import { useWelcomeDialog } from '@/hooks/useWelcomeDialog';
import { useDataInitialization } from '@/hooks/useDataInitialization';
-import { useIsMobile } from '@/hooks/use-mobile';
-import useNotifications from '@/hooks/useNotifications';
import SafeAreaContainer from '@/components/SafeAreaContainer';
-import { BudgetData } from '@/contexts/budget/types';
+import { useInitialDataLoading } from '@/hooks/useInitialDataLoading';
+import { useAppFocusEvents } from '@/hooks/useAppFocusEvents';
+import { useWelcomeNotification } from '@/hooks/useWelcomeNotification';
-// 기본 예산 데이터 (빈 객체 대신 사용할 더미 데이터)
-const defaultBudgetData: BudgetData = {
- daily: {
- targetAmount: 0,
- spentAmount: 0,
- remainingAmount: 0
- },
- weekly: {
- targetAmount: 0,
- spentAmount: 0,
- remainingAmount: 0
- },
- monthly: {
- targetAmount: 0,
- spentAmount: 0,
- remainingAmount: 0
- }
-};
-
-// 메인 컴포넌트
+/**
+ * 애플리케이션의 메인 인덱스 페이지 컴포넌트
+ */
const Index = () => {
- const {
- transactions,
- budgetData,
- selectedTab,
- setSelectedTab,
- handleBudgetGoalUpdate,
- updateTransaction,
- getCategorySpending,
- resetBudgetData
- } = useBudget();
-
- const { user } = useAuth();
+ const { resetBudgetData } = useBudget();
const { showWelcome, checkWelcomeDialogState, handleCloseWelcome } = useWelcomeDialog();
const { isInitialized } = useDataInitialization(resetBudgetData);
- const isMobile = useIsMobile();
- const { addNotification } = useNotifications();
+
+ // 커스텀 훅 사용으로 코드 분리
+ useInitialDataLoading();
+ useAppFocusEvents();
+ useWelcomeNotification(isInitialized);
// 초기화 후 환영 메시지 표시 상태 확인
useEffect(() => {
@@ -60,168 +33,9 @@ const Index = () => {
}
}, [isInitialized, checkWelcomeDialogState]);
- // 앱 시작시 예시 알림 추가 (실제 앱에서는 필요한 이벤트에 따라 알림 추가)
- useEffect(() => {
- try {
- // 환영 메시지가 이미 표시되었는지 확인하는 키
- const welcomeNotificationSent = sessionStorage.getItem('welcomeNotificationSent');
-
- if (isInitialized && user && !welcomeNotificationSent) {
- // 사용자 로그인 시 알림 예시 (한 번만 실행)
- const timeoutId = setTimeout(() => {
- addNotification(
- '환영합니다!',
- '젤리의 적자탈출에 오신 것을 환영합니다. 예산을 설정하고 지출을 기록해보세요.'
- );
- // 세션 스토리지에 환영 메시지 표시 여부 저장
- sessionStorage.setItem('welcomeNotificationSent', 'true');
- }, 2000);
-
- return () => clearTimeout(timeoutId);
- }
- } catch (error) {
- console.error('환영 메시지 알림 표시 중 오류:', error);
- }
- }, [isInitialized, user, addNotification]);
-
- // 페이지가 처음 로드될 때 데이터 로딩 확인 - 에러 방지를 위해 try/catch 추가
- useEffect(() => {
- try {
- console.log('Index 페이지 마운트, 현재 데이터 상태:');
- console.log('트랜잭션:', transactions?.length || 0);
- console.log('예산 데이터:', budgetData || defaultBudgetData);
-
- // 페이지 첫 마운트 시에만 실행되는 로직으로 수정
- const isFirstMount = sessionStorage.getItem('initialDataLoaded') !== 'true';
-
- if (isFirstMount) {
- try {
- // 백업된 데이터 복구 확인 (메인 데이터가 없는 경우만)
- if (!localStorage.getItem('budgetData')) {
- const budgetBackup = localStorage.getItem('budgetData_backup');
- if (budgetBackup) {
- console.log('예산 데이터 백업에서 복구');
- localStorage.setItem('budgetData', budgetBackup);
- }
- }
-
- if (!localStorage.getItem('categoryBudgets')) {
- const categoryBackup = localStorage.getItem('categoryBudgets_backup');
- if (categoryBackup) {
- console.log('카테고리 예산 백업에서 복구');
- localStorage.setItem('categoryBudgets', categoryBackup);
- }
- }
-
- if (!localStorage.getItem('transactions')) {
- const transactionBackup = localStorage.getItem('transactions_backup');
- if (transactionBackup) {
- console.log('트랜잭션 백업에서 복구');
- localStorage.setItem('transactions', transactionBackup);
- }
- }
-
- // 한 번만 이벤트 발생
- window.dispatchEvent(new Event('transactionUpdated'));
- window.dispatchEvent(new Event('budgetDataUpdated'));
- window.dispatchEvent(new Event('categoryBudgetsUpdated'));
-
- // 초기 로드 완료 표시
- sessionStorage.setItem('initialDataLoaded', 'true');
- } catch (error) {
- console.error('백업 복구 시도 중 오류:', error);
- }
- }
- } catch (error) {
- console.error('Index 페이지 초기화 중 오류:', error);
- }
- }, []); // 의존성 배열 비움 - 컴포넌트 마운트 시 한 번만 실행
-
- // 앱이 포커스를 얻었을 때 데이터를 새로고침
- useEffect(() => {
- const handleFocus = () => {
- try {
- console.log('창이 포커스를 얻음 - 데이터 새로고침');
- // 이미 리프레시 중인지 확인하는 플래그
- if (sessionStorage.getItem('isRefreshing') === 'true') {
- console.log('이미 리프레시 진행 중, 중복 실행 방지');
- return;
- }
-
- try {
- sessionStorage.setItem('isRefreshing', 'true');
-
- // 이벤트 발생시켜 데이터 새로고침
- window.dispatchEvent(new Event('storage'));
- window.dispatchEvent(new Event('transactionUpdated'));
- window.dispatchEvent(new Event('budgetDataUpdated'));
- window.dispatchEvent(new Event('categoryBudgetsUpdated'));
-
- // 리프레시 완료 표시 (300ms 후에 플래그 해제)
- setTimeout(() => {
- sessionStorage.setItem('isRefreshing', 'false');
- }, 300);
- } catch (e) {
- console.error('이벤트 발생 오류:', e);
- sessionStorage.setItem('isRefreshing', 'false');
- }
- } catch (error) {
- console.error('포커스 이벤트 처리 중 오류:', error);
- }
- };
-
- // 포커스 이벤트
- window.addEventListener('focus', handleFocus);
-
- // 가시성 변경 이벤트 (백그라운드에서 전경으로 돌아올 때)
- const handleVisibilityChange = () => {
- try {
- if (document.visibilityState === 'visible') {
- console.log('페이지가 다시 보임 - 데이터 새로고침');
- handleFocus();
- }
- } catch (error) {
- console.error('가시성 이벤트 처리 중 오류:', error);
- }
- };
-
- document.addEventListener('visibilitychange', handleVisibilityChange);
-
- // 정기적인 데이터 새로고침 (60초마다로 변경 - 너무 빈번한 리프레시 방지)
- const refreshInterval = setInterval(() => {
- try {
- if (document.visibilityState === 'visible' &&
- sessionStorage.getItem('isRefreshing') !== 'true') {
- console.log('정기 새로고침 - 데이터 업데이트');
- handleFocus();
- }
- } catch (error) {
- console.error('정기 새로고침 처리 중 오류:', error);
- }
- }, 60000); // 10초에서 60초로 변경
-
- return () => {
- window.removeEventListener('focus', handleFocus);
- document.removeEventListener('visibilitychange', handleVisibilityChange);
- clearInterval(refreshInterval);
- };
- }, []);
-
return (
-
-
-
-
-
+