hetamvc
v0.3.38
Published
Backend mvc framework for node expressJs
Downloads
28
Readme
HetaMvc
HetaMvc is MVC back-end framework that consist of expressJs in NodeJs based
Install it with the command below
$ npm i -save hetamvc
아래 문구를 app.js등의 시작 js에 추가해서 실행해 줍니다.
require('hetamvc').init({
scanPath:app_route, //scan path (required)
route:express, //express route (required)
//socket:socketIo, //socket object
//sequelize:require('./sequelize.config.js'), //DB
//mongodb:require('./mongodb.config.js'), //Mongo
//email:require('./email.config.js'), //Email
//locale:require('./locale.config.js'), //Locale
//constants:require('./app.config.js'), //Global Value
//logger:require('./logger.config.js'), //logging
//uploadPath:appConfig._upload.path //File Upload path
//forceAwait: true //force await mode
//rootPath: __dirname //강제 경로 app.js를 외부에서 호출시 import한 값의 경로가 바뀔수 있음
});
scanPath : route path
파일의 최상위 경로
(필수)
route : express objectconst express = express();
(필수)
socket : websocket objectconst socketIo = require('socket.io')(serv,{});
forceAwait : true일때 모든 file에 비동기 필요하면 자동으로 async await를 붙인다.
sequelize : sequelize objectrequire('./sequelize.config.js')
//sequelize.config.js (mysql)
module.exports = {
dialect: "mysql",
username: "root",
password: "xxxxxxx",
passKey: "Encrypt_Key", //Password encryption key
database: "dbname",
host: "localhost",
port: 3306,
logging: true,
force: false,
operatorsAliases: false,
timezone: "-08:00",
dialectOptions: {
charset: 'utf8mb4',
dateStrings: true,
typeCast: true
},
define: {
timestamps: false
},
pool:{
max:20,
min:5,
idle:10000
}
};
//sequelize.config.js (sqlite)
module.exports = {
dialect: "sqlite",
force: false, //강제 테이블변경
operatorsAliases: false,
dialectOptions: {
charset: 'utf8mb4',
dateStrings: true,
typeCast: true
},
define: {
freezeTableName: true, //테이블에s 제거
timestamps: false //false : 자동으로 CreatedAt, UpdatedAt 방지
},
pool:{
max:20,
min:5,
idle:10000
},
storage: __dirname+"/database.sqlite" // default Sequelize('sqlite::memory:');
};
mongodb : mongodb 연결시 설정해줌
require('./mongodb.config.js')
//mongodb.config.js
module.exports = {
username: "",
password: "",
passKey: "Encrypt_Key", //Password encryption key
database: "test",
host: "localhost",
port: 27017,
dialect: "mongodb",
options: {
useNewUrlParser: true,
useUnifiedTopology: true, //pool
serverSelectionTimeoutMS: 10000,
}
/* mongodb transaction을 사용할때 활성화
,replicaSet: {
name : "bgarage_replica",
hosts: [ // Primary를 제외한 나머지 기술
{
host: "bgaragemongo_slave",
port: 27118
},
{
host: "bgaragemongo_arbiter",
port: 27119
}
]
}
*/
};
constants : constants object 초기 상수값
require('./app.config.js')
//app.config.js
const path = require('path');
module.exports = {
_email:{
serverUrl: "http://{IP}"
},
_ip:'',
_root: __dirname,
_upload:{
path: path.resolve(__dirname, './upload')
}
};
email : email사용하기 위해 smtp설정
require('./email.config.js')
//email.config.js
module.exports = {
smtp: "smtp.naver.com",
port: 587,
secure: false,
user: "xxxxx",
pass: "04ec1b40be3a75a12ae522cc38affab8", //Passwords created using @Encrypt can be created in the documentation referenced.
passKey: "Encrypt_Key", //Password encryption key
from: "[email protected]" //Pin the sending user
};
logger : winston logger사용하기 위해 설정
require('./logger.config.js')
//logger.config.js
module.exports = {
level: "debug", //error:0, warn:1, info:2, verbose:3, debug:4, silly:5
output:['console','file'], //default 'console'
dir: 'logs' //file directory
};
locale : 다국어 사용을 위해 i18n설정
require('./locale.config.js')
//locale.config.js
module.exports = {
locales:['en', 'ko'], //사용언어 설정 / 'de' 나 'ja' , 'fr' 등등 추가 가능
directory: '/locales', // 사용언어에 대한 템플릿폴더 생성위치,
defaultLocale: 'en', //기본 사용언어 설정
cookie: 'lang', //쿠키의 이름 설정, 개발자가 자유롭게 이름 설정가능
autoReload: true,
//updateFiles: false, //없는 param을 있으면 json파일을 변경한다
//syncFiles: false, //로케일 정보 동기화
objectNotation:true
};
//en.json
{
"home": {
"title": "test {{aa}}"
}
}
File 구성
파일은 Component, Controller, Service, Model 이렇게 4가지 형태의 @Annotation으로 구분해주며
모든 파일은 Common
@Annotation을 호출 할 수 있습니다.
Common
@Annotation
변수로만 할당하며 모두 contructor안에서 정의한다.
호출하는 객체는 모두 async await 처리를 자동으로 해준다./** @NoAutoAwait () */ class Test{ constructor(){ /** @Inject ('SettingMasterService') */ this.SettingMasterService; /** @Websocket ('/ns') */ this.websocket; /** @Sequelize () */ this.sequelize; /** @Request () */ this.request; /** @Render () */ this.render; /** @Route () */ this.route; /** @Constants () */ this.constants; /** @Encrypt ('Encrypt_Key') */ this.encrypt; /** @Email () */ this.email; /** @Locale () */ this.i18n; /** @Logger () */ this.log; } /** @Starter () */ start(){ console.log('Component/Service/Controller Started.'); } Websocket(){ this.websocket.emit('send data to all'); for(let item of this.websocket.sockets){ let socketId = item[0]; let socket = item[1]; socket.emit('User Id :',socket.userId || 'Anonymous'); } } Sequelize(){ var param = {sDate: sDate, eDate: eDate}; let s = `SELECT * FROM TEST`; let resultList = this.sequelize.query(s,{ type:this.sequelize.QueryTypes.SELECT,raw:true, replacements: param }); } Request(){ try{ var OPTIONS = { uri: 'http://test.com/rest/testinsert', method: 'PUT', //POST json: true, //response type headers: { 'Content-Type': 'application/json', 'x-token': "mytoken" }, body: { "id": "test_id", "name": "test_name" } }; const response = this.request(OPTIONS); console.log(response); }catch(e){ console.log('ERROR :',e); //return e; // throw exception } } Render(){ const str = this.render(this.constants._root+"/templates/email.html", { param1: "title", param2: "contents", param3: "copyright" }); } Route(){ this.route.get('/some/url',function(req,res,next){ res.send('this is dynamic router'); }); } Constants(){ console.log(this.constants._root); // the data in app.config.js } Encrypt(){ let enc = this.encrypt.encode('testEncrypt'); console.log(enc); //14ec1b40be3a75a12ae522cc38affab8 console.log(this.encrypt.decode(enc)); //testEncrypt } Email(){ try{ let mailOptions = { //from: '[email protected]', //single to: '[email protected],[email protected]', //multiple subject: 'some title', //title html: '<p> some contents </p>' //contents }; let result = this.email.send(mailOptions); console.log("Finish sending email : " + result.response); }catch(e){ console.log(e.response); //return e; // throw exception } } Locale(){ console.log(this.i18n.getLocale()); //read default 'en' this.i18n.setLocale('en'); //set lang res.cookie('lang', 'en'); //set lang - same as above // http://host/lang/en //set lang - same as above console.log(this.i18n.__('home.title',{aa:'test'}); //translate back-end //<%= __('sample_title')/*Title*/%> //translate front-end //Using the en.json file when call in the VUE } Logger(){ this.log.debug('debug test'); this.log.info('info test'); this.log.error('error test'); } }
@Inject('Service or Model')
@Service 또는 @Model객체를 변수에 할당@Websocket('/ns')
socketIO객체를 가져옴 namespace(option)로 구분@Sequelize('')
this.sequelize.query()로 query를 바로 호출한다.@Request
외부의 http url을 호출한다.@Render
메일 발송같은 임의로 ExpressJs Render(ejs parsing)를 한다.@Route
ExpressJs 객체를 사용한다. (get,post,delete등을 동적 설정)@Constants
초기에 입력받은 상수 값을 사용 할 수 있다.(config)@Encrypt
문서의 암/복호화를 사용할 수 있다.@Email
초기에 입력받은 Smtp설정으로 간편하게 이메일을 사용 한다.(config)@Locale
다국어 객체를 받음. en.json,ko.json 등의 파일을 사용한다.(config)@Logger
winston logger를 사용 (config)@Starter
시스템이 시작될때 Method를 onload해준다.@ForceAwait
Class 상단에 사용하며 forceAwait == false 일때 자동으로 async와 await를 붙인다.@NoForceAwait
Class 상단에 사용하며 forceAwait == true 일때 자동으로 async와 await를 붙이지 않는다.@Component
File- 공통소스 , 배치 , Filter 등의 용도로 사용합니다.
- class상단에
@Component
를 추가해야합니다. - 모든 @Annotation은
Comment
로 추가해야 합니다.
/** @Component */ export class FilterComponent{ constructor(){} /** @Starter () */ start(){ console.log('component started.'); } // @FilterMapping ('/*') SessionInterceptor(req,res,next){ res.header('X-XSS-Protection' , 0 ); console.log('페이지 호출전에 이 function을 선행 호출'); return next(); } // @Scheduler ('0 */10 * * * *') every10min(){ console.log('10분마다 호출됨.'); } // @Scheduler ('500') every10min(dt, d){ console.log('0.5초 마다 호출됨.'); } /** @Socket ('message','/ns') */ socketMessage(data,socket){ socket.userId = data.userId; console.log('Client에서 socket.emit("message",{}); 로 호출함'); socket.emit('log','Client said : '+data); socket.broadcast.emit('message',data); //나빼고 전체발송 } /** @SocketConnect ('/ns') */ connect(socket){ console.log("socket connection : "+socket.id); const roomId = socket.request.session.roomId; //session읽기 return {roomId:roomId}; } /** @SocketDisconnect ('/ns') */ disconnect(socket, connectReturn){ console.log(connectReturn); //위connect에서 return한 값 console.log("socket disconnected : "+socket.id); } /** @Cacheable ('key', '#vo.id') */ getList(vo){ return this.commonBoardMapper.getOptList(vo); } /** @CacheEvict ('key', '#vo.id') */ insertVo(vo){ return true; } /** @CachePut ('key', '#vo.id') */ updateVo(vo){ return true; } }
Component에서 사용 할수 있는 Method용 Annotation목록 입니다.
// @FilterMapping ('/*')
: request 호출의 선행자로 동작// @Starter()
: node실행할때 선행 실행된다.// @Scheduler ('0 */10 * * * *')
: cron을 이용한 Scheduler// @Socket ('emit name',ns)
: server에서 socket request처리, ns는 옵션// @SocketConnect (ns)
: socket 사용자 접속시, ns는 옵션// @SocketDisconnect (ns)
: socket 사용자 분리시, ns는 옵션// @Cacheable (key, '#param.id')
: 캐시를 사용한다. 1회로드 하고 리턴받은 값을 다음 호출시 전달, ()는 Class+Method로 생성// @CacheEvict (key, '#param.id')
: 캐시 삭제, (key)만 입력하면 하위 캐시 모두 제거// @CachePut (key, '#param.id')
: 캐시 업데이트, 무조건 리턴값을 캐싱한다.
@Controller
File- Express router 설정을 annotation으로 진행 합니다.
- class상단에
@Controller
를 추가해야합니다. - 모든 @Annotation은
Comment
로 추가해야 합니다.
/** @Controller */ /** @RequestMapping ('/v1') */ export class LoginController{ constructor(){} /** @Starter () */ start(){ console.log('controller started.'); } /** @RequestMapping ('/login/:id',get) */ login_get(req,res,next){ let userid = req.params.id; res.render('account/login',{userid}); } /** @RequestMapping ('/login',post) */ login_post(req,res,next){ try{ let userid = req.body.param1; console.log("express 문법을 따릅니다."); res.status(200).json({result:"SUCCESS"}); //put:201 }catch(e){ res.status(500).json({result:e}); } } /** 여러개 파일 업로드 (uploadPath필수) * @anno1 : 경로 * @anno2 : method type (files 고정값) -> (req.files : 파일정보) * @anno3 : input의 name attribute 예) <input type="file" name="image"/> */ /** @RequestMapping ('/imagesubmit',files,'image') */ imageuploadSubmit(req,res,next){ try{ for(var i in req.files){ const fileInfo = req.files[i]; console.log('이미 uploadPath에 파일은 저장됨.'); console.log('파일정보.(filepath:/202101/20/xx)',fileInfo); this.service.insertFileManager(fileInfo); } res.json({result:'SUCCESS'}); }catch(e){ console.log(e); res.json({errorMsg:e}); } } /** 1개파일만 업로드 (uploadPath필수) * @anno1 : 경로 * @anno2 : method type (file 고정값) -> (req.file : 파일정보) * @anno3 : input의 name attribute 예) <input type="file" name="layout"/> */ /** @RequestMapping ('/imagesubmit',file,'layout') */ singleFileUploadSubmit(req,res,next){ try{ const fileInfo = req.file; console.log('이미 uploadPath에 파일은 저장됨.'); console.log('파일정보.(filepath:/202101/20/xx)',fileInfo); this.service.insertFileManager(fileInfo); res.json({result:'SUCCESS'}); }catch(e){ console.log(e); res.json({errorMsg:e}); } } }
- Controller에서만 사용 할수 있는 Method용 Annotation목록 입니다.
// @Starter()
: node실행할때 선행 실행된다.// @RequestMapping ('/v1')
: class 밖의 RequestMapping은 전역 경로// @RequestMapping ('/login',post)
: 전역과 더해진 router
( method list: get, post, delete, put, patch, files, file, all, use )
@Service
File- Service 상호간의 Inject가 가능 하도록 설계된 모듈
- class상단에
@Service
를 추가해야합니다. - 모든 @Annotation은
Comment
로 추가해야 합니다. - @Inject annotation으로 Component, Controller, Service에 추가 될수 있습니다.
- @Inject 할때 Service또는 Model을 inject한다. Service는 2depth까지 Inject가능
/** @Service */ class CommBoardService{ constructor(){ /** @Inject ('CommBoardMapper') */ this.commonBoardMapper; } /** @Starter () */ start(){ console.log('service started.'); } // @Scheduler ('0 */10 * * * *') every10min(){ console.log('10분마다 호출됨.'); } // @Scheduler ('500') every10min(dt, d){ console.log('서버가 시작되면서 0.5초 마다 호출됨.'); } /** * Add List * @param vo : req.body */ /** @Transactional () */ //default : 'REQUIRED' setList(vo){ const cnt = this.commonBoardMapper.getCount(vo); return true; } /** @Transactional ('REQUIRES_NEW','mongoose') */ setList(vo){ //transaction type of mongodb this.commonBoardMapper.setList(vo, cnt); return true; } /** @Transactional ('','sequelize') */ setList(vo){ //transaction type of sequelize this.commonBoardMapper.setOptList(vo, cnt); return true; } /** @Cacheable ('key', '#vo.id') */ getList(vo){ return this.commonBoardMapper.getOptList(vo); } /** @CacheEvict ('key', '#vo.id') */ insertVo(vo){ return true; } /** @CachePut ('key', '#vo.id') */ updateVo(vo){ return true; } }
- Service에서 사용 할수 있는 Method용 Annotation 입니다.
// @Transactional (['REQUIRED','REQUIRES_NEW','NESTED','SUPPORTS','MANDATORY','NOT_SUPPORTED','NEVER'],['mongoose','sequlize'])
: DB Transaction처리// @Starter()
: node실행할때 선행 실행된다.// @Scheduler ('0 */10 * * * *')
: cron을 이용한 Scheduler// @Socket ('emit name',ns)
: server에서 socket request처리, ns는 옵션// @SocketConnect (ns)
: socket 사용자 접속시, ns는 옵션// @SocketDisconnect (ns)
: socket 사용자 분리시, ns는 옵션// @Cacheable (key, '#param.id')
: 캐시를 사용한다. 1회로드 하고 리턴받은 값을 다음 호출시 전달, ()는 Class+Method로 생성// @CacheEvict (key, '#param.id')
: 캐시 삭제, (key)만 입력하면 하위 캐시 모두 제거// @CachePut (key, '#param.id')
: 캐시 업데이트, 무조건 리턴값을 캐싱한다.
( 여러 DB를 동시 사용시 mongoose, sequelize를 수동으로 지정 해야함 )
@Model
File- Database(mongodb) 연결설정 Annotation입니다.
- class상단에
@Model
를 추가해야합니다. - 모든 @Annotation은
Comment
로 추가해야 합니다. - @Inject annotation으로 Component, Controller, Service에 추가 될수 있습니다.
- mode항목에 sequelize 또는 mongodb를 입력해서 구분
- Sequelize
import {HetaSequelize} from 'hetamvc' /** @Model */ export default class FileManager extends HetaSequelize{ init(Sequelize, options) { //options.indexes = [{ unique: false, fields: ['fileGroup']}]; return this.model('filemanager', { id: {type: Sequelize.INTEGER.UNSIGNED, primaryKey: true, autoIncrement: true}, fileGroup: {type: Sequelize.INTEGER.UNSIGNED,defaultValue: 0}, fileName: {type: Sequelize.STRING(200)}, filePath: {type: Sequelize.STRING(100)}, mimeType: {type: Sequelize.STRING(20)}, fileSize: {type: Sequelize.INTEGER.UNSIGNED,defaultValue: 0}, createUser: {type: Sequelize.STRING(16), allowNull: true}, createdAt: {type: 'TIMESTAMP', defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'), allowNull: false} }, options ); } }
- MongoDB
import {HetaMongoose} from 'hetamvc' /** @Model */ export default class FileManager extends HetaMongoose{ init(mongoose) { let schema = new mongoose.Schema({ id: { type: Number, required: true, unique: true }, fileGroup: {type: Number, default: 0}, fileName: { type: String, required: true }, filePath: { type: String, required: true }, mimeType: { type: String, required: false }, fileSize: {type: Number,default: 0}, createUser: {type: String}, createdAt: { type : Date, default: Date.now } //timestamp },{ _id: true, timestamps: true, collection: this.name //테이블명을 classname으로 }); return this.model(this.name, schema); } }