✔ 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
'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 |
댓글