Sequelize 관계 정의하기
참고 글 : Sequelize - associate
Sequelize에서는 테이블 간의 관계 (1:1, 1:N, N:M)를 정의할 수 있습니다.
모델에 속하는 BeingTo, hasOne, hasMany, BelongsToMany 함수 중 하나를 호출하고 다른 모델을 첫 번째 인수로 제공하여 사용합니다.
1:N 관계
1:N 관계에서는 hasMany와 belongsTo 메서드를 사용합니다.
User와 Comment를 정의해보겠습니다.
한 User가 많은 댓글을 가질 수 있으니 User.hasMany를 사용하고, Comment는 한 User에 속할 수 있으니 Comment.belongsTo를 사용합니다.
즉, 외래키가 존재하는 테이블이 belogsTo를 사용합니다.
또한, 두 번째 매개변수로 다양한 옵션을 전달할 수 있습니다.
Options
ForeignKey : 외래 키를 직접 명시
foreignKey를 따로 지정하지 않는다면 모델명+기본 키로 이름이 자동 설정됩니다.
sourceKey : hasMany에서 사용하며 외래 키로 전할 속성을 명시
targetKey : belongsTo에서 사용하며 외래 키로 가져올 속성을 명시
onDelete, onUpdate : 엔티티의 상태 변화를 전파시키는 옵션, (RESTRICT, CASCADE, NO ACTION, SET DEFAULT, SET NULL)
1
2
3
db.User.hasMany(db.Comment, { foreignKey: "commenter", sourceKey: "id" });
db.Comment.belongsTo(db.User, { foreignKey: "commenter", targetKey: "id" });
1:1 관계
1:1 관계에서는 hasOne와 belongsTo를 사용합니다.
1
2
User.hasOne(Info);
Info.belongsTo(User);
M:N 관계
M:N 관계에서는 belongsToMany를 사용합니다.
Options
- through : 생성될 중간 모델(연결 테이블)의 이름을 명시
1
2
3
4
5
// Post
Post.belongsToMany(Hashtag, { through: "PostHashtag" });
// Hashtag
Hashtag.belongsToMany(Post, { through: "PostHashtag" });
예시로 User와 Post를 1 : N 관계로 설정해보겠습니다.
models/posts.js 생성
models 폴더에 posts.js 파일을 생성합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// posts.js
import { DataTypes, Model } from "sequelize";
class Post extends Model {
static init(sequelize) {
return super.init(
{
post_id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false,
// unique
// defaultValue
// validate
},
title: {
type: DataTypes.STRING(30),
allowNull: false,
},
content: {
type: DataTypes.STRING(30),
allowNull: false,
},
},
{
sequelize, // Sequelize 인스턴스
timestamps: false, // true : created_at, updated_at 컬럼이 생성되며 데이터의 생성, 수정 시 시간이 자동으로 입력됩니다.
paranoid: false /* true : deletedAt 컬럼이 생성되며 지운 시각이 기록됩니다. */,
underscored: true, // true : 카멜케이스 대신 스네이크케이스를 사용
freezeTableName: true, // 모델이름 변환 X, 지정한 이름 사용
ModelName: "Post", // 모델의 이름을 지정합니다.
tableName: "posts", // 테이블 이름 명시적 설정
}
);
}
static associate(db) {
db.Post.belongsTo(db.User, { foreignKey: "user_id", targetKey: "user_id" });
}
}
export default Post;
models/users.js
models 폴더의 users.js 파일에도 관계 설정 부분을 추가해줍니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// users.js
import { DataTypes, Model } from "sequelize";
class User extends Model {
static init(sequelize) {
return super.init(
{
user_id: {
type: DataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false,
// unique
// defaultValue
// validate
},
name: {
type: DataTypes.STRING(30),
allowNull: false,
},
age: {
type: DataTypes.INTEGER,
allowNull: true,
}
},
{
sequelize, // Sequelize 인스턴스
timestamps: false, // true : created_at, updated_at 컬럼이 생성되며 데이터의 생성, 수정 시 시간이 자동으로 입력됩니다.
paranoid: false // true : deletedAt 컬럼이 생성되며 지운 시각이 기록됩니다. ,
underscored: true, // true : 카멜케이스 대신 스네이크케이스를 사용
freezeTableName: true, // 모델이름 변환 X, 지정한 이름 사용
modelName: "User", // 모델 이름 명시적 지정
tableName: "users", // 테이블 이름 명시적 설정
}
);
}
static associate(db) {
db.User.hasMany(db.Post, { foreignKey: "user_id", sourceKey: "user_id" });
}
}
export default User;
index.js 수정
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import Sequelize from "sequelize";
// 클래스를 불러옵니다.
import User from "./users.js";
import Post from "./posts.js";
const config = {
username: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_DATABASE,
host: process.env.DB_HOST,
dialect: "mysql",
};
const db = {};
// new Sequelize를 통해 MySQL 연결 객체를 생성합니다.
const sequelize = new Sequelize(
config.database,
config.username,
config.password,
config
);
// 연결객체를 나중에 재사용하기 위해 db.sequelize에 넣어줍니다.
db.sequelize = sequelize;
// 모델 클래스를 넣어줍니다.
db.User = User;
db.Post = Post;
// 모델 정의
User.init(sequelize);
Post.init(sequelize);
// 모델간의 관계 정의
User.associate(db);
Post.associate(db);
export default db;
연결 테스트
에러 없이 posts 테이블에 외래 키로 user_id가 잘 생성된 것을 확인할 수 있습니다.
Sequelize로 CRUD 해보기
기존에 SQL문을 사용했던 CRUD (Create, Read, Update, Delete) 작업을 Sequelize에서는 JS 코드로 구현할 수 있습니다.
데이터 추가 (Create)
주로 create 메서드를 사용하여 레코드를 생성합니다.
create() 메서드
1 2 3 4 5 6 7 8
/* SQL */ INSERT INTO users (name, age) VALUES ('psmin', 27); /* 시퀄라이즈 */ db.User.create({ name: 'psmin', age: 27, });
findOrCreate() 메서드
findOrCreate() 메서드는 값이 없으면 생성하고, 값이 있다면 값을 가져옵니다.
데이터 조회 (findAll, findOne)
findAll(options)
findAll() 메서드는 모든 데이터를 조회합니다.
1 2 3 4 5 6 7 8
/* SQL */ SELECT * FROM users; /* 시퀄라이즈 */ let user = db.User.findAll({}); console.log(user[0].name) // findAll는 여러 행들을 조회하기에, 각 행들이 배열로 저장됩니다.
findOne(options)
findOne() 메서드는 데이터를 하나만 조회합니다.
1 2 3 4 5 6 7 8
/* SQL */ SELECT * FROM users limit 1; /* 시퀄라이즈 */ let user = db.User.findOne({}); console.log(user.name) // findOne는 하나만 조회하기에, 객체로 저장됩니다.
조건 조회
attributes : 가져올 속성를 배열 형태로 지정합니다.
1 2 3 4 5 6 7
/* SQL */ SELECT user_id, name FROM users; /* 시퀄라이즈 */ db.User.findAll({ attributes: ["user_id", "name"], });
where : 조건을 지정하여 특정 레코드만 가져옵니다.
1 2 3 4 5 6 7 8 9
/* SQL */ SELECT * FROM users WHERE user_id = 2; /* 시퀄라이즈 */ db.User.findAll({ where : { user_id : 2 } });
order : 결과를 정렬합니다. (2차원 배열 형태)
1 2 3 4 5 6 7
/* SQL */ SELECT * FROM users ORDER BY age DESC name ASC; /* 시퀄라이즈 */ db.User.findAll({ order:[['age':'DESC'], ['name':'ASC']], });
limit : 조회 결과의 개수를 제한합니다.
offset : 오프셋을 설정하여 페이징을 지원합니다.1 2 3 4 5 6 7 8
/* SQL */ SELECT * FROM users LIMIT 10 OFFSET 5; /* 시퀄라이즈 */ db.User.findAll({ limit: 10, offset: 5, });
특수연산자 (Op)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
const Op = Sequelize.Op
[Op.and]: [{a: 5}, {b: 6}] // (a = 5) AND (b = 6)
[Op.or]: [{a: 5}, {a: 6}] // (a = 5 OR a = 6)
[Op.gt]: 6, // > 6
[Op.gte]: 6, // >= 6
[Op.lt]: 10, // < 10
[Op.lte]: 10, // <= 10
[Op.ne]: 20, // != 20
[Op.eq]: 3, // = 3
[Op.is]: null // IS NULL
[Op.not]: true, // IS NOT TRUE
[Op.between]: [6, 10], // BETWEEN 6 AND 10
[Op.notBetween]: [11, 15], // NOT BETWEEN 11 AND 15
[Op.in]: [1, 2], // IN [1, 2]
[Op.notIn]: [1, 2], // NOT IN [1, 2]
[Op.like]: '%hat', // LIKE '%hat'
[Op.notLike]: '%hat' // NOT LIKE '%hat'
[Op.startsWith]: 'hat' // LIKE 'hat%'
[Op.endsWith]: 'hat' // LIKE '%hat'
[Op.substring]: 'hat' // LIKE '%hat%'
[Op.regexp]: '^[h|a|t]' // REGEXP/~ '^[h|a|t]' (MySQL/PG only)
[Op.notRegexp]: '^[h|a|t]' // NOT REGEXP/!~ '^[h|a|t]' (MySQL/PG only)
예시
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/* SQL */ SELECT name, age FROM users WHERE name = 'Jack' OR age > 30; /* 시퀄라이즈 */ User.findAll({ attributes: ['name', 'age'], where: { [Op.or]: [ { name : 'Jack' }, { age: { [Op.gt]: 30 } } ], // married = 0 or age > 30 }, });
데이터 수정 (update)
update 메서드를 사용해 데이터를 수정합니다.
첫 번째 인수는 수정할 내용, 두 번째 인수는 수정할 레코드를 특정할 조건을 작성합니다.
1
2
3
4
5
6
7
8
9
/* SQL */
UPDATE users SET name = '새로운 이름' WHERE user_id = 2;
/* 시퀄라이즈 */
db.User.update({
name : '새로운 이름',
}, {
where: { user_id: 2 },
});
데이터 삭제 (destroy)
destroy 메서드를 사용해 데이터를 삭제합니다.
1
2
3
4
5
6
7
/* SQL */
DELETE FROM users WHERE user_id = 2;
/* 시퀄라이즈 */
db.User.destroy({
where: { user_id: 2 },
});