1. Sequelize 셋업2. 모델 정의하기2-1. index.js와 모델 연결3. 관계 정의1:1 관계N:M 관계4. 쿼리CRUDSelectJOINRaw SQLTransaction
1. Sequelize 셋업
npm i express sequelize sequelize-cli mysql2
npx sequelize init
: config, models, migrations, seeders 폴더 생성(sequelize-cli가 자동으로 만들어줌)
- 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; module.exports = db;
{ "development": { "username": "root", "password": "1234", "database": "nodejs", "host": "127.0.0.1", "dialect": "mysql" }, "test": { "username": "root", "password": null, "database": "database_test", "host": "127.0.0.1", "dialect": "mysql" }, "production": { "username": "root", "password": null, "database": "database_production", "host": "127.0.0.1", "dialect": "mysql" } }
- app.js에서 sequelize이용하여 db 연결
const { sequelize } = require('./models'); sequelize.sync({force: false }) .then(() => { console.log('데이터베이스 연결 성공'); }) .catch((err) => { console.error(err); });
2. 모델 정의하기
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) {} };
- User 모델로 만들고 exports함
- Sequelize.Model을 확장한 클래스로 선언
- static init 메서드, static associate 메서드로 구성됨
- static init 메서드 : 테이블에 대한 설정
- 첫 번째 인수 : 테이블 컬럼 설정
- 두 번째 인수 : 테이블 자체에 대한 설정
- timestamps : true ⇒ createdAt과 updatedAt 컬럼 자동 추가
- underscored: 테이블명, 컬럼명 기본은 camel case. 이를 스네이크 케이스로 바꾸어줌. true면
- paranoid: true로 설정하면 deletedAt 이라는 컬럼이 생김. 로우를 삭제할 때 완전히 지워지지 않고 deletedAt에 지운 시각이 기록됨
- charset, collate : utf8, utf8_general_ci로 설정해야 한글 입력됨
- 알아서 id를 기본 키로 연결하므로 id 컬럼은 적어줄 필요가 없음
- associate : 다른 모델과의 관계
타입 비교 | MySQL | 시퀄라이즈 |
ㅤ | VARCHAR(100) | STRING(100) |
ㅤ | INT | INTEGER |
ㅤ | TINYINT | BOOLEAN |
ㅤ | DATETIME | DATE |
ㅤ | INT UNSIGNED | INTEGER.UNSIGNED |
ㅤ | NOT NULL | allowNull: false |
ㅤ | UNIQUE | unique: true |
ㅤ | DEFAULT now() | defaultValue: Sequelize.NOW |
2-1. index.js와 모델 연결
const Sequelize = require('sequelize'); const User = require('./user'); const Comment = require('./comment'); 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.User = User; db.Comment = Comment; User.init(sequelize); Comment.init(sequelize); User.associate(db); Comment.associate(db); module.exports = db;
3. 관계 정의
// User : Comment = 1 : N 관계 static associate(db) { db.User.hasMany(db.Comment, { foreignKey: 'commenter', sourceKey: 'id' }); } static associate(db) { db.Comment.belongsTo(db.User, { foreignKey: 'commenter', targetKey: 'id'}); }
- foreignKey가 생기는 테이블에서 belongsTo를 호출 ⇒ 즉 Comment 테이블에 commenter foreignKey 추가됨
- sourceKey의 id와 targetKey의 id 모두 User 모델의 id 임
1:1 관계
db.User.hasOne(db.Info, { foreignKey: 'UserId', sourceKey: 'id' }); db.Info.belongsTo(db.User, { foreignKey: 'UserId', targetKey: 'id' });
- belongsTo를 사용하는 Info 모델에 UserId 컬럼이 추가됨
N:M 관계
db.Post.belongsToMany(db.Hashtag, { through: 'PostHashtag' }); db.Hashtag.belongsToMany(db.Post, { through: 'PostHashtag' });
- N : M 관계 특성상 새로운 모델이 생성됨. through 속성에 새로운 모델의 이름이 명시됨
- 새로 생성된 PostHashtag 모델에 게시글과 해시태그의 아이디가 저장됨
- 즉 1: N, M : 1 로 푼다는 의미
4. 쿼리
CRUD
/* db.sequelize = sequelize; db.User = User; db.Comment = Comment; User.init(sequelize); Comment.init(sequelize); User.associate(db); Comment.associate(db); module.exports = db; */ const { User } = require('../models'); // Create User.create({ name: 'zero', age: 24, married: false, comment: '자기소개1', }); // Read User.findAll({}); User.findOne({}); User.findAll({ attributes: ['name', 'married'], }); // select name, married from nodejs.users; const { Op } = require('sequelize'); const { User } = require('../models'); User.findAll({ attributes: ['name', 'age'], where: { married: true, // Mysql 에서 undefined 지원하지 않으니, 빈 값넣으려면 null로 넣어야 함 age: { [Op.gt]: 30 }, }, }); User.findAll({ attributes: ['id', 'name'], where: { [Op.or]: [{ married: false }, { age: {[Op.gt]: 30} }], }, }); User.findAll({ attributes: ['id', 'name'], order: ['age', 'DESC'], limit: 1, offset: 1, }); // Update User.update({ comment: '바꿀 내용', }, { where: { id : 2}, }); // Delete User.destroy({ where: { id : 2 }, });
Select
- attributes는 nested array를 통해 renaming이 가능함
Model.findAll({ attributes: ['foo', ['bar', 'baz'], 'qux'] });
sequelize.fn
을 이용해서 aggregation 도 가능
User.findAll({ attributes: [ 'workspaceId', 'workspaceZoneId', [sequelize.fn('COUNT', 'workspaceId'), 'workspaceCount'], [sequelize.fn('COUNT', 'workspaceZoneId'), 'workspaceZoneCount'], ], group: ['workspaceId', 'workspaceZoneId'], where: { organizationId: organizationId, connected: true, workspaceId: Array.from(workspaceMap.keys()) }, raw: true, // Return raw result. See sequelize.query for more information. }); // according SQL SELECT `workspace_id` AS `workspaceId`, `workspace_zone_id` AS `workspaceZoneId`, COUNT('workspaceId') AS `workspaceCount`, COUNT('workspaceZoneId') AS `workspaceZoneCount` FROM `users` AS `User` WHERE `User`.`connected` = true AND `User`.`organization_id` = 1 AND `User`.`workspace_id` IN (1, 2, 3, 4, 5) GROUP BY `workspaceId`, `workspaceZoneId`;
raw 옵션 [참고]
raw 옵션이 true 면 데이터가 그대로 반환되게 되고
raw : false → Model 객체로 감싸져 있기 때문에, get() 메서드 호출해야 데이터에 접근이 가능함


JOIN
const user = await User.findOne({ include : [{ model : Comment, }] }); // 또는 다음과 같이 가져올 수 있음 const user = await User.findOne({}); const comments = await user.getComments(); console.log(comments);
- 어떤 모델과 관계가 있는지를 include 배열에 넣어주면 됨
Raw SQL
const [result, metadata] = await sequelize.query('SELECT * from comments'); console.log(result);
Transaction
[ Sequelize ] Transaction
- Sequelize.transaction() 으로 transaction 만들 수 있음
- Model의 메서드에 options.transaction 으로 해당 transaction을 명시해서 트랜잭션 안에서 쿼리 실행되도록 설정 가능함
let workspace = await Workspace.findOne({ where: { id: workspaceId, organizationId: organizationId }, transaction: t, });