공부/Digital Twin Bootcamp

TIL_220124_Backend

Ail_ 2022. 1. 25. 06:16

토큰 관리

완성 코드 참고

C:\Workspace\metacamp-backend2-origin

 

Express

Node.js를 위한 간결한 웹 프레임워크 : 구조를 잡아준다

거의 모든 nodejs 프로젝트는 express로 되어 있다. 그래서 한묶음으로 취급한다.

그래도 이력서엔 nodejs+express로 적자.

express로 짠 nodejs엔 프론트엔드 화면이 존재한다.

 

설치 설명 링크

npm install express-generator -g

 

프로젝트 생성

express --ejs nodeproj

명령어 express / ejs라는 형식으로 express에 프론트엔드 화면을 만들겠다 / 생성할 프로젝트명

 

nodemon

소스코드를 감지해서 수정되면 자동으로 재시작

 

설치

npm install nodemon -g

package.json에 dev 스크립트 추가(개발모드)

  "scripts": {
    "start": "node ./bin/www",
    "dev": "nodemon ./bin/www"
  },

 

eslint 설치

npm install -g eslint

init하기 위해 전역 옵션 설치

 

설정

{
  "env": {
    "commonjs": true,
    "es6": true,
    "node": true
  },
  "extends": [
    "airbnb-base"
  ],
  "globals": {
    "Atomics": "readonly",
    "SharedArrayBuffer": "readonly"
  },
  "parserOptions": {
    "ecmaVersion": 2018
  },
  "rules": {
    "linebreak-style": 0,
    "no-unused-vars": ["warn", { "vars": "all", "args": "after-used", "ignoreRestSiblings": false }]
  }
}

linebreak-style: 0 => Expected linebreaks to be 'LF' but found 에러를 피하기 위함

 

VSCode 설정

/.vscode/settings.json

{
  "eslint.validate": ["javascript", "html"],
  "eslint.alwaysShowStatus": true,
  "editor.tabSize": 2,
  "git.ignoreLimitWarning": true,
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true
  },
}

이제 저장할 때 자동변환 됨

이때 /bin/www 파일은 자동변환 되지 않도록 /* eslint-disable */ 설정

 

환경설정

/.env

NODE_ENV=development
PORT=3000
LOGGER_LEVEL=debug
DB_HOST=localhost
DB_PORT=5432
DB_DATABASE=metacamp_dev
DB_ID=postgres
DB_PASS=postgres
DB_DIALECT=postgres

 

env : environment variable / 환경변수(OS에서 사용 가능한 변수)

venv : virtual environment vaiable

 

echo %환경변수명% : 환경변수 출력(% : 환경변수 / 리눅스에서는 $환경변수명)

대표적인 환경변수 : PATH

 

라이브러리 설치

dotenv 

env 파일을 환경변수로 읽어온다.

npm install dotenv --save

 

bodyparser

npm install body-parser --save

bodyparser 설정

/app.js

// ...

const logger = require('morgan');
const bodyParser = require('body-parser');

// ...

app.use(express.static(path.join(__dirname, 'public')));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

// ...

 

logger

백엔드에선 console.log 안쓰는 게 좋음. 대신 로그 파일 활용

로깅 처리를 위한 winston 설치

npm install winston --save

매일 날짜에 맞춰 로그를 자동으로 생성해주는 라이브러리 설치

npm install winston-daily-rotate-file --save

설정

/lib/logger.js

const { createLogger, format, transports } = require('winston');
require('winston-daily-rotate-file');
const dotenv = require('dotenv');
const fs = require('fs');

dotenv.config();

// logger level 세팅
const loggerLevel = process.env.LOGGER_LEVEL || 'info';

// const { env } = envConfig;
const logDir = 'log';

// Log only if info.level less than or equal to this level
// { error: 0, warn: 1, info: 2, verbose: 3, debug: 4, silly: 5 }

// log directory
if (!fs.existsSync(logDir)) {
  fs.mkdirSync(logDir);
}

// log file
const dailyRotateFileTransport = new transports.DailyRotateFile({
  // 로그파일 출력 세팅
  filename: `${logDir}/%DATE%.log`,
  datePattern: 'YYYY-MM-DD',
  format: format.combine(
    format.printf(
      (info) => `${info.timestamp}[${info.level}] ${info.message}`,
    ),
  ),
});

const logger = createLogger({
  // 로거 환경 세팅(기본 세팅)
  level: loggerLevel,
  format: format.combine(
    // format.label( { label: 'label123' }),
    format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss.SSS' }),
    format.json(),
  ),
  transports: [
    new transports.Console({
      // 콘솔 출력 세팅
      level: loggerLevel,
      format: format.combine(
        format.colorize(),
        format.printf(
          (info) => `${info.timestamp}[${info.level}] ${info.message}`,
        ),
      ),
    }),
    dailyRotateFileTransport,
  ],
});

module.exports = logger;

/app.js 수정

구코드 삭제 및 새로 만든 라이브러리 삽입

