IT 일반,소식

NestJS + NuxtJS로 실시간 서버 헬스 모니터링 대시보드 만들기 (WebSocket 기반)

_Blue_Sky_ 2025. 12. 5. 00:57
728x90

 

 

  • Backend (NestJS): 서버의 리소스 상태를 주기적으로 체크하고, 연결된 클라이언트들에게 WebSocket 이벤트를 Broadcasting 합니다.
  • Frontend (NuxtJS): 소켓 서버에 접속하여 데이터를 수신하고, 이를 시각적인 UI로 렌더링합니다.

 

실시간으로 서버의 CPU, 메모리, 디스크 사용량을 모니터링하고 싶다면?
NestJS 백엔드와 Nuxt 3 프론트엔드를 WebSocket으로 연결해서 멋진 대시보드를 만들어보자!
키워드NestJS, Nuxt3, WebSocket, Gateway, Socket.io, 서버 모니터링, 실시간 대시보드, CPU 사용량, 메모리 사용량, os 모듈, Chart.js목표
  • NestJS에서 주기적으로 서버 상태(os 정보)를 수집
  • WebSocket Gateway을 통해 실시간으로 클라이언트에게 브로드캐스트
  • Nuxt 3에서 Socket.io-client로 연결하여 실시간 차트 업데이트


1. NestJS 백엔드 설정

# 프로젝트 생성
nest new server-monitoring-backend
cd server-monitoring-backend
npm install @nestjs/websockets @nestjs/platform-socket.io socket.io os
Gateway 생성
nest g gateway monitoring
 
// monitoring.gateway.ts
import { WebSocketGateway, WebSocketServer, OnGatewayInit } from '@nestjs/websockets';
import { Server } from 'socket.io';
import * as os from 'os';

@WebSocketGateway({
  cors: {
    origin: '*',
  },
})
export class MonitoringGateway implements OnGatewayInit {
  @WebSocketServer()
  server: Server;

  private interval: NodeJS.Timeout;

  afterInit() {
    // 2초마다 서버 상태 전송
    this.interval = setInterval(() => {
      const cpus = os.cpus();
      const totalMem = os.totalmem();
      const freeMem = os.freemem();
      const loadAvg = os.loadavg()[0]; // 1분 평균 로드

      // CPU 사용률 계산 (간단한 방법)
      let totalIdle = 0, totalTick = 0;
      cpus.forEach(cpu => {
        for (const type in cpu.times) {
          totalTick += cpu.times[type];
        }
        totalIdle += cpu.times.idle;
      });
      const idle = totalIdle / cpus.length;
      const total = totalTick / cpus.length;
      const cpuUsage = 100 - Math.round((100 * idle) / total);

      this.server.emit('serverStats', {
        cpu: cpuUsage,
        memory: Math.round(((totalMem - freeMem) / totalMem) * 100),
        loadAverage: loadAvg.toFixed(2),
        uptime: os.uptime(),
        timestamp: new Date().toISOString(),
      });
    }, 2000);
  }

  handleDisconnect() {
    if (this.interval) clearInterval(this.interval);
  }
}
 
// main.ts - CORS 허용
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.enableCors({
    origin: '*',
  });
  await app.listen(3000);
}

서버 실행:
npm run start:devhttp://localhost:3000
2.


2. Nuxt 3 프론트엔드 설정

 

npx nuxi@latest init server-monitoring-frontend
cd server-monitoring-frontend
npm install socket.io-client chart.js primevue primeicons


plugins/socket.client.ts

import { defineNuxtPlugin } from '#app';
import io from 'socket.io-client';

export default defineNuxtPlugin(() => {
  const socket = io('http://localhost:3000', {
    autoConnect: true,
    reconnection: true,
  });

  return {
    provide: {
      socket,
    },
  };
});

pages/index.vue

<template>
  <div class="container mx-auto p-8">
    <h1 class="text-3xl font-bold mb-8">실시간 서버 헬스 모니터링</h1>
    
    <div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
      <div class="bg-white rounded-lg shadow p-6">
        <h3 class="text-lg font-semibold text-gray-700">CPU 사용량</h3>
        <p class="text-4xl font-bold mt-2">{{ stats.cpu }}%</p>
      </div>
      <div class="bg-white rounded-lg shadow p-6">
        <h3 class="text-lg font-semibold text-gray-700">메모리 사용량</h3>
        <p class="text-4xl font-bold mt-2">{{ stats.memory }}%</p>
      </div>
      <div class="bg-white rounded-lg shadow p-6">
        <h3 class="text-lg font-semibold text-gray-700">로드 평균</h3>
        <p class="text-4xl font-bold mt-2">{{ stats.loadAverage }}</p>
      </div>
    </div>

    <div class="bg-white rounded-lg shadow p-6">
      <h2 class="text-2xl font-bold mb-4">실시간 차트</h2>
      <canvas ref="chartRef"></canvas>
    </div>
  </div>
</template>

<script setup lang="ts">
const { $socket } = useNuxtApp();
const stats = ref({
  cpu: 0,
  memory: 0,
  loadAverage: '0.00',
});

const chartRef = ref<HTMLCanvasElement | null>(null);
let chart: any = null;

const labels = ref<string[]>([]);
const cpuData = ref<number[]>([]);
const memoryData = ref<number[]>([]);

onMounted(() => {
  $socket.on('serverStats', (data: any) => {
    stats.value = data;

    const time = new Date(data.timestamp).toLocaleTimeString();

    labels.value.push(time);
    cpuData.value.push(data.cpu);
    memoryData.value.push(data.memory);

    // 30개 데이터만 유지
    if (labels.value.length > 30) {
      labels.value.shift();
      cpuData.value.shift();
      memoryData.value.shift();
    }

    if (!chart && chartRef.value) {
      const ctx = chartRef.value.getContext('2d');
      chart = new Chart(ctx, {
        type: 'line',
        data: {
          labels: labels.value,
          datasets: [
            {
              label: 'CPU (%)',
              data: cpuData.value,
              borderColor: 'rgb(239, 68, 68)',
              tension: 0.4,
            },
            {
              label: 'Memory (%)',
              data: memoryData.value,
              borderColor: 'rgb(59, 130, 246)',
              tension: 0.4,
            },
          ],
        },
        options: {
          responsive: true,
          maintainAspectRatio: false,
        },
      });
    } else {
      chart?.update();
    }
  });
});

onUnmounted(() => {
  $socket.off('serverStats');
});
</script>

<style scoped>
canvas {
  height: 400px;
}
</style>

실행하기

# 백엔드
cd server-monitoring-backend
npm run start:dev

# 프론트엔드 (새 터미널)
cd server-monitoring-frontend
npm run dev

http://localhost:3000 접속하면 실시간으로 서버 상태가 업데이트되는 대시보드가 나타난다!

이렇게 간단하게 NestJS의 WebSocket Gateway과 Nuxt 3 + Socket.io-client만으로도 강력한 실시간 모니터링 시스템을 만들 수 있다.
추가로 Prometheus + Grafana를 붙이거나, 여러 서버를 모니터링하고 싶다면 gateway에 room 기능이나 DB 연동하면 확장도 쉽다.
실시간 데이터의 재미를 느껴보고 싶다면 지금 바로 따라 만들어보자!
 
728x90