Files
TK-FB-Project/api.hyungi.net/routes/performanceRoutes.js
Hyungi Ahn 2b97844ed1 feat: 포괄적인 성능 최적화 시스템 구축
- 통합 캐싱 시스템 구축:
  * utils/cache.js: Redis + 메모리 캐시 하이브리드 시스템
  * Redis 연결 실패 시 자동 메모리 캐시 fallback
  * 캐시 키 생성, TTL 관리, 패턴 기반 무효화
  * 캐시 미들웨어 및 무효화 헬퍼 함수

- 데이터베이스 쿼리 최적화:
  * utils/queryOptimizer.js: 쿼리 성능 분석 및 최적화
  * 페이지네이션 헬퍼 (최대 100개 제한)
  * 인덱스 최적화 제안 시스템
  * 배치 삽입 최적화 (100개 단위)
  * 최적화된 쿼리 템플릿 (작업자, 프로젝트, 작업보고서)

- 응답 압축 및 최적화:
  * gzip 압축 미들웨어 (1KB 이상, 레벨 6)
  * 압축 제외 헤더 지원 (x-no-compression)
  * 성능 모니터링 시스템

- 성능 모니터링 API:
  * /api/performance/* 엔드포인트 추가
  * 캐시 통계 및 관리 (조회, 초기화)
  * DB 성능 통계 (연결 수, 슬로우 쿼리)
  * 인덱스 분석 및 최적화 제안
  * 쿼리 실행 계획 분석 (EXPLAIN)
  * 시스템 리소스 모니터링

- 실제 적용 사례:
  * workerController.js에 캐싱 및 페이지네이션 적용
  * 캐시 히트/미스 로깅
  * 캐시 무효화 자동 처리

- 보안 및 권한:
  * 성능 관련 API는 관리자 권한 필요
  * 쿼리 분석은 시스템/관리자만 접근 가능
  * 캐시 초기화는 관리자 전용

- Swagger 문서화:
  * 모든 성능 API 완전 문서화
  * 요청/응답 스키마 및 예시 포함
2025-11-03 11:05:07 +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;