import React, { useState, useEffect, useCallback, useMemo } from 'react'; import { useDevices } from '../hooks/useDevices'; import { useSensorData } from '../hooks/useSensorData'; import LoadingSpinner from '../components/LoadingSpinner'; import StatusIndicator from '../components/StatusIndicator'; import SensorCard from '../components/SensorCard'; import SensorChart from '../components/SensorChart'; import { transformSensorDataToChartData, transformSensorDataToCardData, validateChartData, ChartDataPoint, SensorCardData } from '../utils/dataTransformers'; import { isSensorData } from '../utils/typeGuards'; // 안전한 숫자 변환 함수 - 타입 안전성 강화 const safeNumber = (value: any, defaultValue: number = 0): number => { if (value === null || value === undefined) { return defaultValue; } const num = Number(value); if (typeof num === 'number' && !isNaN(num) && isFinite(num)) { return num; } return defaultValue; }; // 센서 데이터 유효성 검증 함수 const validateSensorData = (data: any): boolean => { return data && typeof data === 'object' && typeof data.device_id === 'string' && data.device_id.length > 0; }; const Dashboard: React.FC = () => { const { devices, loading: devicesLoading, error: devicesError, refetch: refetchDevices } = useDevices(); const deviceIds = useMemo(() => devices.map(d => d.device_id), [devices]); console.log('🏠 Dashboard 렌더링 - 디바이스 ID:', deviceIds); const { latestData, loading: dataLoading, error: dataError, refetch: refetchData, isConnected, dataSource, lastUpdate, connectionStatus, retryCount } = useSensorData(deviceIds); console.log('📊 Dashboard 데이터 상태:', { deviceIds, latestDataCount: latestData?.length || 0, loading: dataLoading, error: dataError, isConnected, dataSource, lastUpdate }); // 첫 번째 디바이스 데이터 메모이제이션 - null 체크 강화 const firstDeviceData = useMemo(() => { if (!latestData || !Array.isArray(latestData) || latestData.length === 0) { return null; } const validData = latestData.find(data => validateSensorData(data)); return validData || null; }, [latestData]); // 센서 카드 데이터 메모이제이션 - 새로운 변환 유틸리티 사용 const sensorCards = useMemo((): SensorCardData[] => { return transformSensorDataToCardData(firstDeviceData); }, [firstDeviceData]); // 차트 데이터 메모이제이션 - 새로운 변환 유틸리티 사용 const chartData = useMemo((): ChartDataPoint[] => { try { const transformedData = transformSensorDataToChartData(latestData); // 변환된 데이터 유효성 검증 if (!validateChartData(transformedData)) { console.warn('Invalid chart data detected, using default data'); return transformSensorDataToChartData([]); } return transformedData; } catch (err) { console.error('Error processing chart data:', err); return transformSensorDataToChartData([]); } }, [latestData]); // 에러 처리 const error = devicesError || dataError; if (devicesLoading || dataLoading) { return (
); } if (error) { return (

오류가 발생했습니다

{error}

); } return (
{/* 상태 표시 */}

센서 대시보드

{/* 요약 카드 */}
📱

활성 디바이스

{devices.filter(d => d.status === 'active').length}

📊

총 데이터

{latestData.length}

{/* 센서 카드들 */}
{sensorCards.slice(0, 4).map((card, index) => ( ))}
{sensorCards.slice(4, 8).map((card, index) => ( ))}
{sensorCards.slice(8, 10).map((card, index) => ( ))}
{/* 기본 센서 차트 */}
{/* 환경 센서 차트 */}
{/* 가스 센서 차트 */}
{/* 디바이스 목록 */}

디바이스 상태

{devices.map((device) => ( ))}
디바이스 ID 이름 상태 마지막 연결
{device.device_id} {device.name} {device.status} {new Date(device.last_seen).toLocaleString()}
); }; export default Dashboard;