관리 메뉴

SIMPLE & UNIQUE

4회차_3강 : NODE 스케줄러를 개발해, EC2 서버에 배포한다. 본문

탈잉 강의 자료/react.js(프론트) + node.js(백앤드) 개발에서 배포까지

4회차_3강 : NODE 스케줄러를 개발해, EC2 서버에 배포한다.

착한코딩 2020. 1. 28. 00:10

4_3 목표 : NODE 코드로 스케줄러를 작성하고, 실행시 로깅되도록 한다. 정상동작 확인 후, EC2 서버에 코드를 배포한다.

 

1. cron을 사용해 스케줄러를 구현한다.

  server.js에 batch 라우터를 추가한다.

var express = require('express');

var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var swtoolRouter = require("./routes/SwtoolRout");
var fileuploadRouter = require("./routes/UploadRout");
var BatchRout = require("./routes/BatchRout");

var app = express();

app.use('/', indexRouter);
app.use('/users', usersRouter);

//sw Tool 조회
app.use("/api/Swtool", swtoolRouter);

//파일 업로드
app.use("/api/upload", fileuploadRouter);

//파일 업로드 경로 설정
app.use(express.static("./uploads"));

//시스템 배치
app.use("/api/BatchRout", BatchRout);

const port = process.env.PORT || 5000;
app.listen(port, () => console.log(`Listening on port ${port}`));

  node 경로 C:\Users\ljung\OneDrive\문서\taling0102\routes에 BatchRout.js 파일을 생성하고 아래 코드를 붙여넣는다. cron을 사용하기 위해 node-cron을 설치한다.

npm install --save node-cron

  cron.schedule('* * * * *')은 1분마다 스케줄러를 실행하라는 뜻인데, 로컬에서 테스트용으로 사용하고, EC2 서버에 올릴때는 적절한 실행시간을 세팅하도록 한다.

  아래 코드는 정의한 시간에 CommonMapper.xml에서  inserBachlog라는 mapperID를 찾아 insert 쿼리를 실행시킬 것이다.

var express = require('express');
var router = express.Router();
var cron = require('node-cron');


//매일 00시 5분 실행
// cron.schedule('5 0 * * *', () => {
//매시간 1분마다 실행(1시1분 2시1분 3시1분...)
// cron.schedule('1 0 * * *', () => {
//1분마다 실행
cron.schedule('* * * * *', () => {

  var mapper = 'CommonMapper';//mybatis xml 파일명
  var crud = 'insert';//select, insert, update, delete 중에 입력
  var mapper_id = 'insertBatchlog';

  var param = { is_Batchnm: '테스트 배치', is_Batchlog: '테스트 배치가 정상 실행되었습니다. ' };
  console.log('#######  배치 실행 / 테스트 배치 #######');

  const mysql = require('mysql');
  const mybatisMapper = require('mybatis-mapper');

  const connection = mysql.createConnection({
    host: "databa비밀.ap-northeast-2.rds.amazonaws.com",
    port: "3306",
    database: 'rtrod',
    user: "user_temp",
    password: "비밀"
  });

    mybatisMapper.createMapper(['./models/'+mapper+'.xml']);
    var time1 = new Date();
    console.log('## '+time1+ ' ##');
    console.log("\n Called Mapper Name  = "+mapper);

    //질의문 형식
    var format = { language: 'sql', indent: '  ' };
    var query = mybatisMapper.getStatement(mapper, mapper_id, param, format);
    console.log("\n========= Node Mybatis Query Log Start =========");
    console.log("* mapper namespce : "+mapper+"."+mapper_id+" *\n");
    console.log(query+"\n");

    connection.connect();
    connection.query(query, function (error, results, fields) {  //조회
      if (error) {
        console.log("db error************* : "+error);
      }
      var time2 = new Date();
      console.log('## '+time2+ ' ##');
      console.log('## RESULT DATA LIST ## : \n', results);
      string = JSON.stringify(results);
      var json = JSON.parse(string);

      console.log("========= Node Mybatis Query Log End =========\n");
    });

    connection.end();
  
});


module.exports = router;

 

