From fb43bf6d07e4adce3571622b9397d0ac755cfa62 Mon Sep 17 00:00:00 2001 From: Hyoseong Jo Date: Sun, 28 Dec 2025 17:55:55 +0900 Subject: [PATCH] =?UTF-8?q?=EC=86=8C=EC=8A=A4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/utils/server-scheduler.ts | 39 +++-- frontend/assets/css/main.css | 8 + frontend/components/ServerPortlet.vue | 239 +++++++++++++++++++------- frontend/components/SidebarNav.vue | 182 +++++++++++++++++--- frontend/composables/useSidebar.ts | 24 +++ 5 files changed, 389 insertions(+), 103 deletions(-) create mode 100644 frontend/composables/useSidebar.ts diff --git a/backend/utils/server-scheduler.ts b/backend/utils/server-scheduler.ts index 03a9ce6..2ced357 100644 --- a/backend/utils/server-scheduler.ts +++ b/backend/utils/server-scheduler.ts @@ -62,6 +62,9 @@ async function detectApiVersion(baseUrl: string, serverName: string): Promise 1 ? ((currCpuAvg - prevCpuAvg) / prevCpuAvg) * 100 : currCpuAvg - prevCpuAvg const memChange = prevMemAvg > 1 ? ((currMemAvg - prevMemAvg) / prevMemAvg) * 100 : currMemAvg - prevMemAvg - // CPU 단기 변화율 체크 (증가만 감지) - if (cpuChange >= SHORT_TERM_THRESHOLD) { + // 절대값 변화량 + const cpuAbsChange = currCpuAvg - prevCpuAvg + const memAbsChange = currMemAvg - prevMemAvg + + // CPU 단기 변화율 체크 (증가만 감지, 절대값 5%p 이상일 때만) + if (cpuChange >= SHORT_TERM_THRESHOLD && cpuAbsChange >= MIN_ABSOLUTE_CHANGE) { const recentExists = await queryOne(` SELECT 1 FROM anomaly_logs WHERE target_id = $1 AND detect_type = 'short-term' AND metric = 'CPU' @@ -108,8 +115,8 @@ async function detectAnomalies(targetId: number, serverName: string) { } } - // Memory 단기 변화율 체크 (증가만 감지) - if (memChange >= SHORT_TERM_THRESHOLD) { + // Memory 단기 변화율 체크 (증가만 감지, 절대값 5%p 이상일 때만) + if (memChange >= SHORT_TERM_THRESHOLD && memAbsChange >= MIN_ABSOLUTE_CHANGE) { const recentExists = await queryOne(` SELECT 1 FROM anomaly_logs WHERE target_id = $1 AND detect_type = 'short-term' AND metric = 'Memory' @@ -161,8 +168,12 @@ async function detectAnomalies(targetId: number, serverName: string) { const cpuZscore = cpuStd > 0.1 ? (currCpu - cpuAvg) / cpuStd : 0 const memZscore = memStd > 0.1 ? (currMem - memAvg) / memStd : 0 - // CPU Z-Score 체크 (높은 경우만 감지) - if (cpuZscore >= WARNING_Z) { + // 절대값 변화량 + const cpuAbsDiff = currCpu - cpuAvg + const memAbsDiff = currMem - memAvg + + // CPU Z-Score 체크 (높은 경우만 감지, 절대값 5%p 이상일 때만) + if (cpuZscore >= WARNING_Z && cpuAbsDiff >= MIN_ABSOLUTE_CHANGE) { const recentExists = await queryOne(` SELECT 1 FROM anomaly_logs WHERE target_id = $1 AND detect_type = 'zscore' AND metric = 'CPU' @@ -182,8 +193,8 @@ async function detectAnomalies(targetId: number, serverName: string) { } } - // Memory Z-Score 체크 (높은 경우만 감지) - if (memZscore >= WARNING_Z) { + // Memory Z-Score 체크 (높은 경우만 감지, 절대값 5%p 이상일 때만) + if (memZscore >= WARNING_Z && memAbsDiff >= MIN_ABSOLUTE_CHANGE) { const recentExists = await queryOne(` SELECT 1 FROM anomaly_logs WHERE target_id = $1 AND detect_type = 'zscore' AND metric = 'Memory' @@ -249,8 +260,12 @@ async function detectAnomalies(targetId: number, serverName: string) { const cpuDeviation = cpuStd > 0.1 ? (currCpu - cpuAvg) / cpuStd : 0 const memDeviation = memStd > 0.1 ? (currMem - memAvg) / memStd : 0 - // CPU 베이스라인 체크 (높은 경우만 감지) - if (cpuDeviation >= DEVIATION_THRESHOLD) { + // 절대값 변화량 + const cpuBaseAbsDiff = currCpu - cpuAvg + const memBaseAbsDiff = currMem - memAvg + + // CPU 베이스라인 체크 (높은 경우만 감지, 절대값 5%p 이상일 때만) + if (cpuDeviation >= DEVIATION_THRESHOLD && cpuBaseAbsDiff >= MIN_ABSOLUTE_CHANGE) { const recentExists = await queryOne(` SELECT 1 FROM anomaly_logs WHERE target_id = $1 AND detect_type = 'baseline' AND metric = 'CPU' @@ -271,8 +286,8 @@ async function detectAnomalies(targetId: number, serverName: string) { } } - // Memory 베이스라인 체크 (높은 경우만 감지) - if (memDeviation >= DEVIATION_THRESHOLD) { + // Memory 베이스라인 체크 (높은 경우만 감지, 절대값 5%p 이상일 때만) + if (memDeviation >= DEVIATION_THRESHOLD && memBaseAbsDiff >= MIN_ABSOLUTE_CHANGE) { const recentExists = await queryOne(` SELECT 1 FROM anomaly_logs WHERE target_id = $1 AND detect_type = 'baseline' AND metric = 'Memory' diff --git a/frontend/assets/css/main.css b/frontend/assets/css/main.css index d46d57b..34fd969 100644 --- a/frontend/assets/css/main.css +++ b/frontend/assets/css/main.css @@ -38,6 +38,10 @@ --sidebar-active-bg: #e8e8e8; --sidebar-active-border: #4a90d9; + /* 액센트 색상 */ + --accent-color: #3b82f6; + --accent-bg: #eff6ff; + /* 기타 */ --link-color: #4a90d9; --time-color: #4a90d9; @@ -110,6 +114,10 @@ --sidebar-active-bg: #3a3a3a; --sidebar-active-border: #6b9dc4; + /* 액센트 색상 */ + --accent-color: #60a5fa; + --accent-bg: #1e3a5f; + /* 기타 */ --link-color: #6b9dc4; --time-color: #6b9dc4; diff --git a/frontend/components/ServerPortlet.vue b/frontend/components/ServerPortlet.vue index 91f12b9..66b49cf 100644 --- a/frontend/components/ServerPortlet.vue +++ b/frontend/components/ServerPortlet.vue @@ -27,13 +27,11 @@
-
-
{{ levelIcon(server.level) }} @@ -47,21 +45,27 @@
- {{ server.cpu_percent?.toFixed(0) || '-' }} + + {{ server.cpu_percent?.toFixed(0) || '-' }} +
MEM
- {{ server.memory_percent?.toFixed(0) || '-' }} + + {{ server.memory_percent?.toFixed(0) || '-' }} +
DISK
- {{ server.disk_percent?.toFixed(0) || '-' }} + + {{ server.disk_percent?.toFixed(0) || '-' }} +
@@ -73,7 +77,6 @@
-
CPU
-
+
- {{ container.cpu_percent?.toFixed(0) || '-' }}% + + {{ container.cpu_percent?.toFixed(0) || '-' }}% +
MEM
- {{ formatMemoryShort(container.memory_usage) }} + + {{ formatMemoryShort(container.memory_usage) }} +
↓RX - {{ formatNetworkShort(container.network_rx) }} + + {{ formatNetworkShort(container.network_rx) }} +
↑TX - {{ formatNetworkShort(container.network_tx) }} + + {{ formatNetworkShort(container.network_tx) }} +
@@ -119,7 +130,6 @@
-
컨테이너 없음
@@ -130,6 +140,8 @@ + + diff --git a/frontend/composables/useSidebar.ts b/frontend/composables/useSidebar.ts new file mode 100644 index 0000000..31db423 --- /dev/null +++ b/frontend/composables/useSidebar.ts @@ -0,0 +1,24 @@ +const sidebarCollapsed = ref(true) // 기본값: 닫힌 상태 + +export function useSidebar() { + const isCollapsed = computed(() => sidebarCollapsed.value) + + function toggle() { + sidebarCollapsed.value = !sidebarCollapsed.value + } + + function open() { + sidebarCollapsed.value = false + } + + function close() { + sidebarCollapsed.value = true + } + + return { + isCollapsed, + toggle, + open, + close + } +}