Reverted to edit edt-fb357fcb-7bf8-4196-adc5-e2dc2fed4b19: "Fix notch issue on iOS

Addresses the notch display issue on iOS devices."
This commit is contained in:
gpt-engineer-app[bot]
2025-03-23 10:23:55 +00:00
parent d216daf2f4
commit 606ac2f96a
6 changed files with 93 additions and 870 deletions

View File

@@ -1,142 +0,0 @@
/**
* 이벤트 버스 최적화 - 통합된 이벤트 관리 시스템
*/
// 전역 이벤트 이름 정의
export const APP_EVENTS = {
// 데이터 변경 이벤트
DATA_UPDATED: 'app:data-updated',
TRANSACTION_UPDATED: 'app:transaction-updated',
BUDGET_UPDATED: 'app:budget-updated',
CATEGORY_UPDATED: 'app:category-updated',
// 인증 이벤트
AUTH_STATE_CHANGED: 'app:auth-state-changed',
USER_LOGGED_IN: 'app:user-logged-in',
USER_LOGGED_OUT: 'app:user-logged-out',
// 동기화 이벤트
SYNC_STARTED: 'app:sync-started',
SYNC_COMPLETED: 'app:sync-completed',
SYNC_FAILED: 'app:sync-failed',
// UI 이벤트
PAGE_LOADED: 'app:page-loaded',
TAB_CHANGED: 'app:tab-changed',
}
// 최적화된 이벤트 발생 함수 (디바운스, 이벤트 통합 등)
let queuedEvents: Record<string, any> = {};
let eventQueueTimer: ReturnType<typeof setTimeout> | null = null;
/**
* 이벤트 발생 - 디바운스 적용 (짧은 시간 내 동일 이벤트는 마지막 한 번만 발생)
*/
export function emitEvent(eventName: string, detail?: any): void {
// 이벤트 대기열에 저장
queuedEvents[eventName] = { detail };
// 이미 타이머가 설정되어 있으면 기존 타이머 유지
if (eventQueueTimer) return;
// 타이머 설정 (10ms 후 대기열의 이벤트 모두 발생)
eventQueueTimer = setTimeout(() => {
// 대기열의 모든 이벤트 처리
for (const [name, data] of Object.entries(queuedEvents)) {
try {
// 표준 이벤트 발생
window.dispatchEvent(new CustomEvent(name, {
detail: data.detail,
bubbles: true,
cancelable: true,
}));
// 하위 호환성 유지를 위한 기존 이벤트 매핑
if (name === APP_EVENTS.TRANSACTION_UPDATED) {
window.dispatchEvent(new Event('transactionUpdated'));
} else if (name === APP_EVENTS.BUDGET_UPDATED) {
window.dispatchEvent(new Event('budgetDataUpdated'));
} else if (name === APP_EVENTS.CATEGORY_UPDATED) {
window.dispatchEvent(new Event('categoryBudgetsUpdated'));
}
console.log(`[이벤트] 발생: ${name}`);
} catch (error) {
console.error(`[이벤트] 발생 오류 (${name}):`, error);
}
}
// 대기열 및 타이머 초기화
queuedEvents = {};
eventQueueTimer = null;
}, 10);
}
/**
* 여러 이벤트 한 번에 발생 (배치 이벤트)
*/
export function emitBatchEvents(events: { name: string; detail?: any }[]): void {
// 모든 이벤트를 대기열에 추가
events.forEach(event => {
queuedEvents[event.name] = { detail: event.detail };
});
// 기존 타이머 취소
if (eventQueueTimer) {
clearTimeout(eventQueueTimer);
}
// 새 타이머 설정 (즉시 실행)
eventQueueTimer = setTimeout(() => {
// 대기열의 모든 이벤트 처리
for (const [name, data] of Object.entries(queuedEvents)) {
try {
// 표준 이벤트 발생
window.dispatchEvent(new CustomEvent(name, {
detail: data.detail,
bubbles: true,
cancelable: true,
}));
// 하위 호환성 유지를 위한 기존 이벤트 매핑
if (name === APP_EVENTS.TRANSACTION_UPDATED) {
window.dispatchEvent(new Event('transactionUpdated'));
} else if (name === APP_EVENTS.BUDGET_UPDATED) {
window.dispatchEvent(new Event('budgetDataUpdated'));
} else if (name === APP_EVENTS.CATEGORY_UPDATED) {
window.dispatchEvent(new Event('categoryBudgetsUpdated'));
}
console.log(`[이벤트] 배치 발생: ${name}`);
} catch (error) {
console.error(`[이벤트] 배치 발생 오류 (${name}):`, error);
}
}
// 대기열 및 타이머 초기화
queuedEvents = {};
eventQueueTimer = null;
}, 0);
}
/**
* 페이지가 포커스를 얻었을 때 전체 데이터 새로고침 이벤트 발생
*/
export function setupPageVisibilityEvents(): void {
// 포커스 이벤트
window.addEventListener('focus', () => {
console.log('[이벤트] 창이 포커스를 얻음');
emitEvent(APP_EVENTS.PAGE_LOADED);
});
// 가시성 변경 이벤트
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible') {
console.log('[이벤트] 페이지가 다시 보임');
emitEvent(APP_EVENTS.PAGE_LOADED);
}
});
console.log('[이벤트] 페이지 가시성 이벤트 설정 완료');
}

