2020. 10. 26. 19:44ㆍ컴퓨터언어/Node.js
👍 기본 개념
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();