HomeAboutMeBlogGuest
© 2025 Sejin Cha. All rights reserved.
Built with Next.js, deployed on Vercel
🤩
개발
/
NodeJs
NodeJs
/
Sequelize
Sequelize
/
기초(모델, 관계정의 & 쿼리)

기초(모델, 관계정의 & 쿼리)

1. Sequelize 셋업2. 모델 정의하기2-1. index.js와 모델 연결3. 관계 정의1:1 관계N:M 관계4. 쿼리CRUDSelectJOINRaw SQLTransaction

1. Sequelize 셋업

  1. npm i express sequelize sequelize-cli mysql2
  1. npx sequelize init : config, models, migrations, seeders 폴더 생성(sequelize-cli가 자동으로 만들어줌)
  1. models/index.js 아래와 같이 수정
    1. 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" } }
      config.json
  1. app.js에서 sequelize이용하여 db 연결
    1. 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 모델 정의
  • User 모델로 만들고 exports함
  • Sequelize.Model을 확장한 클래스로 선언
  • static init 메서드, static associate 메서드로 구성됨
    • static init 메서드 : 테이블에 대한 설정
      • 첫 번째 인수 : 테이블 컬럼 설정
        • 타입 비교
          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
      • 두 번째 인수 : 테이블 자체에 대한 설정
        • timestamps : true ⇒ createdAt과 updatedAt 컬럼 자동 추가
        • underscored: 테이블명, 컬럼명 기본은 camel case. 이를 스네이크 케이스로 바꾸어줌. true면
        • paranoid: true로 설정하면 deletedAt 이라는 컬럼이 생김. 로우를 삭제할 때 완전히 지워지지 않고 deletedAt에 지운 시각이 기록됨
        • charset, collate : utf8, utf8_general_ci로 설정해야 한글 입력됨
      • 알아서 id를 기본 키로 연결하므로 id 컬럼은 적어줄 필요가 없음
    • associate : 다른 모델과의 관계

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`;
      sequelize.fn 을 이용한 aggregation
      raw 옵션 [참고]
      raw 옵션이 true 면 데이터가 그대로 반환되게 되고
      raw : false → Model 객체로 감싸져 있기 때문에, get() 메서드 호출해야 데이터에 접근이 가능함
      notion image
      notion image

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, });