티스토리 뷰

Node.js

[Node.js] Sequelize [1]

kkoon9 2020. 1. 11. 22:32
  • 이번 프로젝트에서 Sequelize를 사용했습니다.

mysql vs mysql2 vs promise-mysql vs Sequelize

mysql

  • 세계에서 가장 많이 사용되는 오픈 소스 데이터베이스입니다.
  • 매우 빠른 다중 스레드, 다중 사용자 및 강력한 SQL 데이터베이스 서버를 제공합니다.
  • MySQL 서버는 mission critical하고 로드가 많은 프로덕션 시스템과 대량 배포 소프트웨어에 내장하기 위한 것입니다.
    • mission critical : 업무 수행을 위하여 가장 중요한 요소(국방, 의료, 금융 등)에서 작은 실수만으로도 치명적인 결과를 초래하는 분야를 뜻합니다.

Sequelize?

  • Node.js에 사용하기 쉬운 promise 기반 ORM입니다.
  • 견고한 transaction 지원, relations, read replication 등을 제공합니다

ORM?

  • ORM이란 Object Relational Mapper의 약자로, 객체와 관계와의 설정이라 할 수 있습니다.
  • 자바스크립트 객체와 데이터베이스의 릴레이션을 매핑해준다고 생각하면 됩니다.

왜 why? Sequelize?

  • Sequelize를 사용한 이유는 promise 기반 ORM이기 때문입니다.
  • 자바스크립트 구문을 알아서 SQL로 바꿔주기 때문입니다.
  • SQL 언어를 직접 사용하지 않아도 자바스크립트만으로 MySQL을 조작할 수 있습니다.
    • 물론 MySQL을 모르는 채로 사용하는 것보다는 알고 사용하는 것이 좋습니다.

sequeilze 사용하기

1. 필요한 라이브러리

  • sequelize와 mysql2 패키지를 설치해야 합니다.
  • 그 뒤 sequelize init을 통해 필요한 폴더들을 설치해줍니다.

2. config

{
    "development": {
    "username": "TokDDak RDS 아이디",
    "password": "TokDDak RDS 비밀번호",
    "database": "tokddak",
    "host": "TokDDak RDS 주소",
    "dialect": "mysql"
    },
    "test": {
    "username": "local(Workbench) ID",
    "password": "local(Workbench) 비밀번호",
    "database": "tokddak",
    "host": "127.0.0.1",
    "dialect": "mysql"
    },
    "production": {
    "username": "root",
    "password": null,
    "database": "database_production",
    "host": "127.0.0.1",
    "dialect": "mysql"
    }
}
  • sequelize의 장점으로 development와 test, production을 나누어서 Database 테스트를 할 수 있습니다.
  • 프로젝트에서 test 데이터베이스는 local workbench에서 진행하였고, 실제 애플리케이션 데이터베이스는 RDS에서 진행하였습니다.

3. models/index.js

const Sequelize = require('sequelize');
const env = process.env.NODE_ENV || 'development';
const config = require('../config/config')[env];
const db = {};

const sequelize = new Sequelize(
  config.database, config.username, config.password, config
);

db.sequelize = sequelize;
db.Sequelize = Sequelize;
db.City = require('./cityModel')(sequelize, Sequelize);
db.Activity = require('./activityModel')(sequelize, Sequelize);
db.Food = require('./foodModel')(sequelize, Sequelize);
db.Shopping = require('./shoppingModel')(sequelize, Sequelize);
db.Snack = require('./snackModel')(sequelize, Sequelize);
db.Hotel = require('./hotelModel')(sequelize, Sequelize);
db.Transport = require('./transportModel')(sequelize, Sequelize);
db.Trip = require('./tripModel')(sequelize, Sequelize);
db.TripActivity = require('./tripActivityModel')(sequelize, Sequelize);
db.TripFood = require('./tripFoodModel')(sequelize, Sequelize);
db.TripSnack = require('./tripSnackModel')(sequelize, Sequelize);
db.TripHotel = require('./tripHotelModel')(sequelize, Sequelize);
db.Median = require('./medianModel')(sequelize,Sequelize);
db.Schedule = require('./scheduleModel')(sequelize, Sequelize);
db.User = require('./userModel')(sequelize, Sequelize);
db.Timeline = require('./timelineModel')(sequelize, Sequelize);

