모든 예시에는 아래 모델이 정의됐다고 가정하고 예시 코드를 작성하겠습니다.
const { Sequelize, DataTypes, Model } = require("sequelize");
const config = {
username: "sequelizeUser",
password: "sequelize",
database: "sequelizeDB",
host: "127.0.0.1",
port: 3306,
dialect: "mysql",
logging: false, // SQL문 로그를 콘솔에 표시하지 않음
freezeTableName: true, // sequelize의 모델명과 DB의 테이블명을 일치하게 해줌
};
const { database, username, password } = config;
const sequelize = new Sequelize(database, username, password, config);
// define방식으로 유저 모델 생성 ( sequelize에서만 생성한 것 )
const User = sequelize.define("User", {
firstName: { type: DataTypes.STRING, allowNull: false },
lastName: { type: DataTypes.STRING },
age: { type: DataTypes.INTEGER, defaultValue: 0 }
}, {
// options
});
// class방식으로 게시글 모델 생성 ( sequelize에서만 생성한 것 )
class Post extends Model {}
Post.init(
{
contents: {
type: DataTypes.STRING,
allowNull: false,
},
},
{
sequelize,
// options
},
);
Node.js
의 ORM
도구입니다.
npm i sequelize sequelize-cli
드라이버
1. mysql
: npm i mysql2
2. Postgres
: npm i pg pg-hstore
3. mariadb
: npm i mariadb
sequelize-cli
를 설치했을 경우 npx sequelize init
를 실행하면 기본 폴더들과 기본 코드가 생성됩니다.
// config 예시
const config = {
"username": "nodebirdUser",
"password": "nodebird",
"database": "nodebirdDB",
"host": "127.0.0.1",
"port": 3306,
"dialect": "mysql",
logging: false, // SQL문 로그를 콘솔에 표시하지 않음
freezeTableName: true, // sequelize의 모델명과 DB의 테이블명을 일치하게 해줌
};
const { database, username, password } = config;
const Sequelize = require("sequelize");
const sequelize = new Sequelize(database, username, password, config)
아래 모델 생성 부분부터 보고 해당 내용을 보는 것이 좋습니다.
동기화 한다는 의미는 sequelize
에 정의한 모델을 실제로 DB
에 생성한다는 것을 의미합니다.
// 특정 모델만 동기화
User.sync
.sync({ force: false, alter: false })
.then(() => console.log("User 테이블 생성 완료"))
.catch(console.error);
// sequelize에 정의한 모든 모델 동기화
sequelize
.sync({ force: false, alter: false })
.then(() => console.log("DB 연결 성공!"))
.catch(error => console.error("DB 연결 실패 >> ", error));
sequelize
에 정의한 모델에 해당하는 테이블을 삭제하는 방법입니다.
(async () => {
// 특정 모델만 삭제
await User.drop();
// 전체 모델 삭제
await sequelize.drop();
})();
sequelize.defind()
이나 extends Model
둘 중에 아무거나 사용해도 결과적으로 같은 코드입니다.
정의할게 type
밖에 없는 경우에는 생략 가능합니다.
primary key
가 없는 경우 자동적으로 integer
의 auto_increment
를 적용한 id
컬럼이 생성됩니다.
type
: 타입 정의 ( DataTypes
을 사용함 )allowNull
: null
허용 여부unique
: uniqueKey
적용 여부defaultValue
: 기본값 적용primary
: primaryKey
적용 여부autoIncrement
: auto_increment
적용 여부문자열
1. DataTypes.STRING
: varchar(255)
2. DataTypes.STRING(20)
: varchar(20)
3. DataTypes.TEXT
: text
불리언
1. DataTypes.BOOLEAN
: tinyint(1)
숫자
1. DataTypes.INTEGER
: integer
2. DataTypes.BIGINT
: bigint
3. DataTypes.FLOAT
: float
4. DataTypes.DOUBLE
: double
시간
1. DataTypes.DATETIME
: datetime
2. DataTypes.NOW
기본 동작은 sequelize
모델명의 복수형태로 테이블을 생성하는데 그것을 막아주는 옵션입니다.
즉, 모델명과 같은 이름의 테이블 생성해줍니다.
동기화할 경우 DB
의 테이블명을 직접 지정하는 옵션입니다.
sequelize
의 모델명을 직접 지정하는 옵션입니다.
createdAt
, updatedAt
컬럼 추가 및 자동으로 처리해줍니다.
단, 데이터를 추가할 경우 자동으로 updatedAt
이 업데이트되지만 DB
에 default로 값이 들어가 있는 것은 아닙니다.
bool
값을 넣으면 컬럼추가이고, 문자열을 넣으면 해당 문자열을 이름으로 갖는 컬럼을 생성합니다.
기본 형태는 sequelize.define(모델명, 테이블정의, 테이블옵션)
입니다.
const { Sequelize, DataTypes } = require("sequelize");
const config = {
username: "sequelizeUser",
password: "sequelize",
database: "sequelizeDB",
host: "127.0.0.1",
port: 3306,
dialect: "mysql",
};
const { database, username, password } = config;
const sequelize = new Sequelize(database, username, password, config);
// define을 이용한 모델 생성
const User = sequelize.define(
// 모델명
"User",
// 컬럼 정의
{
_id: {
type: DataTypes.INTEGER.UNSIGNED,
allowNull: false,
primaryKey: true,
autoIncrement: true,
},
name: {
type: DataTypes.STRING(30),
allowNull: true,
unique: true,
},
},
// 옵션값
{
charset: "utf8",
collate: "utf8_general_ci",
}
);
const { Sequelize, DataTypes, Model } = require("sequelize");
const config = {
username: "sequelizeUser",
password: "sequelize",
database: "sequelizeDB",
host: "127.0.0.1",
port: 3306,
dialect: "mysql",
};
const { database, username, password } = config;
const sequelize = new Sequelize(database, username, password, config);
class Post extends Model {}
Post.init(
// 컬럼 정의
{
contents: {
type: DataTypes.STRING,
allowNull: false,
},
},
// 옵션값
{
sequelize,
},
);
클래스로 모델을 정의했다고 해서 new
를 이용해서 모델을 생성하면 안 됩니다.
이유는 모르겠지만 공식 문서에 명시해놨습니다.
build()
save()
create()
// 모델 인스턴스 생성 => 동기처리 ( DB에 생성하는 것이 아니기 때문에 비동기로 처리할 이유가 없음 )
const userModel = User.build({ firstName: "john", lastName: "smith" });
// 생성한 모델 인스턴스 동기화
(async () => {
await userModel.save();
})();
// 모델 인스턴스 생성과 동기화 동시 처리
(async () => {
const createdUser = await User.create({ firstName: "john", lastName: "smith" });
})();
// 출력팁 ( 스코프에 맞진 않지만 출력된다고 가정함 )
// 생성된 결과물을 확인하고 싶을 때 그냥 반환값을 콘솔에 찍으면 복잡한 형태로 찍힘
// 따라서 아래처럼 변형시켜서 찍으면 정확한 결과값만 볼 수 있음
console.log(createdUser.toJSON());
console.log(JSON.stringify(createdUser, null, 4));
set()
update()
(async () => {
const createdUser = await userModel.create({ firstName: "john", lastName: "smith" });
// 방법 1) 직접 변경
createdUser.firstName = "aaaa";
// 방법 2) set() 이용해서 한번에 변경
createdUser.set({
firstName: "bbbb",
lastName: "cccc",
});
// 방법1, 2를 통한 변경사항 동기화
await createdUser.save();
// 방법 3) 변경과 동기화 동시 실행
await createdUser.update({
firstName: "dddd",
lastName: "eeee",
});
})();
destory()
reload()
(async () => {
const createdUser = await userModel.create({ firstName: "john", lastName: "smith" });
// reload
console.log(createdUser.firstName); // "john"
createdUser.firstName = "aaa";
console.log(createdUser.firstName); // "aaa"
await createdUser.reload();
console.log(createdUser.firstName); // "john"
// 생성한 모델 인스턴스 삭제 및 동기화
await createdUser.destroy();
})();
increment()
decrement()
(async () => {
const createdUser = await userModel.create({ firstName: "john", lastName: "smith", age: 10 });
// 방법 1)
await createdUser.increment("age", { by: 2 });
// 방법 2)
await createdUser.increment({ age: 2 });
// decrement는 반대로 작동
})();
해당 테이블의 모든 데이터를 찾습니다.
// SELECT * FROM Users;
(async () => {
const users = await User.findAll();
})();
해당 테이블의 특정 primaryKey
에 해당하는 데이터를 가져옵니다.
// SELECT * FROM Users WHERE id = 1;
// 현재 primary key가 id라서 위처럼 연산함
(async () => {
const user = await User.findByPk(1);
})();
해당 테이블에서 조건에 제일 처음 만족하는 데이터를 가져옵니다.
// SELECT * FROM Users WHERE age = 20 LIMIT = 1;
(async () => {
const user = await User.findOne({ where: { age: 20 } });
})();
없으면 생성하고 있으면 그냥 가져오며, 생성여부를 같이 반환함
(async () => {
const [userData, created] = await User.findOrCreate({ where: { id: 1 } });
})();
(async () => {
await User.create({
nickname: "any",
email: "a@naver.com",
password: "암호화된 비밀번호"
});
})();
(async () => {
await User.update(
{
nickname: "any"
}, {
where: { id: 1 },
});
})();
삭제한 컬럼의 개수를 반환합니다.
(async () => {
await User.destroy({
where: { id: 1 },
});
await User.destroy({
truncate: true,
});
})();
특정 컬럼에 대한 검색, as
, join
연산 등에 대한 사용법을 정리하겠습니다.
특정 row
만 가져올 때 사용하는 속성입니다.
// SELECT firstName, createdAt FROM Users;
User.findAll({
attributes: ["firstName", "createdAt"],
});
// SELECT firstName, createdAt as `생성 날짜` FROM Users;
User.findAll({
attributes: ["firstName", ["createdAt", "생성 날짜"]],
});
// SELECT id, firstName, lastNAme, createdAt FROM Users;
User.findAll({
attributes: {
exclude: ["updatedAt"],
},
});
조건에 해당하는 column
만 가져오는 속성입니다.
동등 비교인 [Op.eq]
는 생략이 가능하기 때문에 이후 예시에서는 생략하겠습니다.
// SELECT * FROM Users WHERE id = 1;
User.findAll({
where: {
id: {
[Op.eq]: 1
}
}
});
// [Op.eq]는 생략
User.findAll({
where: {
id: 1
}
});
AND
연산입니다.
// SELECT * FROM Users WHERE id = 1 AND age = 0;
User.findAll({
where: {
[Op.and]: [
{
id: {
[Op.eq]: 1,
},
},
{
age: {
[Op.eq]: 0,
},
},
]
},
});
// [Op.eq]는 생략
User.findAll({
where: {
[Op.and]: [
{
id: 1
},
{
age: 0
},
]
},
});
User.findAll({
where: {
[Op.or]: [
{ id: 1 },
{ firstName: "john" }
]
}
});
Op.ne
: !=Op.is
: is null?Op.not
: is false?Op.gt
: >Op.gte
: >=Op.lt
: <Op.lte
: <=Op.between
: betweenOp.notBetween
: notBetweenOp.in
: 포함 여부Op.notIn
: 비포함 여부이외에도 많이 있으니 필요시 찾아서 사용하면 됩니다.
const users = await User.findAll({
where: [sequelize.where(sequelize.fn("char_length", sequelize.col("firstName")), 4)],
});
(async () => {
// SELECT * FROM Users ORDER BY age DESC, _id DESC;
const users = await User.findAll({
order: [
["age", "DESC"],
["_id", "DESC"],
],
});
})();
// include를 사용한 경우 가장 바깥에 "[모델, 컬럼, 기준]"순서로 적어주면 됩니다.
(async () => {
// SELECT * FROM Users INNER JOIN Posts ON Users._id = Posts.UserId ORDER BY Users.createdAt DESC, Posts.createdAt ASC
const users = await User.findAll({
attributes: ["_id", "name", "createdAt"]
include: {
model: Post,
attributes: ["_id", "content", "createdAt"],
// 여기에다가 order 적으면 오류도 안 나고 아무 효과도 없음 ( 아래코드 효과없음 )
order: ["crteatedAt", "ASC"]
},
// 여기처럼 가장 바깥에다가 정렬 기준을 적어줘야 함
order: [
["createdAt", "DESC"]
[Post, "createdAt", "ASC"],
],
});
})();
// 5번째부터 5개 읽기
(async () => {
await User.findAll({ limit: 5, offset: 5 });
})();
(async () => {
// SELECT * FROM Users LIMIT 2, 3
await User.findAll({ limit: 3, offset: 2 });
// SELECT COUNT(*) AS `count` FROM Users WHERE age = 0;
await User.count({ where: { age: 0 } });
// SELECT MAX(age) AS `max` FROM Users;
await User.max("age");
// SELECT MIN(age) AS `min` FROM Users;
await User.min("age");
// SELECT SUM(age) AS `sum` FROM Users;
await User.sum("age");
// UPDATE Users SET age = age + 5, updatedAt='2021-12-26 03:29:27' WHERE _id = 1;
await User.increment({ age: 5 }, { where: { id: 1 } });
// decrement는 반대로 작동
})();
=== >> 여기부터 이어서 정리하기 << ===
추후에 필요시 사용해보고 작성 예정
유효성 검사는 sequelize
에서 검사하고 실패시 DB
로 쿼리자체를 보내지 않음
제약 조건은 DB
에서 검사하고 실패시 sequelize
에서 받아서 js
로 변환해서 보여줌
추후에 필요시 사용해보고 작성 예정
간단한 쿼리는 sequelize.query()
를 이용해서 결과를 얻을 수 있다.
(async () => {
const [results, metadata] = await sequelize.query("SELECT * FROM Users;");
})();
필수적인 부분은 아니라고 생각해서 자세한건 직접 찾아보기
기본적으로 모델명Id
라는 이름을 가진 foregin key
가 생성된다.
일대일관계를 정의할 때 사용한다.
일대일관계일 경우 foregin key
가 어디에 있어도 상관없으므로 본인 판단에 의해서 foregin key
를 넣을 테이블을 정하면 된다.
BelongsTo()
를 사용한 모델에 foregin key
가 붙게 된다.
// 만약 유저가 게시글을 하나만 작성할 수 있다고 가정하고 진행
User.hasOne(Post);
Post.belongsTo(User);
/**
* 위처럼 코드 작성시 Post테이블에 UserId라는 컬럼이 생기고
* 해당 컬럼이 user.id를 참조하는 foreign key가 된다.
*/
BelongsTo
를 정의한 모델에 foreignKey
가 생성됨
User.hasMany(Post);
Post.belongsTo(User);
// 여기서도 마찬가지로 Post테이블에 UserId가 생긴다.
새로운 모델이 생성되며, through
속성을 통해서 모델명을 정해야 함
User.belongsToMany(Post, { through: "UserPosts" });
Post.belongsToMany(User, { through: "UserPosts" });
// UserPosts라는 중간 테이블이 생기고 UserId, PostI라는 foreignKey를 컬럼으로 가진다.
위 4가지 함수의 두 번째 인자로 옵션을 지정해 줄 수 있다.
RESTRICT
, CASCADE
, SET DEFAULT
, SET NULL
값을 줄 수 있으며,
onDelete
는 SET NULL
, onUpdate
는 CASECADE
가 기본값이다.
관계에서 기본적으로 제공되는 foregin key
의 값을 변경시키는데 사용한다.
// 둘중에 하나만 적거나 { name: }부분은 생략해도 된다.
// foreginkey의 type, allowNull, defaultValue 등을 지정할 수 있다.
Foo.hasOne(Bar, {
foreignKey: {
name: 'myFooId'
}
});
Bar.belongsTo(Foo, {
foreignKey: {
name: 'myFooId'
}
});
N : M
관계일 경우에 새로 생성될 중간 테이블의 이름을 정한다.
중간 테이블은 관계를 가지는 두개의 테이블의 식별자를 foregin key
로 갖는 컬럼 두개를 가진다.
foregin key
에 대한 별칭을 지정한다.
아직 글만 읽어서 추후에 테스트 후 작성함
as
설정을 줬을 경우 모델명대신 설정한 이름으로 메서드명이 바뀐다.
userInstance.getPost();
userInstance.setPost();
userInstance.createPost();
userInstance.getPost();
userInstance.setPost();
userInstance.createPost();
userInstance.getPosts();
userInstance.countPosts();
userInstance.hasPost();
userInstance.hasPosts();
userInstance.setPosts();
userInstance.addPost();
userInstance.addPosts();
userInstance.removePost();
userInstance.removePosts();
userInstance.createPost();
userInstance.getPosts();
userInstance.countPosts();
userInstance.hasPost();
userInstance.hasPosts();
userInstance.setPosts();
userInstance.addPost();
userInstance.addPosts();
userInstance.removePost();
userInstance.removePosts();
userInstance.createPost();
// User와 Post가 1 : N 관계일 경우 ( foreginKey: UserId )
(async () => {
// 유저 생성
const createdUser = await User.create({ name: "user1" });
// 게시글 생성
const createdPost1 = await Post.create({ content: "content1" });
const createdPost2 = await Post.create({ content: "content2" });
// 관계 메서드 사용 예시
await createdUser.getPosts(); // null
await createdUser.countPosts(); // 0
await createdUser.hasPosts(); // false
await createdUser.setPosts([creatdPosts1, createdPost2]); // 유저 정보 반환
await createdUser.addPosts([creatdPosts1, creatdPosts2]); // 유저 정보 반환
// remove의 반환값이 뭔지 도저히 모르겠음
// posts의 컬럼이 삭제되는 것이 아니고 관계를 가지는 값만 제거됨 ( UserId )
await createdUser.removePosts([createdPosts1, createPost2]);
await createdUser.createPost({ content: "생성할 게시글의 컨텐츠" }); // 유저와 게시글이 join된 값이 반환됨
})();
관계를 지정했을 경우 사용 가능하며, join
연산을 해줍니다.
// 유저와 게시글 1 : N
const userWithPosts = await User.findAll({ include: Post });
const postsWithUser = await Post.findAll({ include: User });
// userWithPosts
[
{
_id,
name,
Posts: [{ _id, content }, { _id, content }]
},
// ...
]
// postsWithUser
[
{
_id,
content,
User: { _id, name }
},
// ...
]
model
: 어떤 모델을 조인할지 지정하는 속성값입니다.as
: 모델의 별칭을 지정한 경우 적어주는 속성값입니다.required
: inner join
, outer join
을 결정하는 속성값입니다.right
: outer join
일 경우 right outer join
을 결정하는 속성값입니다.all
, nested
: 연관된 모든 테이블을 가져올지 결정하는 속성값입니다.include
내부에서 where
사용 시 join
의 on
에 조건이 들어가게 됩니다.
최상위 where
에 조건을 넣는 방법은 최상위 where
에서 $테이블명.컬럼$
형태로 사용하면 됩니다.
N : M
관계에서 include
를 사용하면 중간 테이블의 모든 데이터를 가져오게 됩니다.
중간 테이블의 데이터를 관리하려면 through
와 attritures
를 사용하면 됩니다.
targetKey
sourceKey
sequelize.authenticate()
try {
await sequelize.authenticate();
console.log('Connection has been established successfully.');
} catch (error) {
console.error('Unable to connect to the database:', error);
}
sequelize.close();
: 연결 끊기sequelize.drop()
: 모든 테이블 삭제모델.drop()
: 해당 테이블과 관련된 테이블 삭제getter/setter는 아직 테스트안해봄