## 참고 ##

 cron예시

 cron에 쓰이는 특수문자

 * : 모든 값을 뜻합니다.

 ? : 특정한 값이 없음을 뜻합니다. 

 - : 범위를 뜻합니다. (예) 월요일에서 수요일까지는 MON-WED로 표현

 , : 특별한 값일 때만 동작 (예) 월,수,금 MON,WED,FRI 

 / : 시작시간 / 단위  (예) 0분부터 매 5분 0/5

 L : 일에서 사용하면 마지막 일, 요일에서는 마지막 요일(토요일)

 W : 가장 가까운 평일 (예) 15W는 15일에서 가장 가까운 평일 (월 ~ 금)을 찾음

 # : 몇째주의 무슨 요일을 표현 (예) 3#2 : 2번째주 수요일

 

  node 경로 C:\Users\ljung\OneDrive\문서\taling0102\models에 CommonMapper.xml 파일을 추가해 아래 코드를 붙여넣는다.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="CommonMapper">  
  
  <insert id="insertBatchlog"> 
    INSERT INTO rtrod.rtrod_batch_log
    (
        batch_cd
        , batch_nm
        , batch_log
        , reg_date
    )
    VALUES (
        CONCAT("BL",DATE_FORMAT(now(), '%Y%m%d%H%i%s'))
        , #{is_Batchnm}
        , <![CDATA[#{is_Batchlog}]]> 
        , DATE_FORMAT(now(), '%Y%m%d%H%i%s')
    )
  </insert>
 
</mapper>

workbench를 실행해 rtrod_batch_log 테이블을 생성한다.

use rtrod;

CREATE TABLE `rtrod_batch_log` (
  `batch_cd` varchar(100) DEFAULT NULL COMMENT '배치 코드',
  `batch_nm` varchar(100) DEFAULT NULL COMMENT '배치명',
  `batch_log` varchar(100) DEFAULT NULL COMMENT '배치내용',
  `reg_date` varchar(100) DEFAULT NULL COMMENT '등록날짜',
  KEY `IDX_rtrod_batch_log` (`batch_cd`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

select * from taling.rtrod_batch_log;
delete from rtrod_batch_log;

-- delete 안된다면 safy모드 해제
SET SQL_SAFE_UPDATES = 0;

-- insert 할때 한글 깨짐 방지
ALTER TABLE taling.rtrod_batch_log convert to charset utf8;

-- 테이블 구조조회
DESC rtrod_batch_log;
-- 테이블 인텍스 추가1
CREATE INDEX IDX_rtrod_batch_log1 ON rtrod_batch_log 
(swt_toolname);

CREATE INDEX IDX_rtrod_batch_log2 ON rtrod_batch_log 
(reg_user, reg_date);

-- 유일 인덱스 추가
ALTER TABLE rtrod_batch_log ADD UNIQUE INDEX (update_date);
-- 인덱스삭제
DROP INDEX IDX_rtrod_batch_log ON rtrod_batch_log ;
DROP INDEX update_date ON rtrod_batch_log ;

-- 테이블 인텍스 조회
SHOW INDEX FROM rtrod_batch_log;
-- 테이블 comment까지 조회
SHOW FULL COLUMNS FROM rtrod_batch_log;

-- 전체 테이블 comment 조회
SELECT TABLE_SCHEMA,TABLE_NAME,AUTO_INCREMENT,TABLE_COMMENT
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME LIKE '%rtrod%'
AND TABLE_SCHEMA LIKE '%taling%';

-- 전체테이블 컬럼 조회
SELECT 
  A.TABLE_SCHEMA, 
  A.TABLE_NAME, 
  A.COLUMN_NAME, 
  CONCAT(A.DATA_TYPE,'(',IFNULL(CHARACTER_MAXIMUM_LENGTH,IFNULL(NUMERIC_PRECISION,'')),')') TYPE, 
  A.IS_NULLABLE,
  B.TABLE_COMMENT,
  A.COLUMN_COMMENT
FROM INFORMATION_SCHEMA.COLUMNS A
INNER JOIN INFORMATION_SCHEMA.TABLES B
ON(A.TABLE_NAME = B.TABLE_NAME)
WHERE A.TABLE_SCHEMA NOT IN ('information_schema', 'performance_schema', 'isb_stg', 'isb_prd')
AND A.TABLE_SCHEMA LIKE '%taling%'  
ORDER BY A.TABLE_SCHEMA, A.TABLE_NAME, ORDINAL_POSITION;

2. 서버를 실행해 1분마다 insert 문이 실행되는 것을 확인한다.

 설정한 시간마다 실행되는 쿼리를 확인한다.

 

실제로 table에 insert된 데이터를 확인한다.

 

3. ec2 서버에 스케줄러 관련 파일들을 업로드한다.

추가/수정된 파일(server.js, /routes/BatchRout.js, /models/CommonMapper.js)만 따로 새로운 폴더에 옮기고 tar 파일로 압축한다.

mobaXterm Sftp로 서버 경로를 보면, 아래와 같이 Owner와 Group권한이 모두 root이다.

Sftp 접속 계정은 ubuntu이기 때문에, 수정권한이 없다.

서버 폴더에 파일을 추가하기 위해서 Group권한을 ubuntu계정에 부여한다.

 

SSH 터미널 접속 후, 권한을 부여할 폴더 상위 경로에서 아래 명령어를 실행한다.

chown 명령어는 taling0127 이라는 폴더와 하위경로에 있는 폴더/파일들의 권한을 수정하는데

Owner는 root 계정에 Group은 ubuntu계정에 부여하겠다는 뜻이다.

 

chmod 명령어는 앞에 7은 root, 두번째 7은 group 권한을 의미하는데

## 참고 ##

7 숫자의 의미 : rwx권한 (read,write,execute)을 2진법으로 변환하면 111 > 십진법으로 변환하면 7

// Onwer 권한은 root계정, Group 권한은 ubuntu계정으로 부여 
chown -R root:ubuntu taling0127
// Onwer와 Group에 모두 읽기/쓰기/실행 권한을 부여한다.
chmod -R 770 taling0127

권한 부여 후 SFTP로 돌아오면 Group권한이 ubuntu로 수정된 것을 확인할 수 있다.

tar파일을 node 경로에 위치시킨다.

SSH 터미널에서 node경로에 위치한 tar파일 압축을 해제한다.

## 참고 ##

압축할 때 알집을 사용하면 tar파일 압축을 해제할 때, 압축한 최상위 폴더는 제외하고 하위 폴더/파일만 아래와 같이 꺼내진다. 이때 장점은 압축해제와 동시에 기존 파일들을 덮어쓰면서 소스가 반영된다는 것이다.

반디집은 초기 세팅이 최상위 폴더가 포함돼 압축해제가 된다. 설정을 바꾸면 될 것 같긴한데, 아직 방법을 찾지 못했다.

node 경로에서 node-cron을 설치해준다.

npm install --save node-cron

설치완료 후, 서버를 구동하면 1분마다 스케줄러가 실행되는 것을 확인할 수 있다.

4. BatchRout.js 에서 cron을 매일 1회만 실행하도록 수정 후, ec2 서버를 계속 구동시킨다.

  BatchRout.js 파일에서 아래와 같이 00시 5분에만 실행하도록 코드를 수정한다.

var express = require('express');
var router = express.Router();
var cron = require('node-cron');


//매일 00시 5분 실행
cron.schedule('5 0 * * *', () => {
//매시간 1분마다 실행(1시1분 2시1분 3시1분...)
// cron.schedule('1 0 * * *', () => {
//1분마다 실행
// cron.schedule('* * * * *', () => {

  var mapper = 'CommonMapper';//mybatis xml 파일명
  var crud = 'insert';//select, insert, update, delete 중에 입력
  var mapper_id = 'insertBatchlog';

  var param = { is_Batchnm: '테스트 배치', is_Batchlog: '테스트 배치가 정상 실행되었습니다. ' };
  console.log('#######  배치 실행 / 테스트 배치 #######');

  const mysql = require('mysql');
  const mybatisMapper = require('mybatis-mapper');

  const connection = mysql.createConnection({
    host: "database-1.c9kj98ezdrrz.ap-northeast-2.rds.amazonaws.com",
    port: "3306",
    database: 'rtrod',
    user: "rtrod_user",
    password: "ajoumed1025"
  });

    mybatisMapper.createMapper(['./models/'+mapper+'.xml']);
    var time1 = new Date();
    console.log('## '+time1+ ' ##');
    console.log("\n Called Mapper Name  = "+mapper);

    //질의문 형식
    var format = { language: 'sql', indent: '  ' };
    var query = mybatisMapper.getStatement(mapper, mapper_id, param, format);
    console.log("\n========= Node Mybatis Query Log Start =========");
    console.log("* mapper namespce : "+mapper+"."+mapper_id+" *\n");
    console.log(query+"\n");

    connection.connect();
    connection.query(query, function (error, results, fields) {  //조회
      if (error) {
        console.log("db error************* : "+error);
      }
      var time2 = new Date();
      console.log('## '+time2+ ' ##');
      console.log('## RESULT DATA LIST ## : \n', results);
      string = JSON.stringify(results);
      var json = JSON.parse(string);

      console.log("========= Node Mybatis Query Log End =========\n");
    });

    connection.end();
  
});


module.exports = router;

로컬에서 cmd창에서 yarn dev로 서버를 구동시키고, cmd 창을 닫으면 서버가 꺼진다.

EC2 서버에서도 마찬가지로 SSH 터미널에서 yarn dev로 서버를 구동하고, 터미널을 닫으면 서버가 꺼진다.

이때, 터미널이 종료되어도 서버를 계속 구동시키기 위해서 nohup라는 명령어를 사용한다.

 

반대로 서버를 끄고 싶은데, 터미널을 종료해도 꺼지지 않을 것이다.

이럴 때는, react 서버 포트인 3000번 포트를 종료시키는 명령어를 사용한다. 

// 서버 상시구동
nohup yarn dev &
// 서버 종료시킬 때
fuser -k 3000/tcp

nohup 명령어로 서버를 켜놓고, 다음날 rtrod_batch_log 테이블에 00시 05분 데이터가 들어왔는지 확인한다.

 

https://taling.me/Talent/Detail/19341

 

[2월/주말] REACT, NODE, MYSQL, AWS 개발부터 배포까지/ 따라하면 완성되는 웹프로젝트. | 탈잉

✔ 수업 목표 ⊙ Font-end(react), back-end(node), mysql 구조를 프레임워크화 한다. ⊙ SELECT, UPDATE, DELETE, INSERT를 각각 1세트씩 구현한다. (CRUD 세트를 참고해서 만들고 싶은 기능을 혼자 개발할 수 있어요) ⊙ AWS EC2 인스턴스를 생성하고, 서버에 접근하여 파일을 컨트롤 할 수 있다. ⊙ AWS RDS(mysql) 인스턴스를 생성하고, 필요한 테이블들을 관리할 수 있다. ✔ 제 수업만의

taling.me

 

Comments