오늘은 헬스체크 서버 기본설정하고 서비스 구현하는 작업을 진행했다. 요즘 TCP만 하다가 API 서버 만드려고 하니
막히는 부분이 많아 조금밖에 구현하지 못했다.
Express 서버를 설정하고, 라우터와 에러 핸들러 미들웨어를 설정했다.
import { config } from './config/config.js';
import express from 'express';
import router from './routes/index.router.js';
import errorHandlingMiddleware from './middlewares/error-handling.middleware.js';
const app = express();
app.use(express.json());
app.use('/', router);
app.use(errorHandlingMiddleware);
app.listen(config.server.port, config.server.host, () => {
console.log(`SERVER ON - ${config.server.host}:${config.server.port}`);
});
각 라우트는 인증 미들웨어와 컨트롤러 메서드를 사용하여 요청을 처리한다.
import express from 'express';
import { CheckController } from '../controllers/check.controller.js';
import authMiddleware from '../middlewares/auth.middleware.js';
const router = express.Router();
const checkController = new CheckController();
router.get('/svrStatus', authMiddleware, checkController.getSvrStatus);
router.get('/availableSvr', authMiddleware, checkController.getAvailableSvr);
router.post('/svrStatus', authMiddleware, checkController.setSvrStatus);
export default router;
요청 헤더에서 API 키를 추출하고, 유효성을 검사한 후 다음 미들웨어로 전달한다.
import { checkHashed } from '../utils/auth/checkHashed.util.js';
import CustomErr from '../utils/error/CustomErr.js';
export default async function (req, _, next) {
try {
const { authorization } = req.headers;
if (!authorization)
throw new CustomErr('요청한 사용자의 API-KEY가 없습니다.', 401);
const isValid = await checkHashed(authorization);
if (!isValid)
throw new CustomErr('일치하지 않은 API-KEY 정보입니다.', 401);
next();
} catch (err) {
next(err);
}
}
서버 상태를 저장하고, 유효성을 검사하며, 필요한 경우 에러를 발생시킨다.
import { svrListModel } from '../models/svrList.model.js';
import { svrPortListModel } from '../models/svrPortList.model.js';
import CustomErr from '../utils/error/CustomErr.js';
class checkSvrService {
constructor() {
this.#svrListModel = new svrListModel();
this.#svrPortListModel = new svrPortListModel();
this.lowServer = null;
this.lowCnt = Infinity;
this.portNum = 5000;
}
setSvrStatus(ip, cpuUsage, memUsage, sessionCnt) {
const ipAddress = ip.split('.');
const incorrectIp = ipAddress.some((ip) => ip < 0 || ip > 255);
const incorrectCpu = cpuUsage > 100 || cpuUsage < 0;
const incorrectMem = memUsage > 100 || memUsage < 0;
if (ipAddress.length < 4 || incorrectIp)
throw new CustomErr('아이피 형식이 올바르지 않습니다.', 400);
if (incorrectCpu || incorrectMem)
throw new CustomErr('서버 입력 정보가 올바르지 않습니다.', 400);
if (!this.#svrListModel.has(ip)) this.setSvrPort(ip, this.portNum);
const result = this.#svrListModel.set(ip, { cpuUsage, memUsage, sessionCnt });
if (!result) throw new CustomErr('서버 정보 입력에 실패하였습니다.', 500);
if (cpuUsage <= 90 && memUsage <= 90 && sessionCnt < this.lowCnt) {
this.lowServer = ip;
this.lowCnt = sessionCnt;
}
}
}
export default checkSvrService;
export class svrListModel {
#svrList;
constructor() {
this.#svrList = new Map();
}
get() {
return Object.entries(this.#svrList);
}
has(ip) {
return this.#svrList.has(ip);
}
set(ip, infos) {
this.#svrList.set(ip, infos);
const verification = this.#svrList.get(ip);
if (verification === infos) return true;
}
find(ip) {
return this.#svrList.get(ip);
}
delete(ip) {
if (this.#svrList.has(ip)) this.#svrList.delete(ip);
}
}
export class svrPortListModel {
#svrPort;
constructor() {
this.#svrPort = new Map();
}
getPorts() {
return this.#svrPort.values();
}
find(ip) {
return this.#svrPort.get(ip);
}
set(ip, port) {
this.#svrPort.set(ip, port);
}
delete(ip) {
if (this.#svrPort.has(ip)) this.#svrPort.delete(ip);
}
}
내일중으로 GCP API와 연동해서 인스턴스 자동생성 및 제거 구현할 예정이다.