[Node.js] Express Rest API 서버 - mysql2 sequelize 연동

기존 Express 프레임워크로 API 서버 뼈대를 구축했었는데 이번에는 mysql DB 연동을 진행해보려 합니다.

이번에 추가된 모듈은 dotenv, morgan, mysql2, sequelize, sequelize-cli 총 5개인데요.

 

 

dotenv 는 환경변수를 .env 파일로 관리할 수 있도록 서포트해주는 모듈입니다.

https://www.npmjs.com/package/dotenv

 

dotenv

Loads environment variables from .env file. Latest version: 16.3.1, last published: 2 months ago. Start using dotenv in your project by running `npm i dotenv`. There are 36361 other projects in the npm registry using dotenv.

www.npmjs.com

 

morgan 은 로그를 콘솔 혹은 파일로 남길 수 있도록 서포트 해주는 모듈입니다.

https://www.npmjs.com/package/morgan

 

morgan

HTTP request logger middleware for node.js. Latest version: 1.10.0, last published: 3 years ago. Start using morgan in your project by running `npm i morgan`. There are 8583 other projects in the npm registry using morgan.

www.npmjs.com

mysql2, sequelize, sequelize-cli 은 mysql (mariadb) 제어를 서포트해주는 모듈입니다.

mysql과 mysql2 모듈이 각각 존재하는데 mysql2는 비동기를 지원하는 모듈입니다.

시퀄라이즈가 추가되면서 폴더 구조가 복잡하게 확장되었습니다.

 

 

config / migrations / models / seeders 폴더는 sequelize-cli generator 명령어를 통해 기본 생성된 폴더입니다.

 

controllers 는 router 처리함수를 제공해줍니다.  기존 v0.1 에서는 routes 하위에 js파일 내에 라우트마다 함수를 즉시 생성해서 제공했었는데 콘트롤러로 따로 빼서 구현하도록 구조를 변경하였습니다.

 

utils, logs 폴더는 morgan 모듈 관련 폴더인데 utils 아래 js 파일에 로그 관련 설정이 남겨져 있고 logs 폴더에는 로그 파일들이 저장됩니다.

 

 

 

 

 

 

1. dotenv 설정하기

 

app.js 에 아래와 같이 dotenv 관련 코드를 작성해줍니다.

.env 파일을 루뜨 하위에 생성해 환경설정 파라미터를 관리해주면 됩니다.

저는 PORT=3001 파라미터 하나만 추가해서 서버 실행시 listen 포트로 지정해주었습니다.

 

//app.js

const dotenv = require('dotenv');
// .env 환경변수 데이타 로드
dotenv.config();

// process.env로 환경변수에 접근
app.set('port', process.env.PORT);

// 서버 실행시 포트번호 가져와서 적용
app.listen(
  app.get('port') || process.env.PORT, 
  () => console.log("API Server v0.1 Start at port",app.get('port'))
);

2. morgan 로그 남기기

 

log.js 파일 내 로그파일 경로 지정 및 파일스트림을 생성해서 리턴해줍니다.

 

app.js 에서 이 파일스트림을 전달받아 app.use(morgan()); 으로 로그 설정을 진행해주면 됩니다.

총 5개의 로그 형식이 제공되는데 combined, common, dev, short, tiny 키워드로 지정이 가능합니다. 공식 문서에서 로그 형태를 확인하시어 본인 상황에 맞는 로그로 지정하시면 됩니다.

//utils/log.js

const fs = require('fs');
const path = require('path');

const accessLogStream = fs.createWriteStream(
    path.join('${__dirname}', '/../logs/access.log'),
    { flags: 'a' }
)

module.exports = accessLogStream

//app.js

const morgan = require('morgan');
const accessLogStream = require('./utils/log');

// 로그 설정
app.use(morgan('common', { stream: accessLogStream }));

 

3. sequelize 적용

 

DB제어를 위해 mysql2, 시퀄라이즈 모듈을 설치해줍니다.

터미널에서 sequelize-cli init 명령을 통해 기본 셋업을 진행해줍니다. 이 과정을 거치면 폴더가 추가로 생성이 됩니다.

저는 윈도우 환경에서 진행했는데 sequelize-cli.cmd 로 명령어를 넣어야 실행이 되더군요. 시스템 path 가 제대로 지정이 안되어 있는듯 합니다. 

 

sequelize generate 명령어로 사용자 모델을 생성해줍니다.

sequelize model:generate --name User --attributes user_id:string, user_name:string, nick:string, email:string

ID, 이름, 닉네임, 이메일 필드로 구성된 User 모델이 생성됩니다.

models/user.js 파일내 구성이 되며 Sequelize.Model 클래스를 상속받은 User 클래스 하위에 구성이 되었습니다. 

제가 듣던 강의 내용이 있는데 현재 시퀄라이즈가 만들어내는 모델의 코드가 상이해서 기존 강의 코드를 참고해 수정을 좀 가했습니다.

 

// models/user.js

const Sequelize = require('sequelize');