/** 1:N T : Timeline */
db.Trip.hasMany(db.Timeline);
db.Timeline.belongsTo(db.Trip);

/** 1:N T : Schedule */
db.Trip.hasMany(db.Schedule);
db.Schedule.belongsTo(db.Trip);

/** 1:N City : Activity */
db.City.hasMany(db.Activity);
db.Activity.belongsTo(db.City);

/** 1:N City : Food */
db.City.hasMany(db.Food);
db.Food.belongsTo(db.City);

/** 1:N City : Shopping */
db.City.hasMany(db.Shopping);
db.Shopping.belongsTo(db.City);

/** 1:N City : Snack */
db.City.hasMany(db.Snack);
db.Snack.belongsTo(db.City);

/** 1:N City : Hotel */
db.City.hasMany(db.Hotel);
db.Hotel.belongsTo(db.City);

/** 1:N City : Transport */
db.City.hasMany(db.Transport);
db.Transport.belongsTo(db.City);

/** 1:N Trip : TripActivity */
db.Trip.hasMany(db.TripActivity, {
  onDelete: 'cascade'
});
db.TripActivity.belongsTo(db.Trip);

/** 1:N Trip : TripHotel */
db.Trip.hasMany(db.TripHotel, {
  onDelete: 'cascade'
});
db.TripHotel.belongsTo(db.Trip);

/** 1:N Trip : TripFood */
db.Trip.hasMany(db.TripFood, {
  onDelete: 'cascade'
});
db.TripFood.belongsTo(db.Trip);

/** 1:N Trip : TripSnack */
db.Trip.hasMany(db.TripSnack, {
  onDelete: 'cascade'
});
db.TripSnack.belongsTo(db.Trip);

// Shopping이랑 Transport는 사용자가 금액을 입력하므로 따로 1:N 테이블을 만들지 않는다.

/** 1:N Trip : Schedule */
db.Trip.hasMany(db.Schedule, {
  onDelete: 'cascade'
});
db.Schedule.belongsTo(db.Trip);

/** 1:N User : Trip */
db.User.hasMany(db.Trip);
db.Trip.belongsTo(db.User);

module.exports = db;
  • models/index.js에서 데이터베이스 테이블 및 테이블 간 관계를 설정할 수 있습니다.
  • 프로젝트에서 사용한 테이블 수는 16개이고, 관계는 1:N만을 사용했습니다.
    • 1:1, N:M, 한 테이블에서의 N:M은 다음 게시물에 블로깅하겠습니다!

4. models 사용

  • Sequelize에서 주로 사용되는 문법은 다음과 같습니다.
    • findAll
    • findOne
    • create
    • update
    • destory
  • 하나하나 차근차근 살펴보겠습니다.
  • 다음은 실제 프로젝트에서 사용했던 코드입니다.
const rm = require('../module/util/responseMessage');
const utils = require('../module/util/utils');
const sc = require('../module/util/statusCode');
const fn = require('sequelize').fn; // orderby를 위해 사용했던 sequelize 문법
const col = require('sequelize').col; // table col을 가져오기 위해 사용했던 sequelize

const {Trip} = require('../models');
  • 특정 테이블의 CRUD를 사용하기 위해서는 models/index에서 require해옵니다.
    • Trip 테이블을 사용한다고 해서 require('../models/Trip')를 하면 오류가 발생하게 됩니다.

4-1. findAll

