개요
es6 형 클래스 생성을 위한 Node.js 모듈 생성 데이터베이스 테이블의 열 이름으로 나열된 열 이름으로 DTO 클래스 생성하는 경우 사용!
물론 이 클래스를 이용하지 않아도 될 경우가 좀더 JS 형식에 맞는 형태 일 수 있으나 필요하여 다음의 조건을 만족하는 클래스 생성기를 만들어 본다.
물론 JS 특성상 구지 클래스를 만들지 않아도 객체 자체를 사용해도 되는 경우가 대부분이다. 하지만! DB 를 다루는 서버 입장에서는 DTO, DAO, SERVICE, CONTROLLER 등으로 계층별로 다룰때 DTO 객체를 정의해서 정형 데이터베이스로 부터 데이터를 처리하는 경우가 많이 있었다.(물론 이 구조가 올바른지는…)
이때 형식이 결정되어 있는 DTO 객체 사용하는 경우에 한하여
이때 쓰이는 class 를 자동 생성
요구사항
- Node.js 기반으로 js 스크립트에서 실행가능하며 require 또는 import 이용하여 로딩 할 수 있도록 함
- 함수 호출 하여 사용하도록 한다. 함수 호출 방식은 다음과 같다.
- 입력: 클래스 이름, 데이터베이스 테이블의 열이름 문자열
ex) 클래스명: Person,
필드명: ID, NAME, GENDER, REG_DATE - 출력: 이 조건들을 이용하여 만든 클래스 파일
class Person extends Model { constructor(object){ this._id = obejct[Person.FIELD_ID]; this._name = obejct[Person.FIELD_NAME]; this._gender = obejct[Person.FIELD_GENDER]; this._reg_date = obejct[Person.FIELD_REG_DATE]; } /* STATIC FIELD NAME*/ static get FIELD_ID = 'ID'; static get FIELD_NAME = 'NAME'; static get FIELD_GENDER = 'GENDER'; static get FIELD_REG_DATE = 'REG_DATE'; /*getter & setter*/ get id() { return id; } set id(id) { this._id = _id; } get name() { return this._name; } set name(name) { this._name=_name; } get gender() { return this._sex; } set gender(sex) { this._sex = sex; } get red_date(reg_date) { return this._reg_date; } set reg_date(reg_date) { this._reg_date = reg_date; } }
구현
- 위 요구사항을 만족하기 위해 다음과 같이 구현한다.
- 폴더구조
- 템플릿
- genDTO index.js
폴더 구조
- 폴더 구조는 다음과 같다.
genDTO : 최상위 폴더
├── index.js : 모듈 index.js
└── template : 템플릿 양식 저장
└── es6ClassType1-forRDB.txt : Relational Database DTO 생성을 위한
템플릿
- 템플릿은 %s 와 같은 형식 지정자로 구성하여 다음을 구성한다.
es6ClassType1-forRDB.txt
/* eslint-disable no-underscore-dangle,camelcase */
const Model = require('./Model');
const async = require('async');
class %s extends Model {
constructor(object) {
super(object);
if (object) {
%s
}
}// constructor()
/* STATIC FIELD NAMES */
%s
/* getter & setter */
%s
}// end of class
module.exports = %s;
|;|this._%s = object[%s.FIELD_%s];
|;|static get FIELD_%s() { return '%s'; }
|;|get %s() { return this._%s; }
set %s(value){ this._%s = value; }
-
마지막 부분은 각각 설명하면 다음과 같다.
- this.%s = object[%s.FIELD%s] : 생성자 내부 필드 지정 부분
- static get FIELD_%s() { return ‘%s’; } : static field 필드 템플릿
- get %s() { return this.%s; } : getter 템플릿
set %s(value){ this.%s = value; } : setter 템플릿
-
순서에 유의하여 변환 변수 대입하여 js 용 sprintf 에 대입하여 처리하도록 하자! 순서대로 잘 넣어야 한다.
-
여기서 Model extends 부분의 클래스는 다음과 같다.
Model.js
const logger = require(`${__dirname}/../../lib/logger`)();
/**
* SMC PRO 클래스
*/
class Model {
/**
* SMCPRO 생성자
* @param object
*/ constructor(object) {
}
/**
* 필드 이름
* @param fieldName
* @returns {*}
*/
get(fieldName) {
return this[`_${fieldName.toLowerCase()}`];
}
/**
* * @param fieldName
* @param value
*/
set(fieldName, value) {
this[`_${fieldName.toLowerCase()}`] = value;
}
static fields(Class) {
const arrFields = [];
let fields = null;
if (Class) {
fields = Object.getOwnPropertyNames(Class);
for (const idx in fields) {
if (fields[idx].indexOf('FIELD') !== -1) {
arrFields.push(Class[fields[idx]]);
} } } else {
fields = Object.getOwnPropertyNames(this);
for (const idx in fields) {
if (fields[idx].indexOf('FIELD') !== -1) {
arrFields.push(this[fields[idx]]);
} } } return arrFields;
}
values(Class) {
const arrValues = [];
const fields = Object.getOwnPropertyNames(Class);
for (const idx in fields) {
if (fields[idx].indexOf('FIELD') !== -1) {
arrValues.push(this.get(Class[fields[idx]]));
} }
return arrValues;
}
checkFields(fields) {
let isCheckResult = true;
for (const idx in fields) {
if (this.get(fields[idx]) === null || this.get(fields[idx]) === undefined) {
isCheckResult = false;
break;
} } return isCheckResult;
}}// end of class
module.exports = Model;
- Model.js의 class Model 는 다음 메소드를 미리 구현해 두었다.
- get(fieldName) : 하위 클래스에 FIELD 로 정의된 정적 변수를 이용하여 실제 객체 내용에 접근하여 값 리턴
- set(fieldName, value) : 하위 클래스에 FIELD 로 정의된 정적 변수를 이용하여 실제 객체 내용에 접근하여 값 대입
- 그 밖에 기본 클래스 다룰때 필요한 메소드 구현 해둠
genDTO index.js
- 이 모듈에서 사용하는 의존성 패키지는 다음과 같다.
const sprintf = require('sprintf-js').sprintf;
const fs = require('fs');
const dtoClassFieldsName = [];
const init = function () {
const templateData = fs.readFileSync(`${__dirname}/template/es6ClassType1-forRDB.txt`, 'utf-8').split('|;|');
const templateClassTotal = templateData[0];
const templateFieldDefiniation = templateData[1];
const templateStaticFieldDefiniation = templateData[2];
const templateGetterSetterDefiniation = templateData[3];
// console.log('template: ', templateData);
function GenDTO() {
// 객체 생성
}
const instance = new GenDTO();
instance.genClass = function (className, fieldNames, dtoDirPath) {
console.log('인자 검사 수행');
if (!className || !fieldNames) {
throw new Error('Parameters Error');
} else if (!Array.isArray(fieldNames) && typeof fieldNames !== 'string') {
throw new Error('Parameters Error - fieldNames type invalid');
} console.log('인자 전처리 작업');
if (typeof fieldNames === 'string') {
fieldNames = fieldNames.split(',');
fieldNames.forEach(function (elem, idx) {
fieldNames[idx] = elem.trim();
}); } if (!dtoDirPath) {
dtoDirPath = './';
} // 처리작업 수행
console.log('클래스 생성 ... 시작');
const result = instance.doGenClass(className, fieldNames);
fs.writeFileSync(`${dtoDirPath}/${className}.js`, result);
console.log('클래스 생성 ... 완료');
};
instance.doGenClass = function (className, fieldNames) {
const classNameResult = className;
const fieldDefinitionResult = instance.doGenFieldDefinition(className, fieldNames);
const staticFieldDefinitionResult = instance.doGenStaticFieldDefinition(className, fieldNames);
const getterSetterDefinitionResult = instance.doGenGetterSetterDefinition(className, fieldNames);
return sprintf(templateClassTotal, classNameResult, fieldDefinitionResult, staticFieldDefinitionResult, getterSetterDefinitionResult, classNameResult);
};
instance.doGenFieldDefinition = function (className, fieldNames) {
// console.log('doGenFieldDefinition');
// console.log('className>>', className); // console.log('fieldNames>>', fieldNames); let result = '';
let tabHead = '';
fieldNames.forEach((elem, idx) => {
result += sprintf(`${tabHead}${templateFieldDefiniation}`, elem.toLowerCase(), className, elem);
if (idx === 0) {
tabHead = ' ';
} }); return result;
};
instance.doGenStaticFieldDefinition = function (className, fieldNames) {
// console.log('doGenStaticFieldDefinition');
// console.log('className>>', className); // console.log('fieldNames>>', fieldNames); let result = '';
let tabHead = '';
fieldNames.forEach((elem, idx) => {
result += sprintf(`${tabHead}${templateStaticFieldDefiniation}`, elem, elem);
if (idx === 0) {
tabHead = ' ';
} }); return result;
};
instance.doGenGetterSetterDefinition = function (className, fieldNames) {
// console.log('doGenStaticFieldDefinition');
// console.log('className>>', className); // console.log('fieldNames>>', fieldNames); let result = '';
let tabHead = '';
fieldNames.forEach((elem, idx) => {
result += sprintf(`${tabHead}${templateGetterSetterDefiniation}`, elem.toLowerCase(), elem.toLowerCase(), elem.toLowerCase(), elem.toLowerCase());
if (idx === 0) {
tabHead = ' ';
} }); return result;
};
return instance;
};
module.exports = init();
배포
- git: TBD
- 이 부분 Model.js 의존 관계 해결할 경우 수행 하도록 하겠습니다.
결론
- 이러한 방식을 통해 차후 프로젝트 생성 내가 주로 하는 패턴 구성으로 각종 설정만 달리하여 구성 요소 자동 생성하는 프로젝트 생성기로 확장해도 될 것 같댜.
(필요하다 시급히!! 매번 직접 설치 후 설정 변경 너무 힘들고 귀찮아…)- 이쯤에서 다른 프로젝트 생성기에서 나오는 express-generator 속을 보고 싶다.
- Model 부분은 상속받는 코드도 마찬가지로 생성해주는 기능이 있어야 할 것 같다.
- es6에서 인정하는 객체의 정의가 불충분 할 것 같은데… 이부분 알아보자
- 객체의 조건: 생성자, 소멸자, 복사 생성자, 대입연산자, getter&setter, 비교연산자 오버라이딩 등(C++ 기준), 더 필요한 것은 없을까?
- 성능에 대해서는 고려하지 않고 내 편한대로 우선 만든 것.
- 차후 안정화와 사용에 문제가 전혀 되지 않으면 나중에는 npm으로 배포 도전하겠다.
댓글 없음:
댓글 쓰기