class User extends Sequelize.Model {
  static initiate(sequelize) {
    User.init({
      user_id: {
        type: Sequelize.STRING(40),
        allowNull: true,
        unique: true,
      },
      user_name: {
        type: Sequelize.STRING(15),
        allowNull: false,
      },
      nick: {
        type: Sequelize.STRING(100),
        allowNull: true,
      },
      email: {
        type: Sequelize.STRING(100),
        allowNull: true,
      },
    }, {
      sequelize,
      timestamps: true,
      underscored: false,
      modelName: 'User',
      tableName: 'users',
      paranoid: true,
      charset: 'utf8',
      collate: 'utf8_general_ci',
    });
  }

  // 테이블 관계 설정
  static associate(db) {    
  }
};

module.exports = User;

 

cli로 마이그레이션 up,down 코드를 통해 테이블 생성 및 드랍을 하던데 저는 다른 방식으로 app.js 내 시퀄라이즈 sync 시 force 를 true 로 설정해서 db 테이블을 생성했습니다. force 가 false일 때는 아무일도 일어나지 않습니다. 실제 운영환경에서 true로 설정 시 매우 위험해질듯 하네요


// DB 설정
sequelize.sync({ force: false })
  .then(() => {
    console.log('데이터베이스 연결 성공');
  })
  .catch((err) => {
    console.error(err);
  });


 

 

이제 User 를 제어하는 콘트롤러를 작성해줍니다. 

 

User 리스트 조회, 특정 User 조회, User 생성, User 수정 4가지 정도로 간단한 제어 기능을 구현해봅니다.

 

전체 사용자 조회 처리 function 입니다. 

mysql2 는 비동기 방식을 지원해주기 때문에 async 방식으로 코드를 짜야 합니다.

 

Users 모델 객체를 통해 쿼리 함수들을 사용할 수 있는데 기본적으로 CRUD는 create / findAll / findOne / update / destroy 가 있습니다. 직관적이라서 쉽게 접근이 가능합니다.

 

공식 문서를 통해 추가적인 쿼리함수들을 확인하실 수 있습니다.

https://sequelize.org/docs/v6/core-concepts/model-querying-basics/

 

Model Querying - Basics | Sequelize

Sequelize provides various methods to assist querying your database for data.

sequelize.org

 

 

그리고 Raw Query 도 사용이 가능한데 저는 로우쿼리에 익숙하지만 시퀄라이즈가 제공하는 기능을 익히려면 함수쿼리로 사용하는게 좋을듯 합니다.

 

조회 결과로 user 가 true 라면 res.render 로 화면을 출력하거나 res.send 로 json 데이터를 넘기도록 되어 있습니다.

const Users  = require('../models/user');

// 전체 사용자 조회
exports.getAllUser = async (req, res, next) => {
    
    try{
        //const user = await Users.findAll({attributes: ['user_id', 'user_name', 'nick', 'email']});
        const user = await db.sequelize.query("SELECT user_id, user_name, nick, email FROM `users`", { type: QueryTypes.SELECT });

        if( user ){
            //res.render('user', {users: user});
            res.send(JSON.stringify(user));
        }
    }catch(err){
        console.error(err);
        next(err);
    }    
};

 

POSTMAN 으로 api 호출 시 실제 넘겨받는 데이터인데 정상적으로 모든 사용자 정보가 넘어오는 것을 확인하실 수 있습니다.

 

시퀄라이즈 쿼리는 Promise 를 반환하므로 .then(result){} 의 방식으로 붙여서 결과값을 반환받고 후속처리를 진행해주어야 합니다. 프라미스를 통해 콜백 지옥을 막을 수 있다고 하지만 then() 으로 계속 확장해 나가야 하는 부분에서 불편함이 있기 때문에 async, await 호출 방식으로 쓰는 것을 추천받았습니다. 

그래서 처음엔 .then(){}.then(){}..catch(){} 방식으로 작성했던 코드를 async, awiat 로 리팩토링을 진행했습니다

 

 

user 라우터를 생성해줍니다.

get방식, post 방식으로 api를 구성해주었으며  user 콘트롤러 내 함수들을 콜백함수로 지정해주었습니다. 

// routes/user.js

const express = require('express');
const router = express.Router();

const user = require('../controllers/user');

// 메인 페이지 출력
router.get('/', (req, res, next) => {
    res.render('index', {title: 'User Page'})    
});

// 전체 사용자 조회
router.get('/list', user.getAllUser);

// 특정 사용자 조회
router.get('/:user_id/info', user.getUserInfo);

// 사용자 생성
router.post('/addUser', user.createUser);

// 사용자 수정
router.post('/editUser', user.editUser);

// 사용자 삭제
//router.post('/deleteUser', user.deleteUser);

module.exports = router;


// app.js
const userRouter = require('./routes/user');
app.use('/user', userRouter);

 

다음에는 세션과 권한에 대한 부분을 추가할 예정이며 view 템플릿을 nunjucks 로 변경하거나 api 서버 구축 시 프론트를 리액트로 구성해보는 작업을 진행해보도록 하겠습니다.

 

전체 소스는 Git repository quant_api/v0.2 에 올려두었습니다. 

https://github.com/eunipapa/quant_api.git

 

GitHub - eunipapa/quant_api

Contribute to eunipapa/quant_api development by creating an account on GitHub.

github.com