View File

@@ -1,118 +0,0 @@
/**
* 성능 최적화를 위한 유틸리티 함수
*/
// 디바운스: 여러 호출 중 마지막 호출만 실행
export function debounce<T extends (...args: any[]) => any>(
func: T,
wait: number
): (...args: Parameters<T>) => void {
let timeout: ReturnType<typeof setTimeout> | null = null;
return function (...args: Parameters<T>) {
const later = () => {
timeout = null;
func(...args);
};
if (timeout) clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// 스로틀: 일정 시간 내에 한 번만 실행
export function throttle<T extends (...args: any[]) => any>(
func: T,
limit: number
): (...args: Parameters<T>) => void {
let inThrottle = false;
return function (...args: Parameters<T>) {
if (!inThrottle) {
func(...args);
inThrottle = true;
setTimeout(() => {
inThrottle = false;
}, limit);
}
};
}
// 실행 중인 작업 추적
const pendingOperations: Record<string, boolean> = {};
// 중복 실행 방지 (한 번에 동일 작업 한 번만 실행)
export function preventDuplicateOperation<T extends (...args: any[]) => Promise<any>>(
operationKey: string,
func: T
): (...args: Parameters<T>) => Promise<ReturnType<T> | void> {
return async function (...args: Parameters<T>): Promise<ReturnType<T> | void> {
// 이미 실행 중인 작업이면 중단
if (pendingOperations[operationKey]) {
console.log(`[성능] ${operationKey} 작업이 이미 실행 중입니다. 중복 요청 무시.`);
return;
}
try {
// 작업 시작 플래그 설정
pendingOperations[operationKey] = true;
console.log(`[성능] ${operationKey} 작업 시작`);
// 작업 실행
const result = await func(...args);
return result;
} finally {
// 작업 종료 후 플래그 해제
pendingOperations[operationKey] = false;
console.log(`[성능] ${operationKey} 작업 완료`);
}
};
}
// 네트워크 요청 캐시 (메모리 캐시, 세션 내 유효)
const requestCache: Record<string, { data: any; timestamp: number }> = {};
// 캐시 설정 (TTL: 캐시 유효 시간(ms))
export function withCache<T extends (...args: any[]) => Promise<any>>(
cacheKey: string,
func: T,
ttl: number = 60000 // 기본값 1분
): (...args: Parameters<T>) => Promise<ReturnType<T>> {
return async function (...args: Parameters<T>): Promise<ReturnType<T>> {
const now = Date.now();
// 캐시 유효성 확인
if (
requestCache[cacheKey] &&
now - requestCache[cacheKey].timestamp < ttl
) {
console.log(`[성능] 캐시된 데이터 사용: ${cacheKey}`);
return requestCache[cacheKey].data;
}
// 캐시 만료 또는 없음 - 새로 요청
console.log(`[성능] 새 데이터 요청: ${cacheKey}`);
const result = await func(...args);
// 결과 캐싱
requestCache[cacheKey] = {
data: result,
timestamp: now
};
return result;
};
}
// 배치 상태 업데이트 관리
export function batchedUpdates<T>(updates: () => T): T {
// React의 unstable_batchedUpdates 사용 (가능한 경우)
if (typeof window !== 'undefined' && 'ReactDOM' in window) {
// @ts-ignore: ReactDOM이 전역에 존재할 수 있음
return window.ReactDOM.unstable_batchedUpdates(updates);
}
// 폴백: 기본 실행
return updates();
}

View File

@@ -1,99 +0,0 @@
/**
* 동기화 최적화 유틸리티
*/
import { debounce, throttle, preventDuplicateOperation } from '../performance/debounceThrottle';
import { trySyncAllData } from '@/utils/syncUtils';
import { toast } from '@/hooks/useToast.wrapper';
// 동기화 상태 플래그
let isSyncRunning = false;
let lastSyncTime = 0;
const MIN_SYNC_INTERVAL = 30000; // 최소 동기화 간격 (30초)
/**
* 최적화된 동기화 함수 - 중복 방지, 속도 제한 적용
*/
export const optimizedSync = preventDuplicateOperation(
'sync-all-data',
async (userId: string) => {
// 이미 동기화 중이면 중단
if (isSyncRunning) {
console.log('[최적화] 이미 동기화 작업이 진행 중입니다.');
return { success: false, reason: 'already-running' };
}
// 너무 빈번한 동기화 요청 방지
const now = Date.now();
if (now - lastSyncTime < MIN_SYNC_INTERVAL) {
console.log(`[최적화] 동기화 요청이 너무 빈번합니다. ${MIN_SYNC_INTERVAL / 1000}초 후 다시 시도하세요.`);
return { success: false, reason: 'too-frequent' };
}
try {
isSyncRunning = true;
console.log('[최적화] 동기화 시작...');
// 네트워크 상태 확인
if (!navigator.onLine) {
console.log('[최적화] 오프라인 상태입니다. 동기화를 건너뜁니다.');
return { success: false, reason: 'offline' };
}
// 실제 동기화 수행
const result = await trySyncAllData(userId);
// 마지막 동기화 시간 기록
lastSyncTime = Date.now();
return result;
} catch (error) {
console.error('[최적화] 동기화 오류:', error);
// 중요 오류만 사용자에게 표시
toast({
title: "동기화 오류",
description: "데이터 동기화 중 문제가 발생했습니다. 나중에 다시 시도해주세요.",
variant: "destructive",
});
return { success: false, reason: 'error', error };
} finally {
isSyncRunning = false;
}
}
);
/**
* 디바운스된 동기화 함수 (빠르게 여러 번 호출해도 마지막 한 번만 실행)
*/
export const debouncedSync = debounce(
async (userId: string) => {
console.log('[최적화] 디바운스된 동기화 실행');
return optimizedSync(userId);
},
2000 // 2초 대기
);
/**
* 스로틀된 동기화 함수 (일정 시간 내 한 번만 실행)
*/
export const throttledSync = throttle(
async (userId: string) => {
console.log('[최적화] 스로틀된 동기화 실행');
return optimizedSync(userId);
},
10000 // 10초마다 최대 한 번
);
/**
* 자동 동기화 시도 (에러 무시)
*/
export const attemptBackgroundSync = async (userId: string) => {
try {
return await optimizedSync(userId);
} catch (error) {
console.error('[최적화] 백그라운드 동기화 오류 (무시됨):', error);
return { success: false, reason: 'background-error' };
}
};