대시보드 시스템 상태 카드에 메모리·디스크 신호등 추가

- Backend: PipelineMonitorService에 system 항목 추가 (OS 메모리, 디스크 사용률)
- Frontend: SystemMonitorDto에 system 필드 추가
- DashboardPage: 시스템/메모리/디스크 3열 신호등 레이아웃 적용
- dashboardCommand.css: gc-dash-sys-status, gc-sys-tl-item 스타일 추가

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Macbook
2026-06-11 16:37:42 +09:00
parent 900c743aa1
commit 14fafea739
4 changed files with 132 additions and 8 deletions
+58 -8
View File
@@ -182,15 +182,65 @@ const DashboardPage: React.FC<Props> = ({ theme, onGoChart }) => {
<section className="gc-dash-card">
<h2 className="gc-dash-card-title"> </h2>
<div className="gc-dash-card-inner gc-dash-card-inner--center">
<div className={`gc-traffic-light gc-traffic-light--${health}`} aria-label="시스템 상태">
<span className="gc-traffic-bulb gc-traffic-bulb--r" />
<span className="gc-traffic-bulb gc-traffic-bulb--y" />
<span className="gc-traffic-bulb gc-traffic-bulb--g" />
<div className="gc-dash-card-inner gc-dash-sys-status">
{/* 전체 시스템 */}
<div className="gc-sys-tl-item">
<div className={`gc-traffic-light gc-traffic-light--${health}`} aria-label="시스템 상태">
<span className="gc-traffic-bulb gc-traffic-bulb--r" />
<span className="gc-traffic-bulb gc-traffic-bulb--y" />
<span className="gc-traffic-bulb gc-traffic-bulb--g" />
</div>
<p className="gc-traffic-caption">
{health === 'green' ? '정상 운영' : health === 'yellow' ? '주의 필요' : '점검 필요'}
</p>
<span className="gc-sys-tl-label"></span>
</div>
<p className="gc-traffic-caption">
{health === 'green' ? '정상 운영' : health === 'yellow' ? '주의 필요' : '점검 필요'}
</p>
{/* 메모리 */}
{(() => {
const memPct = mon.system?.osMemPct ?? mon.jvm.heapPct;
const memLevel: 'green' | 'yellow' | 'red' =
memPct >= 90 ? 'red' : memPct >= 70 ? 'yellow' : 'green';
const memUsed = mon.system?.osMemUsedMb ?? mon.jvm.heapUsedMb;
const memTotal = mon.system?.osMemTotalMb ?? mon.jvm.heapMaxMb;
return (
<div className="gc-sys-tl-item">
<div className={`gc-traffic-light gc-traffic-light--${memLevel}`} aria-label="메모리 사용량">
<span className="gc-traffic-bulb gc-traffic-bulb--r" />
<span className="gc-traffic-bulb gc-traffic-bulb--y" />
<span className="gc-traffic-bulb gc-traffic-bulb--g" />
</div>
<p className="gc-traffic-caption">{memPct.toFixed(0)}%</p>
<p className="gc-traffic-caption gc-traffic-caption--sub">
{memUsed >= 1024 ? `${(memUsed/1024).toFixed(1)}GB` : `${memUsed}MB`}
{' / '}
{memTotal >= 1024 ? `${(memTotal/1024).toFixed(1)}GB` : `${memTotal}MB`}
</p>
<span className="gc-sys-tl-label"></span>
</div>
);
})()}
{/* 디스크 */}
{(() => {
const diskPct = mon.system?.diskPct ?? 0;
const diskLevel: 'green' | 'yellow' | 'red' =
diskPct >= 90 ? 'red' : diskPct >= 70 ? 'yellow' : 'green';
const diskUsed = mon.system?.diskUsedGb ?? 0;
const diskTotal = mon.system?.diskTotalGb ?? 0;
return (
<div className="gc-sys-tl-item">
<div className={`gc-traffic-light gc-traffic-light--${diskLevel}`} aria-label="디스크 사용량">
<span className="gc-traffic-bulb gc-traffic-bulb--r" />
<span className="gc-traffic-bulb gc-traffic-bulb--y" />
<span className="gc-traffic-bulb gc-traffic-bulb--g" />
</div>
<p className="gc-traffic-caption">{diskPct.toFixed(0)}%</p>
<p className="gc-traffic-caption gc-traffic-caption--sub">
{diskUsed.toFixed(1)}GB / {diskTotal.toFixed(1)}GB
</p>
<span className="gc-sys-tl-label"></span>
</div>
);
})()}
</div>
</section>
+30
View File
@@ -321,6 +321,36 @@
font-size: 0.72rem;
color: var(--se-text-muted);
}
.gc-traffic-caption--sub {
font-size: 0.62rem;
margin-top: 2px;
}
/* ── 시스템 상태 — 3열 신호등 ── */
.gc-dash-sys-status {
display: flex;
align-items: flex-start;
justify-content: space-around;
gap: 8px;
padding: 8px 4px;
}
.gc-sys-tl-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
flex: 1;
}
.gc-sys-tl-label {
font-size: 0.65rem;
font-weight: 600;
color: var(--se-text-muted);
letter-spacing: 0.03em;
text-transform: uppercase;
margin-top: 2px;
}
/* ── Indicator bars ── */
+8
View File
@@ -576,6 +576,14 @@ export interface SystemMonitorDto {
};
traffic: Record<string, number>;
memory: { markets: number; series: number; totalBars: number; maxBarsPerSeries: number };
system?: {
osMemUsedMb: number;
osMemTotalMb: number;
osMemPct: number;
diskUsedGb: number;
diskTotalGb: number;
diskPct: number;
};
queue: { pending: number; capacity: number; fillRatio: number; enqueued: number; dequeued: number; dropped: number };
workers: { processed: number; errors: number; active: boolean; workerThreads: number };
orders: {