fix: add ErrorBoundary to isolate client-side rendering errors
CI/CD / build-and-push (push) Successful in 1m31s Details

This commit is contained in:
Cloud User 2026-03-16 12:33:16 +09:00
parent f9605360c5
commit 7a0b1841f9
4 changed files with 93 additions and 37 deletions

View File

@ -8,6 +8,7 @@ import TimeSeriesChart from '@/components/charts/TimeSeriesChart';
import NetworkChart from '@/components/charts/NetworkChart';
import NodeTable from '@/components/report/NodeTable';
import AlertSection from '@/components/report/AlertSection';
import ErrorBoundary from '@/components/ErrorBoundary';
export default function Home() {
const [days, setDays] = useState(7);
@ -51,7 +52,7 @@ export default function Home() {
if (error || !data) {
return (
<div className="min-h-screen flex items-center justify-center">
<div className="text-red-500"> : {error?.message}</div>
<div className="text-red-500"> : {String(error)}</div>
</div>
);
}
@ -59,11 +60,13 @@ export default function Home() {
return (
<main className="min-h-screen bg-gray-100 p-6">
<div ref={reportRef} className="max-w-7xl mx-auto space-y-6">
<ErrorBoundary fallback="ReportHeader 오류">
<ReportHeader
start={data.period.start}
end={data.period.end}
onExportPdf={handleExportPdf}
/>
</ErrorBoundary>
<div className="flex gap-2">
{[1, 3, 7, 14, 30].map((d) => (
@ -79,23 +82,36 @@ export default function Home() {
))}
</div>
<ErrorBoundary fallback="SummaryCards 오류">
<SummaryCards
cpu={data.metrics.cpu}
memory={data.metrics.memory}
disk={data.metrics.disk}
network={data.metrics.network}
/>
</ErrorBoundary>
<ErrorBoundary fallback="CPU 차트 오류">
<TimeSeriesChart title="CPU 사용률" instances={data.metrics.cpu} unit="%" />
</ErrorBoundary>
<ErrorBoundary fallback="Memory 차트 오류">
<TimeSeriesChart title="Memory 사용률" instances={data.metrics.memory} unit="%" />
</ErrorBoundary>
<ErrorBoundary fallback="Disk 차트 오류">
<TimeSeriesChart title="Disk 사용률" instances={data.metrics.disk} unit="%" />
</ErrorBoundary>
{data.metrics.nas.length > 0 && (
{data.metrics.nas && data.metrics.nas.length > 0 && (
<ErrorBoundary fallback="NAS 차트 오류">
<TimeSeriesChart title="NAS 사용률" instances={data.metrics.nas} unit="%" />
</ErrorBoundary>
)}
<ErrorBoundary fallback="Network 차트 오류">
<NetworkChart data={data.metrics.network} />
</ErrorBoundary>
<ErrorBoundary fallback="NodeTable 오류">
<div>
<h2 className="text-lg font-semibold text-gray-800 mb-3"> </h2>
<NodeTable
@ -105,7 +121,9 @@ export default function Home() {
disk={data.metrics.disk}
/>
</div>
</ErrorBoundary>
<ErrorBoundary fallback="AlertSection 오류">
<div>
<h2 className="text-lg font-semibold text-gray-800 mb-3"> </h2>
<AlertSection
@ -114,6 +132,7 @@ export default function Home() {
disk={data.metrics.disk}
/>
</div>
</ErrorBoundary>
</div>
</main>
);

View File

@ -0,0 +1,37 @@
'use client';
import { Component, ReactNode } from 'react';
interface Props {
children: ReactNode;
fallback?: string;
}
interface State {
hasError: boolean;
error: Error | null;
}
export default class ErrorBoundary extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error: Error): State {
return { hasError: true, error };
}
render() {
if (this.state.hasError) {
return (
<div className="bg-red-50 border border-red-200 rounded-lg p-4">
<p className="text-red-800 text-sm font-medium">{this.props.fallback || '컴포넌트 렌더링 오류'}</p>
<p className="text-red-600 text-xs mt-1">{this.state.error?.message}</p>
<pre className="text-red-500 text-xs mt-2 overflow-auto max-h-40">{this.state.error?.stack}</pre>
</div>
);
}
return this.props.children;
}
}

View File

@ -4,7 +4,7 @@ import dynamic from 'next/dynamic';
import type { NetworkMetrics } from '@/types/metrics';
import { formatTimestamp, formatBytes } from '@/lib/formatters';
const ReactECharts = dynamic(() => import('echarts-for-react'), { ssr: false });
const ReactECharts = dynamic(() => import('echarts-for-react').then(mod => mod), { ssr: false, loading: () => <div style={{height: 300}} /> });
interface NetworkChartProps {
data: NetworkMetrics;

View File

@ -4,7 +4,7 @@ import dynamic from 'next/dynamic';
import type { MetricInstance } from '@/types/metrics';
import { formatTimestamp } from '@/lib/formatters';
const ReactECharts = dynamic(() => import('echarts-for-react'), { ssr: false });
const ReactECharts = dynamic(() => import('echarts-for-react').then(mod => mod), { ssr: false, loading: () => <div style={{height: 300}} /> });
interface TimeSeriesChartProps {
title: string;