diff --git a/src/components/AddTransactionButton.tsx b/src/components/AddTransactionButton.tsx index 03dc537..b692275 100644 --- a/src/components/AddTransactionButton.tsx +++ b/src/components/AddTransactionButton.tsx @@ -8,6 +8,7 @@ import { supabase } from '@/lib/supabase'; import { isSyncEnabled } from '@/utils/syncUtils'; import ExpenseForm, { ExpenseFormValues } from './expenses/ExpenseForm'; import { Transaction } from '@/components/TransactionCard'; +import { normalizeDate } from '@/utils/sync/transaction/dateUtils'; const AddTransactionButton = () => { const [showExpenseDialog, setShowExpenseDialog] = useState(false); @@ -53,11 +54,14 @@ const AddTransactionButton = () => { const { data: { user } } = await supabase.auth.getUser(); if (isSyncEnabled() && user) { + // ISO 형식으로 날짜 변환 + const isoDate = normalizeDate(formattedDate); + const { error } = await supabase.from('transactions').insert({ user_id: user.id, title: data.title, amount: parseInt(numericAmount), - date: formattedDate, + date: isoDate, // ISO 형식 사용 category: data.category, type: 'expense', transaction_id: newExpense.id diff --git a/src/hooks/transactions/supabaseUtils.ts b/src/hooks/transactions/supabaseUtils.ts index 6993cd9..79cb653 100644 --- a/src/hooks/transactions/supabaseUtils.ts +++ b/src/hooks/transactions/supabaseUtils.ts @@ -3,6 +3,45 @@ import { Transaction } from '@/components/TransactionCard'; import { supabase } from '@/lib/supabase'; import { isSyncEnabled } from '@/utils/syncUtils'; import { useAuth } from '@/contexts/auth/AuthProvider'; +import { formatISO, parseISO } from 'date-fns'; + +// ISO 형식으로 날짜 변환 (Supabase 저장용) +const convertDateToISO = (dateStr: string): string => { + try { + // 이미 ISO 형식인 경우 그대로 반환 + if (dateStr.match(/^\d{4}-\d{2}-\d{2}T/)) { + return dateStr; + } + + // "오늘, 시간" 형식 처리 + if (dateStr.includes('오늘')) { + const today = new Date(); + + // 시간 추출 시도 + const timeMatch = dateStr.match(/(\d{1,2}):(\d{2})/); + if (timeMatch) { + const hours = parseInt(timeMatch[1], 10); + const minutes = parseInt(timeMatch[2], 10); + today.setHours(hours, minutes, 0, 0); + } + + return formatISO(today); + } + + // 일반 날짜 문자열은 그대로 Date 객체로 변환 시도 + const date = new Date(dateStr); + if (!isNaN(date.getTime())) { + return formatISO(date); + } + + // 변환 실패 시 현재 시간 반환 + console.warn(`날짜 변환 오류: "${dateStr}"를 ISO 형식으로 변환할 수 없습니다.`); + return formatISO(new Date()); + } catch (error) { + console.error(`날짜 변환 오류: "${dateStr}"`, error); + return formatISO(new Date()); + } +}; // Supabase와 트랜잭션 동기화 export const syncTransactionsWithSupabase = async (user: any, transactions: Transaction[]): Promise => { @@ -56,12 +95,15 @@ export const updateTransactionInSupabase = async (user: any, transaction: Transa if (!user || !isSyncEnabled()) return; try { + // 날짜를 ISO 형식으로 변환 + const isoDate = convertDateToISO(transaction.date); + const { error } = await supabase.from('transactions') .upsert({ user_id: user.id, title: transaction.title, amount: transaction.amount, - date: transaction.date, + date: isoDate, // ISO 형식 사용 category: transaction.category, type: transaction.type, transaction_id: transaction.id @@ -69,6 +111,8 @@ export const updateTransactionInSupabase = async (user: any, transaction: Transa if (error) { console.error('트랜잭션 업데이트 오류:', error); + } else { + console.log('Supabase 트랜잭션 업데이트 성공:', transaction.id); } } catch (error) { console.error('Supabase 업데이트 오류:', error); @@ -86,6 +130,8 @@ export const deleteTransactionFromSupabase = async (user: any, transactionId: st if (error) { console.error('트랜잭션 삭제 오류:', error); + } else { + console.log('Supabase 트랜잭션 삭제 성공:', transactionId); } } catch (error) { console.error('Supabase 삭제 오류:', error); diff --git a/src/hooks/transactions/transactionOperations/deleteOperation/deleteTransactionStorage.ts b/src/hooks/transactions/transactionOperations/deleteOperation/deleteTransactionStorage.ts index e299e10..5868e1c 100644 --- a/src/hooks/transactions/transactionOperations/deleteOperation/deleteTransactionStorage.ts +++ b/src/hooks/transactions/transactionOperations/deleteOperation/deleteTransactionStorage.ts @@ -4,6 +4,7 @@ import { Transaction } from '@/components/TransactionCard'; import { saveTransactionsToStorage } from '../../storageUtils'; import { deleteTransactionFromSupabase } from '../../supabaseUtils'; import { toast } from '@/hooks/useToast.wrapper'; +import { normalizeDate } from '@/utils/sync/transaction/dateUtils'; /** * 스토리지 및 Supabase 삭제 처리 @@ -31,6 +32,10 @@ export const handleDeleteStorage = ( // 동기적 에러를 피하기 위해 setTimeout으로 감싸기 setTimeout(() => { try { + // ISO 형식으로 날짜 변환 + const isoDate = normalizeDate(transactionToDelete.date); + console.log('삭제 중인 트랜잭션 ISO 날짜:', isoDate); + deleteTransactionFromSupabase(user, id) .catch(error => { console.error('Supabase 삭제 오류:', error); diff --git a/src/hooks/transactions/transactionOperations/updateTransaction.ts b/src/hooks/transactions/transactionOperations/updateTransaction.ts index c381577..403be73 100644 --- a/src/hooks/transactions/transactionOperations/updateTransaction.ts +++ b/src/hooks/transactions/transactionOperations/updateTransaction.ts @@ -6,6 +6,7 @@ import { toast } from '@/hooks/useToast.wrapper'; import { saveTransactionsToStorage } from '../storageUtils'; import { updateTransactionInSupabase } from '../supabaseUtils'; import { TransactionOperationProps } from './types'; +import { normalizeDate } from '@/utils/sync/transaction/dateUtils'; /** * 트랜잭션 업데이트 기능 @@ -28,9 +29,15 @@ export const useUpdateTransaction = ( // 상태 업데이트 setTransactions(updatedTransactions); - // Supabase 업데이트 시도 + // Supabase 업데이트 시도 (날짜 형식 변환 추가) if (user) { - updateTransactionInSupabase(user, updatedTransaction); + // ISO 형식으로 날짜 변환 + const transactionWithIsoDate = { + ...updatedTransaction, + dateForSync: normalizeDate(updatedTransaction.date) + }; + + updateTransactionInSupabase(user, transactionWithIsoDate); } // 이벤트 발생 diff --git a/src/hooks/transactions/useTransactionsEvents.ts b/src/hooks/transactions/useTransactionsEvents.ts index 422533d..edcaaf0 100644 --- a/src/hooks/transactions/useTransactionsEvents.ts +++ b/src/hooks/transactions/useTransactionsEvents.ts @@ -43,6 +43,20 @@ export const useTransactionsEvents = ( }, 200); }; + // 트랜잭션 변경 이벤트 (통합 이벤트) + const handleTransactionChange = (e: CustomEvent) => { + console.log('트랜잭션 변경 이벤트 감지:', e.detail?.type); + + // 처리 중 중복 호출 방지 + if (isProcessing) return; + + isProcessing = true; + setTimeout(() => { + loadTransactions(); + isProcessing = false; + }, 150); + }; + // 스토리지 이벤트 const handleStorageEvent = (e: StorageEvent) => { if (e.key === 'transactions' || e.key === null) { @@ -76,6 +90,7 @@ export const useTransactionsEvents = ( // 이벤트 리스너 등록 window.addEventListener('transactionUpdated', handleTransactionUpdate); window.addEventListener('transactionDeleted', handleTransactionDelete); + window.addEventListener('transactionChanged', handleTransactionChange as EventListener); window.addEventListener('storage', handleStorageEvent); window.addEventListener('focus', handleFocus); @@ -89,6 +104,7 @@ export const useTransactionsEvents = ( console.log('useTransactions - 이벤트 리스너 제거'); window.removeEventListener('transactionUpdated', handleTransactionUpdate); window.removeEventListener('transactionDeleted', handleTransactionDelete); + window.removeEventListener('transactionChanged', handleTransactionChange as EventListener); window.removeEventListener('storage', handleStorageEvent); window.removeEventListener('focus', handleFocus); };