Files
Hyungi Ahn 2b1c7bfb88 feat: 다수 기능 개선 - 순찰, 출근, 작업분석, 모바일 UI 등
- 순찰/점검 기능 개선 (zone-detail 페이지 추가)
- 출근/근태 시스템 개선 (연차 조회, 근무현황)
- 작업분석 대분류 그룹화 및 마이그레이션 스크립트
- 모바일 네비게이션 UI 추가
- NAS 배포 도구 및 문서 추가

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 14:41:01 +09:00

389 lines
12 KiB
JavaScript

/**
* @swagger
* tags:
* name: Performance
* description: 성능 모니터링 및 최적화 API
*/
const express = require('express');
const router = express.Router();
const { asyncHandler } = require('../utils/errorHandler');
const cache = require('../utils/cache');
const { getPerformanceStats, suggestIndexes, analyzeQuery } = require('../utils/queryOptimizer');
/**
* @swagger
* /api/performance/cache/stats:
* get:
* tags: [Performance]
* summary: 캐시 통계 조회
* description: 현재 캐시 시스템의 상태와 통계를 조회합니다.
* security:
* - bearerAuth: []
* responses:
* 200:
* description: 캐시 통계 조회 성공
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* message:
* type: string
* example: "캐시 통계 조회 성공"
* data:
* type: object
* properties:
* type:
* type: string
* example: "memory"
* connected:
* type: boolean
* example: true
* keys:
* type: integer
* example: 42
* hits:
* type: integer
* example: 150
* misses:
* type: integer
* example: 25
* hitRate:
* type: number
* example: 0.857
* 401:
* description: 인증 필요
* 500:
* description: 서버 오류
*/
router.get('/cache/stats', asyncHandler(async (req, res) => {
const stats = cache.getStats();
res.success(stats, '캐시 통계 조회 성공');
}));
/**
* @swagger
* /api/performance/cache/flush:
* post:
* tags: [Performance]
* summary: 캐시 초기화
* description: 모든 캐시 데이터를 삭제합니다. (관리자 전용)
* security:
* - bearerAuth: []
* responses:
* 200:
* description: 캐시 초기화 성공
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* message:
* type: string
* example: "캐시가 성공적으로 초기화되었습니다."
* 401:
* description: 인증 필요
* 403:
* description: 권한 부족
* 500:
* description: 서버 오류
*/
router.post('/cache/flush', asyncHandler(async (req, res) => {
// 관리자 권한 확인
if (req.user?.access_level !== 'admin' && req.user?.access_level !== 'system') {
return res.status(403).json({
success: false,
error: '캐시 초기화 권한이 없습니다.'
});
}
await cache.flush();
res.success(null, '캐시가 성공적으로 초기화되었습니다.');
}));
/**
* @swagger
* /api/performance/database/stats:
* get:
* tags: [Performance]
* summary: 데이터베이스 성능 통계
* description: 데이터베이스 연결 상태와 성능 지표를 조회합니다.
* security:
* - bearerAuth: []
* responses:
* 200:
* description: DB 성능 통계 조회 성공
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* message:
* type: string
* example: "DB 성능 통계 조회 성공"
* data:
* type: object
* properties:
* connections:
* type: object
* properties:
* current:
* type: integer
* example: 5
* max:
* type: integer
* example: 151
* slowQueries:
* type: integer
* example: 0
* timestamp:
* type: string
* format: date-time
* 401:
* description: 인증 필요
* 500:
* description: 서버 오류
*/
router.get('/database/stats', asyncHandler(async (req, res) => {
const stats = await getPerformanceStats();
res.success(stats, 'DB 성능 통계 조회 성공');
}));
/**
* @swagger
* /api/performance/database/indexes/{tableName}:
* get:
* tags: [Performance]
* summary: 인덱스 최적화 제안
* description: 특정 테이블의 인덱스 상태를 분석하고 최적화 제안을 제공합니다.
* security:
* - bearerAuth: []
* parameters:
* - in: path
* name: tableName
* required: true
* schema:
* type: string
* description: 분석할 테이블명
* example: "workers"
* responses:
* 200:
* description: 인덱스 분석 성공
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* message:
* type: string
* example: "인덱스 분석 완료"
* data:
* type: object
* properties:
* tableName:
* type: string
* example: "workers"
* currentIndexes:
* type: array
* items:
* type: object
* properties:
* name:
* type: string
* column:
* type: string
* unique:
* type: boolean
* suggestions:
* type: array
* items:
* type: object
* properties:
* type:
* type: string
* column:
* type: string
* reason:
* type: string
* sql:
* type: string
* 401:
* description: 인증 필요
* 500:
* description: 서버 오류
*/
router.get('/database/indexes/:tableName', asyncHandler(async (req, res) => {
const { tableName } = req.params;
const analysis = await suggestIndexes(tableName);
res.success(analysis, '인덱스 분석 완료');
}));
/**
* @swagger
* /api/performance/query/analyze:
* post:
* tags: [Performance]
* summary: 쿼리 성능 분석
* description: SQL 쿼리의 실행 계획과 성능을 분석합니다. (관리자 전용)
* security:
* - bearerAuth: []
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required:
* - query
* properties:
* query:
* type: string
* example: "SELECT * FROM workers WHERE department = ?"
* params:
* type: array
* items:
* type: string
* example: ["생산부"]
* responses:
* 200:
* description: 쿼리 분석 성공
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* message:
* type: string
* example: "쿼리 분석 완료"
* data:
* type: object
* properties:
* executionTime:
* type: integer
* example: 15
* explainResult:
* type: array
* items:
* type: object
* recommendations:
* type: array
* items:
* type: object
* properties:
* type:
* type: string
* message:
* type: string
* suggestion:
* type: string
* 401:
* description: 인증 필요
* 403:
* description: 권한 부족
* 500:
* description: 서버 오류
*/
router.post('/query/analyze', asyncHandler(async (req, res) => {
// 관리자 권한 확인
if (req.user?.access_level !== 'admin' && req.user?.access_level !== 'system') {
return res.status(403).json({
success: false,
error: '쿼리 분석 권한이 없습니다.'
});
}
const { query, params = [] } = req.body;
if (!query) {
return res.status(400).json({
success: false,
error: '분석할 쿼리가 필요합니다.'
});
}
const analysis = await analyzeQuery(query, params);
res.success(analysis, '쿼리 분석 완료');
}));
/**
* @swagger
* /api/performance/system/info:
* get:
* tags: [Performance]
* summary: 시스템 정보 조회
* description: 서버의 메모리, CPU, 업타임 등 시스템 정보를 조회합니다.
* security:
* - bearerAuth: []
* responses:
* 200:
* description: 시스템 정보 조회 성공
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* example: true
* message:
* type: string
* example: "시스템 정보 조회 성공"
* data:
* type: object
* properties:
* uptime:
* type: number
* example: 3600.5
* memory:
* type: object
* properties:
* rss:
* type: integer
* heapTotal:
* type: integer
* heapUsed:
* type: integer
* external:
* type: integer
* nodeVersion:
* type: string
* example: "v18.17.0"
* platform:
* type: string
* example: "linux"
* 401:
* description: 인증 필요
* 500:
* description: 서버 오류
*/
router.get('/system/info', asyncHandler(async (req, res) => {
const systemInfo = {
uptime: process.uptime(),
memory: process.memoryUsage(),
nodeVersion: process.version,
platform: process.platform,
cpuUsage: process.cpuUsage(),
timestamp: new Date().toISOString()
};
res.success(systemInfo, '시스템 정보 조회 성공');
}));
module.exports = router;