TypeScript - Express 기본 세팅

권민제·2021년 8월 21일
1

TypeScript

목록 보기
2/4
post-thumbnail

package.json

{
  "name": "typescript-express-basic",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "tsc",
    "build:watch": "tsc -w",
    "dev": "NODE_ENV=dev ts-node-dev --respawn --transpile-only src/start.ts",
    "start:dev": "nodemon --watch src --delay 500ms --exec ts-node src/start.ts",
    "start": "NODE_ENV=production node build/start.js"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@types/express": "^4.17.13",
    "@types/morgan": "^1.9.3",
    "@types/multer": "^1.4.7",
    "@types/node": "^16.4.12",
    "nodemon": "^2.0.12",
    "ts-node": "^10.1.0",
    "ts-node-dev": "^1.1.8",
    "typescript": "^4.3.5"
  },
  "dependencies": {
    "dotenv": "^10.0.0",
    "express": "^4.17.1",
    "morgan": "^1.10.0",
    "multer": "^1.4.2",
    "winston": "^3.3.3",
    "winston-daily-rotate-file": "^4.5.5"
  }
}

TypeScript

Express

Ts

$ npm i -D @types/express

Js

$ npm i express

Node module

nodejs 기본 모듈들을 import할 수 있게 하는 모듈

$ npm i -D @types/node

Dev Test 실행 관련 모듈

$ npm i -D ts-node
$ npm i -D ts-node-dev
$ npm i -D nodemon

Logger 관련 모듈

$ npm i -D @types/morgan
$ npm i morgan
$ npm i winston winston-daily-rotate-file

File 관련 모듈

$ npm i -D @types/multer
$ npm i multer

Env 모듈

$ npm i dotenv
$ npm i path

Script

"scripts": {
    "build": "tsc",
    "build:watch": "tsc -w",
    "dev": "NODE_ENV=dev nodemon --watch src/ --delay 500ms --exec ts-node src/start.ts",
    "start:dev": "NODE_ENV=dev ts-node-dev --respawn --transpile-only src/start.ts",
    "start": "NODE_ENV=production node build/start.js"
},

dev

$ npm run dev

nodemon - 코드를 변경할 때마다 node를 재실행 시켜주는 모듈
--watch {folder} : 설정한 폴더안에 변화가 있으면 감지해서 재시작함
--exec ts-node : ts파일을 nodemon으로 실행시킬 수 있게함

start:dev

$ npm run start:dev

ts-node-dev - ts파일로 node dev서버 실행

start

$ npm run start

ts를 빌드한 js file로 node 서버 시작

Code Setting

Directory

ts-express-basic
├── node_modules
├── env
│   └── server.env
├── logs
├── src
│   ├── config
│   │   ├── env.ts
│   │   ├── route.ts
│   │   ├── logger.ts
│   │   └── morgan.ts
│   ├── controller
│   │   └── test.ts
│   ├── app.ts
│   └── start.ts
├── package.json
└── tsconfig.json

env / server.env

DB_HOST=
DB_USER=
DB_DATABASE=
DB_PASSWORD=
DB_PORT=

src / config / env.ts

import path from "path";
import dotenv from "dotenv";

const config = dotenv.config({
    path : path.join(__dirname, '../../env/server.env')
})

export const env = {
    database : {
        host: process.env.DB_HOST,
        user: process.env.DB_USER,
        database: process.env.DB_DATABASE,
        password: process.env.DB_PASSWORD,
        port: Number(process.env.DB_PORT)
    }
}

src / config / route.ts

import express from 'express';
const router = express.Router();
export default router

src / config / logger.ts

import winston from 'winston';
import winstonDaily from 'winston-daily-rotate-file';

const logDir = 'logs';  // logs 디렉토리 하위에 로그 파일 저장
const { combine, timestamp, printf, colorize} = winston.format;

// Define log format
const logFormat = printf(info => { return `[ ${info.level} ] ${info.timestamp} : ${info.message}`});

/*
 * Log Level
 * error: 0, warn: 1, info: 2, http: 3, verbose: 4, debug: 5, silly: 6
 */