totalDayRead : ({
        TripId
    }) => {
        return new Promise(async (resolve, reject) => {
            const trip = await Trip.findOne({
                where: {
                    id: TripId,
                },
                attributes: ['totalDay']
            });
            console.log(trip);
            console.log(typeof trip);
            if (!trip) {
                resolve({
                    code: sc.INTERNAL_SERVER_ERROR,
                    json: utils.successFalse(sc.INTERNAL_SERVER_ERROR, rm.TRIP_READ_FAIL)
                });
                return;
            }
            if (trip.length == 0) {
                resolve({
                    code: sc.NO_CONTENT,
                    json: utils.successFalse(sc.NO_CONTENT, rm.TRIP_EMPTY)
                });
                return;
            }
            resolve({
                code: sc.SUCCESS,
                json: utils.successTrue(sc.SUCCESS, "여행 일수를 조회합니다.", trip)
            });
        });
    },
  • Trip.findAll에서 where절을 설정하여 모든 정보를 가져오게 됩니다.
  • attributes 옵션을 추가하여 가져오고 싶은 속성정보만 가져올 수 있습니다.
  • findOne은 row 정보 하나만 가져오고 findAll과 문법은 일치합니다.

4-2. create

create: ({
        title,
        momentStart,
        momentEnd,
        activityBudget,
        hotelBudget,
        foodBudget,
        shoppingBudget,
        snackBudget,
        transportBudget,
        totalDay,
        CityId,
        UserId
    }) => {
        return new Promise(async (resolve, reject) => {
            const obj = new Object();
            let trip;
            try {
                trip = await Trip.create({
                    title: title,
                    start: momentStart,
                    end: momentEnd,
                    city : city.dataValues.name,
                    country: city.dataValues.country,
                    activityBudget: activityBudget,
                    hotelBudget: hotelBudget,
                    foodBudget: foodBudget,
                    shoppingBudget: shoppingBudget,
                    snackBudget: snackBudget,
                    transportBudget: transportBudget,
                    totalDay: totalDay,
                    UserId: UserId
                });
            } catch (error) {
                reject({
                    code: sc.INTERNAL_SERVER_ERROR,
                    json: utils.successFalse(sc.INTERNAL_SERVER_ERROR, rm.TRIP_CREATE_FAIL)
                });
            }
            resolve({
                code: sc.SUCCESS,
                json: utils.successTrue(sc.SUCCESS, rm.TRIP_CREATE_SUCCESS)
            });
        });
    },
  • create와 다음에 나올 destory를 사용할 때 주의사항은 try catch문 내에서 사용해야 합니다.
  • totalDay: totalDay 에서 좌항은 데이터베이스 내 테이블 필드의 실제 이름을 기입해야 하며 우항은 변수이름입니다.

4-3. update

    trippingUpdate: ({
        id,
    }) => {
        return new Promise(async (resolve, reject) => {
            try {
                await Trip.update({
                    status: 2
                }, {
                    where: {
                        id: id,
                    }
                });

            } catch (error) {
                reject({
                    code: sc.INTERNAL_SERVER_ERROR,
                    json: utils.successFalse(sc.INTERNAL_SERVER_ERROR, rm.TRIPPING_UPDATE_FAIL)
                });
            }
            resolve({
                code: sc.SUCCESS,
                json: utils.successTrue(sc.SUCCESS, rm.TRIPPING_UPDATE_SUCCESS)
            });
        });
    },
  • 바꿔줄 필드의 값은 create와 동일하고, 바꿔줄 필드의 조건은 findAll에서 사용한 where절과 동일합니다.

4-4. destroy

    delete: ({
        id
    }) => {
        return new Promise(async (resolve, reject) => {
            try {
                trip = await Trip.destroy({
                    where: {
                        id: id
                    }
                });

            } catch (error) {
                reject({
                    code: sc.INTERNAL_SERVER_ERROR,
                    json: utils.successFalse(sc.INTERNAL_SERVER_ERROR, rm.TRIP_DELETE_FAIL)
                });
            }
            resolve({
                code: sc.SUCCESS,
                json: utils.successTrue(sc.SUCCESS, rm.TRIP_DELETE_SUCCESS)
            });
        });
    }
  • destroy는 findAll에서 사용한 where과 동일합니다.

추가해야 할 것들

  1. 1:1, N:M, 한 테이블 내의 N:M
  2. fn, col

참고

'Node.js' 카테고리의 다른 글

[Sequelize] 반경 내 주위 매장 조회하기  (1) 2020.06.06
[Node.js] Express  (0) 2020.01.10
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
글 보관함