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 완전 문서화
  * 요청/응답 스키마 및 예시 포함
This commit is contained in:
Hyungi Ahn
2025-11-03 11:05:07 +09:00
parent dea325739a
commit 2b97844ed1
7 changed files with 1270 additions and 14 deletions

View File

@@ -11,17 +11,20 @@
"@simplewebauthn/server": "^13.1.1",
"async-retry": "^1.3.3",
"bcryptjs": "^2.4.3",
"compression": "^1.8.1",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.18.2",
"express-rate-limit": "^7.5.0",
"express-rate-limit": "^7.5.1",
"express-validator": "^7.2.1",
"helmet": "^7.2.0",
"jsonwebtoken": "^9.0.0",
"multer": "^1.4.5-lts.1",
"mysql2": "^3.14.1",
"node-cache": "^5.1.2",
"pm2": "^5.3.0",
"qrcode": "^1.5.4",
"redis": "^5.9.0",
"sqlite3": "^5.1.6",
"swagger-jsdoc": "^6.2.8",
"swagger-ui-express": "^5.0.1"
@@ -394,6 +397,66 @@
"debug": "^4.3.1"
}
},
"node_modules/@redis/bloom": {
"version": "5.9.0",
"resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-5.9.0.tgz",
"integrity": "sha512-W9D8yfKTWl4tP8lkC3MRYkMz4OfbuzE/W8iObe0jFgoRmgMfkBV+Vj38gvIqZPImtY0WB34YZkX3amYuQebvRQ==",
"license": "MIT",
"engines": {
"node": ">= 18"
},
"peerDependencies": {
"@redis/client": "^5.9.0"
}
},
"node_modules/@redis/client": {
"version": "5.9.0",
"resolved": "https://registry.npmjs.org/@redis/client/-/client-5.9.0.tgz",
"integrity": "sha512-EI0Ti5pojD2p7TmcS7RRa+AJVahdQvP/urpcSbK/K9Rlk6+dwMJTQ354pCNGCwfke8x4yKr5+iH85wcERSkwLQ==",
"license": "MIT",
"dependencies": {
"cluster-key-slot": "1.1.2"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/@redis/json": {
"version": "5.9.0",
"resolved": "https://registry.npmjs.org/@redis/json/-/json-5.9.0.tgz",
"integrity": "sha512-Bm2jjLYaXdUWPb9RaEywxnjmzw7dWKDZI4MS79mTWPV16R982jVWBj6lY2ZGelJbwxHtEVg4/FSVgYDkuO/MxA==",
"license": "MIT",
"engines": {
"node": ">= 18"
},
"peerDependencies": {
"@redis/client": "^5.9.0"
}
},
"node_modules/@redis/search": {
"version": "5.9.0",
"resolved": "https://registry.npmjs.org/@redis/search/-/search-5.9.0.tgz",
"integrity": "sha512-jdk2csmJ29DlpvCIb2ySjix2co14/0iwIT3C0I+7ZaToXgPbgBMB+zfEilSuncI2F9JcVxHki0YtLA0xX3VdpA==",
"license": "MIT",
"engines": {
"node": ">= 18"
},
"peerDependencies": {
"@redis/client": "^5.9.0"
}
},
"node_modules/@redis/time-series": {
"version": "5.9.0",
"resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-5.9.0.tgz",
"integrity": "sha512-W6ILxcyOqhnI7ELKjJXOktIg3w4+aBHugDbVpgVLPZ+YDjObis1M0v7ZzwlpXhlpwsfePfipeSK+KWNuymk52w==",
"license": "MIT",
"engines": {
"node": ">= 18"
},
"peerDependencies": {
"@redis/client": "^5.9.0"
}
},
"node_modules/@scarf/scarf": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.4.0.tgz",
@@ -1049,6 +1112,24 @@
"wrap-ansi": "^6.2.0"
}
},
"node_modules/clone": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
"integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==",
"license": "MIT",
"engines": {
"node": ">=0.8"
}
},
"node_modules/cluster-key-slot": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz",
"integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==",
"license": "Apache-2.0",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@@ -1080,6 +1161,54 @@
"resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
"integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag=="
},
"node_modules/compressible": {
"version": "2.0.18",
"resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
"integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
"license": "MIT",
"dependencies": {
"mime-db": ">= 1.43.0 < 2"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/compression": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz",
"integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==",
"license": "MIT",
"dependencies": {
"bytes": "3.1.2",
"compressible": "~2.0.18",
"debug": "2.6.9",
"negotiator": "~0.6.4",
"on-headers": "~1.1.0",
"safe-buffer": "5.2.1",
"vary": "~1.1.2"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/compression/node_modules/debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"license": "MIT",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/compression/node_modules/negotiator": {
"version": "0.6.4",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz",
"integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -1595,9 +1724,10 @@
}
},
"node_modules/express-rate-limit": {
"version": "7.5.0",
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.0.tgz",
"integrity": "sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==",
"version": "7.5.1",
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz",
"integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==",
"license": "MIT",
"engines": {
"node": ">= 16"
},
@@ -1605,7 +1735,7 @@
"url": "https://github.com/sponsors/express-rate-limit"
},
"peerDependencies": {
"express": "^4.11 || 5 || ^5.0.0-beta.1"
"express": ">= 4.11"
}
},
"node_modules/express-validator": {
@@ -1985,6 +2115,7 @@
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/helmet/-/helmet-7.2.0.tgz",
"integrity": "sha512-ZRiwvN089JfMXokizgqEPXsl2Guk094yExfoDXR0cBYWxtBbaSww/w+vT4WEJsBW2iTUi1GgZ6swmoug3Oy4Xw==",
"license": "MIT",
"engines": {
"node": ">=16.0.0"
}
@@ -2833,6 +2964,18 @@
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
"license": "MIT"
},
"node_modules/node-cache": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/node-cache/-/node-cache-5.1.2.tgz",
"integrity": "sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==",
"license": "MIT",
"dependencies": {
"clone": "2.x"
},
"engines": {
"node": ">= 8.0.0"
}
},
"node_modules/node-gyp": {
"version": "8.4.1",
"resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz",
@@ -2949,6 +3092,15 @@
"node": ">= 0.8"
}
},
"node_modules/on-headers": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz",
"integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
}
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -3589,6 +3741,22 @@
"node": ">=8.10.0"
}
},
"node_modules/redis": {
"version": "5.9.0",
"resolved": "https://registry.npmjs.org/redis/-/redis-5.9.0.tgz",
"integrity": "sha512-E8dQVLSyH6UE/C9darFuwq4usOPrqfZ1864kI4RFbr5Oj9ioB9qPF0oJMwX7s8mf6sPYrz84x/Dx1PGF3/0EaQ==",
"license": "MIT",
"dependencies": {
"@redis/bloom": "5.9.0",
"@redis/client": "5.9.0",
"@redis/json": "5.9.0",
"@redis/search": "5.9.0",
"@redis/time-series": "5.9.0"
},
"engines": {
"node": ">= 18"
}
},
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",