const createError = require('http-errors');
const express = require('express');
const path = require('path');
const cookieParser = require('cookie-parser');
// const logger = require('morgan'); // 구코드 삭제
const bodyParser = require('body-parser');
const logger = require('./lib/logger');


//...

const app = express();
logger.info('app start');

// ...

// app.use(logger('dev')); // 구코드 삭제
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

Notepad++에서 열어서 확인하면 편함

logger 사용 예제

debug 레벨일 경우 debug까지 나온다.

info 레벨일 경우 info까지 나온다.

환경 설정은 nodemon이 안먹히기 때문에 껐다가 다시 npm run dev 해줘야 한다.

 

cors

설치

npm install cors --save

환경 설정

/config/corsConfig.json

{
  "origin": ["http://localhost:3000"],
  "methods": ["OPTIONS", "GET", "POST", "HEAD", "PUT", "DELETE"]
}

origin에 적힌 주소 허용

methods에 들어가는 요소 허용

주의! cors 정책에서 origin은 등록하지말고 전부 막도록 해야한다.

(font에서 접근할 때 proxy를 이용해서 접근하도록 해야한다.)

적용

//...
const cors = require('cors');
const corsConfig = require('./config/corsConfig.json');
const logger = require('./lib/logger');
//...

app.use(cors(corsConfig));
app.use(express.json());
//...

 

DB 연동

우클-connection

DBeaver 테이블 편집

SQL 편집기에서 drop table(커서를 세미콜론 안에두고 ctrl+enter로 한줄씩 실행)

버전 확인

 

ORM Object Relational Mapping

OOP 객체지향

RDB 관계지향 : table

객체지향 - 관계지향 변환을 위해 ORM 방식 탄생

 

Sequelize ORM

버전 6 사용

공식 문서

 

DB 클라이언트 설치(postgresql)

npm install pg

sequelize 설치

npm install sequelize --save

sequelize 접속 설정

const Sequelize = require('sequelize');
const dotenv = require('dotenv');

dotenv.config(); // .env 파일 불러오기

const db = {
  username: process.env.DB_ID,
  password: process.env.DB_PASS,
  database: process.env.DB_DATABASE,
  host: process.env.DB_HOST,
  port: process.env.DB_PORT,
  dialect: process.env.DB_DIALECT,
};

// sequelize 생성
const sequelize = new Sequelize(
  db.database,
  db.username,
  db.password,
  {
    host: db.host,
    port: db.port,
    dialect: db.dialect,
  },
);

exports.sequelize = sequelize;

 

 

[env] 설명

env 파일을 환경 변수로 읽어오기 위한 코드

\Desktop\Backend code\sequlize_220106\metacamp-backend\models\index.js

const env = process.env.NODE_ENV || 'development' // default 값 'development'
const config = require('../config/config')[env];

여기서 아래 코드의 경우

const config = require('../config/config')[env];

다음과 같다.

const config1 = require('../config/config');
const config = config1['development']
// 혹은
const config1 = require('../config/config');
const config = config1.development

 

Model

데이터 타입

링크 참고

 

두 요소를 합쳐서 유니크 만드는 법

ex) 학년+반

/models/department.js

      sc_grade: {
        type: Sequelize.STRING(50),
        unique: school,
      },
      sc_class: {
        type: Sequelize.STRING(50),
        unique: school,
      },

sequelize 옵션

 {
      sequelize,
      // tableName: 'tableName', // table명을 수동으로 생성 함
      // freezeTableName: true, // true: table명의 복수형 변환을 막음
      underscored: true, // true: underscored, false: camelCase
      timestamps: true, // createAt, updatedAt
      paranoid: true, // deletedAt
    });
  }
};

DB는 대소문자 구별 X

따라서 underscored : true로 자동 변환 해주는 게 좋다.

false : camelCase로는 대소문자 구별이 없어 힘듦

 

인덱스 파일 생성

const { sequelize } = require('./connection');
const Department = require('./department');

const db = {};

db.sequelize = sequelize;

// model 생성
db.Department = Department;

// model init
Department.init(sequelize);

module.exports = db;

모델 초기화 = 테이블 생성

/app.js

const models = require('./models/index');
const logger = require('./lib/logger');
//...

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

// DB 연결 확인 및 table 생성
models.sequelize.authenticate().then(() => {
  logger.info('DB connection success');

  // sequelize sync (table 생성)
  models.sequelize.sync().then(() => {
    logger.info('Sequelize sync success');
  }).catch((err) => {
    logger.error('Sequelize sync error', err);
  });
}).catch((err) => {
  logger.error('DB Connection fail', err);
});
...

결과

 

 


느낀 점

nodejs, express, sequelize까지 쭉쭉 배웠다.

IoT 했다가 웹 했다가 왔다갔다하니까 바뀔 때면 머릿속에서 로딩이 좀 되고 있는데 그래도 이번엔 아예 모르는 게 아니라 로딩이 짧아서 다행이었다.