본문 바로가기
Node.js

Node.js 교과서 4주차

by WhoamixZerOne 2022. 6. 9.

✔ Sequelize

Sequelize는 DB 작업을 편리하게 사용할 수 있도록 도와주는 ORM 라이브러리이다.

MySQL, PostgreSQL, MariaDB 등 많은 RDBMS를 지원하고 Promise 기반으로 구현되었기 때문에 비동기 로직을 편리하게 작성할 수 있다.

  • "$ npm i sequelize mysql2"
  • "$ npm i -D sequelize-cli"
  • "$ npx sequelize init" (디렉터리 구조 초기화)

위의 명령어로 sequelize와 mysql 드라이버를 설치하고 sequelize-cli는 sequelize 명령 실행에 필요하기에 개발용에 설치해준다.

디렉터리 구조는 config, migrations, models, seeders 4개의 디렉터리가 생성된다.

  • config/config.json : DB 연동 시 필요한 환경 설정
  • migrations : 데이터베이스 테이블에 필드 정보가 변경되거나 구조가 변경되는 등과 같이 특수한 상황에서 사용
  • models : 테이블(Model)을 정의하고 관계를 설정
  • seeders : 서버를 실행하거나 콘솔 창에서 명령어를 실행 시 sequelize를 통해 DB에 데이터를 생성할 때 사용

config.json 파일을 수정한 뒤에, "$ npx sequelize db:create" 명령어를 통해 스키마(Database) 생성한다.

테스트 db 스키마의 경우는 "$ npx sequelize db:create --env test" 와 같이 진행한다.

테이터 테이블 Type 비교

MySQL Sequelize
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

Model 생성

// models/index.js
const Sequelize = require('sequelize')
const env = process.env.NODE_ENV || 'development'
const config = require('../config/config')[env]
const User = require('./user')

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

db.sequelize = sequelize

User.init(sequelize)
User.associate(db)

module.exports = db
// models/user.js
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
      },
      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'
    })
  }
}
// app.js
const { sequelize } = require('./models')
sequelize
  .sync({ force: false }) // 테이블 생성(이미 존재하는 경우 먼저 삭제)
  .then(() => { console.log('DB connect success') }
  .catch((err) => { console.error(err) })

모델 옵션 정의

  • sequelize - 연결 인스턴스(db.sequelize) 전달
  • timestamps: (boolean) - createdAt & updatedAt 컬럼 생성(데이터 추가, 수정에 대해 자동)
  • paranoid: (boolean) - deletedAt 컬럼 생성 / 데이터를 삭제하지 않고 삭제되는 시점을 등록(백업 서버, 데이터 복구를 위해 사용) / 조건은 timestamps가 true일 때 가능
  • underscored: (boolean) - default는 카멜 케이스로 생성 / true이면 스네이크 케이스로 생성
  • modelName: '모델명' - 모델 명
  • tableName: '테이블명' - 테이블 명
  • charset: 'utf8mb4' - 인코딩(mb4 이모티콘)
  • collate: 'utf8mb4_general_ci - 인코딩

관계 정의하기

  • 1:1 관계 hasOne() < - > belongsTo()
  • 1:N 관계 hasMany() < - > belongsTo()
  • N:M 관계 belongsToMany(, { through: '교차 테이블명' })
// user.js
// 1:1 관계
// User은 Profile을 하나 가질 수 있다(hasOne)
static associate(db) {
  db.User.hasOne(db.Profile)
}

// profile.js
// Profile은 User에 대해 속한다(belongsTo)
static associate(db) {
  db.Profile.belongsTo(db.User)
}
// user.js
// 1:N 관계
// User은 Post을 여러 개 가질 수 있다(hasMany)
static associate(db) {
  db.User.hasMany(db.Post)
}

// post.js
// Post은 User에 대해 속한다(belongsTo)
static associate(db) {
  db.Post.belongsTo(db.User)
}
// user.js
// N:M 관계
// User은 팔로워, 팔로우을 여러 개 가질 수 있다(belongsToMany)
static associate(db) {
  db.User.belongsToMany(db.User, {
    foreignKey: 'followingId',
    as: 'Followers',  // 상대 테이블의 이름 지정
    through: 'Follow'  // 교차 테이블 명
  })
  db.User.belongsToMany(db.User, {
    foreignKey: 'followerId',
    as: 'Followings',
    through: 'Follow'
  })
}

Join on

sequelize에서는 join을 include로 비슷한 기능을 수행할 수 있다(관계있는 것 엮을 수 있음)

// 사용자 가져오면서 댓글로 가져온다
const user = await User.findOne({
  include: [
    { model: Comment }
  ]
})
console.log(user.Comments) // 사용자 댓글

혹은 위 방법을 아래와 같이 사용할 수도 있다.

get+모델명으로 관계 있는 데이터 로딩 가능

// 쿼리를 2번 수행
const user = await User.findOne({})
const comments = await user.getComments()
console.log(comments) // 사용자 댓글

include 사용했을 때 join한 테이블의 컬럼명 사용 방법을 몰라서 삽질을 많이 했었다...

그래서 사용 방법을 추가하려고 한다.

먼저 사용 배경은 Company : Post(1:N)로 구성되어 있다.

await Post.findAll({
  include: {
    model: Company,
    as: 'companys',
    attributes: ['corp', 'country', 'area'],
    require: true, // inner join으로 require: true 사용
  },
  attributes: ['id', 'positions'],
  where: {
    [Op.or]: {
      [`$companys.corp$`]: {  // join한 테이블.컬럼명 접근
        [Op.like]: `${search}%`
      },
      positions: {
        [Op.like]: `${search}%`
      }
    }
  }
});

// Model
db.Post.belongsTo(db.Company, { as: 'companys' });

조건에서 join한 테이블의 컬럼명을 주기 위해선

[`$테이블애칭명.컬럼명$`]

위와 같이 해야 한다. 애칭은 as: '애칭명'으로 사용할 수 있는데

include만 적용해서는 안되고 Model 정의에 적용해줘야 한다.

 

아직도 경험이 부족해 보충할게 많을거라 생각한다.

또 필요한 내용이 있으면 추가할 예정이다.

 

 

🔗 Reference

 

Sequelize

Sequelize is a modern TypeScript and Node.js ORM for Postgres, MySQL, MariaDB, SQLite and SQL Server, and more. Featuring solid transaction support, relations, eager and lazy loading, read replication and more.

sequelize.org

 

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

[NestJS] Configuration 설정 & TypeORM 연결  (0) 2023.05.31
Node.js 구조 & 동작 원리  (0) 2022.08.17
Node.js 교과서 3주차  (0) 2022.06.01
Node.js 교과서 2주차  (0) 2022.05.25
Node.js 교과서 1주차  (1) 2022.05.14

댓글