728x90
반응형

1. node-json-db 와 shortid 를 설치해준다. 

$ npm i node-json-db shortid @types/shortid --save

 

2. config 폴더 아래 db 설정을 해준다.

/config/nodejsondb.ts

import { JsonDB } from 'node-json-db';
import { Config } from 'node-json-db/dist/lib/JsonDBConfig'

const db = new JsonDB(new Config("myDataBase", true, false, '/'));

db.push('/one/lastdate', "");
db.push('/one/data',[]);

export {db}

 

3. 라우터에 결과값을 저장하는 메소드를 사용해준다.

추가로 검색한 데이터는 로그로 남겨둔다.

/routers/one.ts

import express, { Request, Response, NextFunction } from 'express';
var router = express.Router();

// middlewares
import { httpLoggingMiddleware } from '../middlewares';

// logger
import {
    loggerHttp,
    loggerDebug,
    loggerError,
    loggerInfo 
    } from '../config/winston';

// selenium
import webdriver from 'selenium-webdriver';
import chrome from 'selenium-webdriver/chrome';

// node-json-db
import { db } from '../config/nodejsondb';

router.get('/',httpLoggingMiddleware, async function (req: Request, res: Response, next: NextFunction) {
    // 웹드라이버 설정
    let driver = await new webdriver.Builder()
    .forBrowser('chrome')
    .setChromeOptions(new chrome.Options().windowSize({width: 1920, height: 1080}))
    .setChromeService(new chrome.ServiceBuilder(process.env.CHROMEDRIVER_PATH))
    .build()
    try {

        // 브라우저에 접속
        await driver.get('https://typo.tistory.com/')

        // 현재 주소 가져오기
        const text = await driver.getCurrentUrl();

        // 저장 데이터
        const savedata = {
            id : shortid.generate(),
            date : new Date,
            result : text
        }

        // 데이터베이스에 저장 
        db.push('/one[]', savedata);

        // log 저장
        loggerInfo.info(JSON.stringify(savedata))

        res.send({success: true, result : text});
    }
    catch(Err) {
        loggerError.info(JSON.stringify(Err))
        console.log(Err)
        driver.close();
        res.send({success: false, result : Err})
    }
});

export default router;

 

여기서 중요한 점은 db에서 접근하는 경로 "/one" 와 라우터의 경로인 "/one"( '/'제외 )의 문자열를 일치하게 만들었다는 점이다.

이 방법으로 우린 미들웨어에서 라우터의 경로에 맞게 db에 손쉽게 접근할 수 있다. ( request.originalUrl 사용 )

아직은 이해가 안 될 수 있지만 차근차근 알게 될 것이다.

 

보통 크롤링 api 서버를 열어두고 제한을 안두면 무분별하게 라우터에 접속해서 userAgent 또는 접속 IP가 막힐 수도 있다.

때문에 userAgent를 바꿔주거나 IP를 우회하는 방법도 있지만 살짝 불법 느낌이 있어서 여기서는 전에 접속했던 시간을 비교해서 5분 간격으로 크롤링이 가능하게끔 만들어보겠다.

 

방법은 미들웨어에서 url path와 db path를 비교해서 전에 크롤링했던 데이터의 시간을 보고, 5분이 넘었는지 안넘었는지 확인 후

만약 5분이 안지난 상태면 res.send 메소드를 사용해 실패 메세지를 전송하도록 하겠다.

 

4. timeInterval 미들웨어를 만들어준다.

/middlewares/timeInterval.ts

import express, { Request, Response, NextFunction } from 'express';

const {   
    loggerInfo,
    loggerError,
    loggerHttp,
    loggerDebug,
 } = require('../config/winston');

// node-json-db
import { db } from '../config/nodejsondb';

// module
import { expireHandler } from '../modules';

export const timeIntervalMiddleware = async (req: Request, res: Response, next: NextFunction) => {
    try {
        // url
        let url = req.originalUrl;

        // 30분이 지난 데이터는 지워줌
        expireHandler(url);

        // 전체 데이터베이스 조회
        const data = await db.getData('/');

        // 처음 크롤링 할 때는 key값 ( 데이터베이스 주소 )이 없는 상태이므로 패스
        // 라우터 경로에서 '/'를 뺀 값이랑 데이터베이스의 키 값이랑 같다.
        if(!Object.keys(data).includes(url.split('/')[1])  || !(await db.getData(url + "[-1]"))) {
            next();
        }
        // 데이터가 존재할 경우 시간을 현 시간과 비교함
        else {
        	expireHandler(url);
        
            // 현재 시간
            let nowTime = new Date()
            let beforeTime = new Date(await (await db.getData(url + "[-1]")).date); // 제일 최신 데이터의 조회 시간
            let diff = (nowTime.getTime() - beforeTime.getTime()) / (1000*60); // 분 으로 계산한다.
            // 전 시간과 비교하여 차이가 5분 미만일 때
            if (diff < 5) {
                res.send({success: false, result: "5분 미만입니다."})
            }
            else {
                next();
            }
        }
        // next();
    }
    catch (Err) {
        console.log(Err)
        loggerError.info(Err)
        res.send({ success: false, result: "timeInterverError" });
    }
}

 

/middlewares/index.ts

export * from "./winston"
export * from "./timeInterval"

 

/routers/one.ts

...

// middlewares
import { httpLoggingMiddleware, timeIntervalMiddleware } from '../middlewares';

...

router.get('/', timeIntervalMiddleware ,httpLoggingMiddleware, async function (req: Request, res: Response, next: NextFunction) {

...

 

5. 서버를 실행하고 브라우저를 켜보자.

 

크롤링이 시작하기도 전에 실패 메세지가 도착했다.

 

6. 일정 시간이 지난 데이터는 지워주는 모듈을 만든다.

node-json-db 에는 expire을 설정해주는 메소드가 없는거 같다. 때문에 일정 시간이 지난 데이터를 지워주는 모듈을 만들어봤다.

모듈화에 대해선 다음 포스트에서 제대로 다루겠다.

 

/modules/db/expireHandler.ts

// node-json-db
import { db } from '../../config/nodejsondb';

// logger
import { loggerError } from '../../config/winston';

export const expireHandler = async (dbPath: string) => {
    try {
        let nowTime = new Date(); // 현재 시간

        // 데이터 베이스 안 데이터들
        const datalist = await db.getData(dbPath);

        let index = 0; // 기준점이 되는 index

        // 데이터 매핑 및 시간 차이 계산해서 30분 지난 데이터는 삭제
        await datalist.map(async (item : any, index2: number) => {
            let beforeTime =  new Date(item.date); // 데이터들 저장된 시간
            let diff = (nowTime.getTime() - beforeTime.getTime()) / (1000*60); // 데이터들 시간 차이
            // 저장된 지 30분이 지나면 index 체크
            if(diff > 30) {
                index = index + 1;
            }
        })

         // 설정한 시간을 기준점으로 잡고  그 이전에 만든 데이터를 다 삭제한다. (앞에서부터 shift)
        for (let i = 0; i < index; i ++) {
            await db.delete(dbPath + '/data[0]');
        }
    }
    catch(Err) {
        loggerError.info(JSON.stringify(Err))
    }
}

 

/modules/index.ts

export * from "./db/expireHandler"

 

 

미들웨어에 적용시킨다.

/middlewares/timeInterval.ts

...

// module
import { expireHandler } from '../modules';

export const timeIntervalMiddleware = async (req: Request, res: Response, next: NextFunction) => {
    try {
        // 30분이 지난 데이터는 지워줌
        expireHandler(req.originalUrl)
        
...
728x90
반응형

+ Recent posts