const logger = winston.createLogger({
    format: 
        combine(
            timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
            logFormat,
        ),
    transports: [
        // info 레벨 로그를 저장할 파일 설정
        new winstonDaily({
            level: 'info',
            datePattern: 'YYYY-MM-DD',
            dirname: logDir,
            filename: `%DATE%.log`,
            maxFiles: 30,  // 30일치 로그 파일 저장
            zippedArchive: true, 
        }),
        // error 레벨 로그를 저장할 파일 설정
        new winstonDaily({
            level: 'error',
            datePattern: 'YYYY-MM-DD',
            dirname: logDir + '/error',  // error.log 파일은 /logs/error 하위에 저장 
            filename: `%DATE%.error.log`,
            maxFiles: 30,
            zippedArchive: true,
        }),
    ],
});

// Production 환경이 아닌 경우(dev 등) 
if (process.env.NODE_ENV !== 'production') {
    logger.add(new winston.transports.Console({
        format: combine(
            colorize({ all: true }),  // 색깔 넣어서 출력
            logFormat,
        )
    }));
}

export default logger;

src / config / morgan.ts

import morgan, { StreamOptions } from "morgan";
import logger from "./logger";


const combined = ':remote-addr - :remote-user ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"'
// :method / request에 대한 HTTP method | :url 요청된 URL | :status HTTP 상태 | :response-time 응답시간 | :remote-addr 사용자의 IP 주소 | :http-version HTTP version |
const morganFormat = process.env.NODE_ENV !== "production" ? 'dev' : combined; // NOTE: morgan 출력 형태

const stream: StreamOptions = { // morgan log 출력 설정 winston logger 사용
  write: (message) => logger.info(message),
};


const morganMiddleware = morgan( morganFormat,{ stream }); // morgan use setting

export default morganMiddleware;

src / config / morgan.ts

import morgan, { StreamOptions } from "morgan";
import logger from "./logger";


const combined = ':remote-addr - :remote-user ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"'
// :method / request에 대한 HTTP method | :url 요청된 URL | :status HTTP 상태 | :response-time 응답시간 | :remote-addr 사용자의 IP 주소 | :http-version HTTP version |
const morganFormat = process.env.NODE_ENV !== "production" ? 'dev' : combined; // NOTE: morgan 출력 형태

const stream: StreamOptions = { // morgan log 출력 설정 winston logger 사용
  write: (message) => logger.info(message),
};


const morganMiddleware = morgan( morganFormat,{ stream }); // morgan use setting

export default morganMiddleware;

src / controller / test.ts

import express, { Request, Response } from 'express';
import router from '../config/route';
import logger from '../config/logger';

const testRoute = router;

testRoute.get('/test', (req: Request, res: Response) => { 
    res.status(200).send();
});

export default testRoute

src / app.ts

import express, {Request, Response} from "express";
import logger from './config/logger';
import morganMiddleware from "./config/morgan";
import router from './config/route';
import testRoute from './controller/testRoute';

export class App{
    public app;

    constructor(){
        this.app = express();
        this.setMiddleware();
        this.setExpress();
    }

    private setExpress() : void { // express setting 
        try {
            this.setRoute();
        } catch(err){
            logger.error(err);
        }
    }
    private setRoute() : void { // express routing controller setting
        this.app.use(router);
        this.app.use(testRoute);
    }
    private setMiddleware() : void {
        this.app.use(express.json()); // request body parsing setting
        this.app.use(express.urlencoded({ extended: false })); // request body parsing setting
        this.app.use(morganMiddleware) // morgan http logging setting
    }
}

src / start.ts

import { App } from './app';
import logger from './config/logger';

try {
    const app = new App().app;
    const port : number = 3000;

    app.listen(port, () => {
        logger.info(` NODE ENV = ${process.env.NODE_ENV} `)
        logger.info(`Server is running on port : ${port}`);
    });
} catch(err) {
    logger.error(err);
}
profile
성장하는 개발자!

0개의 댓글