[npm] Sequelize X MySQL 에서 헷갈리거나 어려울만한 것 정리

2020. 10. 26. 19:44컴퓨터언어/Node.js

728x90
반응형

👍 기본 개념

 

1. Sequelize는 ORM이다.

 

2. ORM이란, Object-Relational Mapping의 약자로 JavaScript의 자료형인 "Object"와 실제 DB에 저장된 "Relation"을 "대응(Mapping)"시켜주는 도구를 말한다.

 

3. Sequelize와 연결될 수 있는 DB는 관계형 DB면 상관없다. 만약 MySQL DB와 연결하려면 npm mysql2를 설치해야 한다.

 

4. MySQL은 오픈소스 관계형 DBMS이다.

 

5. 우리가 하려는 것은 다음과 같다.

웹 서비스의 데이터들 간에 관계가 명확하므로 관계형 데이터베이스를 사용할 것임
그리고 그 관계형 데이터베이스를 조작하기 위해 오픈소스 DBMS인 MySQL을 사용할 것임
그리고 그 DBMS를 Node.js & Express에서 편리하게 사용하기 위해 Sequelize를 사용할 것임

 


👍 Sequelize 사용을 위한 세팅

 

1. Sequelize를 사용하려면 다음을 설치한다.

$ npm i sequelize sequelize-cli mysql2

 

2. 다음을 실행하여 관련 기본 파일을 생성한다.

$ npx sequelize init

 

 

3. 2번 과정에서 만들어진 기본 폴더 중에서 config/config.json에서 자신이 이번 프로젝트에 사용할 스키마의 이름과 MySQL 비밀번호를 "개발용"이냐 "테스트용"이냐 "배포용"이냐에 따라 각각 적어준다.

{
  "development": {
    "username": "root",
    "password": "비밀이야",
    "database": "MySqlEx",
    "host": "127.0.0.1",
    "dialect": "mysql"
  },
  "test": {
    "username": "root",
    "password": "비밀이야",
    "database": "database_test",
    "host": "127.0.0.1",
    "dialect": "mysql"
  },
  "production": {
    "username": "root",
    "password": "비밀이야",
    "database": "database_production",
    "host": "127.0.0.1",
    "dialect": "mysql"
  }
}

4, 우리가 추후에 모듈화하여 정의할 각 모델(테이블)을 한번에 로드할 수 있도록 model/index.js을 수정한다.

const Sequelize = require("sequelize");
const Comment = require("./Comment");
const User = require("./User");

const env = process.env.NODE_ENV || "development";
const config = require("../config/config.json")[env];

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

db.sequelize = sequelize;

db.User = User;
db.Comment = Comment;

User.init(sequelize);
Comment.init(sequelize);

User.associate(db);
Comment.associate(db);

module.exports = db;

5. 각 모델을 만든다. User과 Comment가 있다고 하자. 두 모델 간 관계 설정이 어려울 수 있는데, 후술한다.

const Sequelize = require("sequelize");

module.exports = class User extends Sequelize.Model {
    static init(sequelize) {
        return super.init({
            name: {
                type: Sequelize.STRING(20),
                allowNull: false,
                unique: true,
            },
            age: {
                type: Sequelize.INTEGER.UNSIGNED,
                allowNull: false
            },
            married: {
                type: Sequelize.BOOLEAN,
                allowNull: false
            },
            comment: {
                type: Sequelize.TEXT,
                allowNull: true
            },
            created_at: {
                type: Sequelize.DATE,
                allowNull: false,
                defaultValue: Sequelize.NOW
            }
        }, {
            sequelize,
            timestamps: false,
            underscored: false,
            modelName: "User",
            tableName: "users",
            paranoid: false,
            charset: "utf8",
            collate: "utf8_general_ci"
        })
    }

    static associate(db) {
        db.User.hasMany(db.Comment, {foreignKey: "commenter", sourceKey: "id"});
    }
}
const Sequelize = require("sequelize");

module.exports = class Comment extends Sequelize.Model {
    static init(sequelize) {
        return super.init({
            comment: {
                type: Sequelize.STRING(100),
                allowNull: false
            },
            created_at: {
                type: Sequelize.DATE,
                allowNull: false,
                defaultValue: Sequelize.NOW
            }
        }, {
            sequelize,
            timestamps: false,
            modelName: "Comment",
            tableName: "comments",
            paranoid: false,
            charset: "utf8",
            collate: "utf8_general_ci"
        })
    }

    static associate(db) {
        db.Comment.belongsTo(db.User, {foreignKey: "commenter", targetKey: "id"});
    }
}

6. 서버 프로그램 진입 파일(init.js 또는 app.js 등)에서 model/index.js를 require하고 sync()로 실행한다.

const express = require("express");
const PORT = process.env.PORT || 3000;
const { sequelize } = require("./models");
const globalRouter = require("./routers/globalRouter");
const app = express();
sequelize.sync({force: false})
    .then(()=>`DB CONNECTED`)
    .catch((err)=>console.error(err));
app.set("view engine", "pug");
app.use("/", globalRouter);
app.listen(PORT, ()=>console.log(`http://localhost:${PORT}`));

👍 관계 설정

 

두 릴레이션 User와 Comment가 있다고 하자.

이때 한 명의 User는 여러 Comment를 남길 수 있지만, 반대로 하나의 Comment는 단 하나의 작성자만 가질 수 있다.

따라서 User : Comment = 1 : N이다.

 

Sequelize에서는 이를 직관적으로 표현할 수 있다.

User 모델에 hasMany를, Comment 모델에 belongsTo를 넣으면 되는 것이다.

 

// User.js

static associate(db) {
	db.User.hasMany(db.Comment, {foreignKey: "commenter", sourceKey: "id"});
}
// 외래키인 commenter 컬럼이 현재 User의 id 컬럼을 참조하고 있음
// Comment.js

static associate(db) {
	db.Comment.belongsTo(db.User, {foreignKey: "commenter", targetKey: "id"});
}
// belongsTo에서의 foreignKey는 자신에게 속해있는 컬럼이고, targetKey는 foreignKey가 참조하는 상대 모델의 컬럼이다.

 

🔥 중요한 것 🔥

모델을 정의할 때는, 1이든 N이든 둘다 같은 이름의 외래키 컬럼을 갖는다.

하지만, 실제 물리적으로 추가될 때는 belongsTo가 있는 모델, 즉 참조하는 릴레이션에만 생긴다.

 

👊 두 모델의 관계가 다음과 같다고 하면 코드 역시 달라진다.

 

1:1(일대일) 관계

둘 중 어느 한 쪽에 외래키를 줘도 상관 없으므로, 외래키를 어느 모델에 줄 것인지만 확실하게 정한 후, 그 모델을 belongsTo로 설정하면 된다.

 

N:M(다대다) 관계

두 모델 모두 서로에게 belongsToMany(db.상대모델, {through: "중간테이블이름"})을 설정한다.

 

👊 두 모델을 JOIN해서 가져오는 방법

 

<전제조건>

// 파일 : model/index.js

...
    static associate(db) {
        db.User.hasMany(db.Comment, { foreignKey: "commenter", sourceKey: "id" });
    }

 

1번째 방법 : include를 이용 - 간편함

const user = await User.findOne({
    include: [{
        model: Comment
    }]
});

2번째 방법 : get함수를 이용 - 쿼리를 2번 나눠서 진행하므로 성능에 있어서 유리함

const user = await User.findOne({});
const comments = await user.getAnswers();
728x90
반응형