[Sequelize] 기본개념 / MySQL Query 대신에 Sequelize로 구현한 후기 및 코드 공유
※ Sequelize 개념 소개
▶ 우선, Sequelize란 Node.js ORM 프레임워크이다.
기본적으로 DB연결은 네트워크 통신을 필요로하고, 따라서 비동기 처리를 위해 Promise 기반으로 만들어져있다.
▶ Sequelize 하나로 다양한 DBMS (Postgres, MySQL, MariaDB, SQLite, Microsoft SQL Server)를 지원하지만,
각 SQL을 DBMS 특성마다 성능을 최적화시킬 수 없다는 아쉬움이 있다.
Manual | Sequelize
Constraints & Circularities Adding constraints between tables means that tables must be created in the database in a certain order, when using sequelize.sync. If Task has a reference to User, the User table must be created before the Task table can be crea
sequelize.org
※ 프로젝트 적용 후기
▶ Sequelize를 사용하면 SQL문을 직접 작성하지 않아도 된다는 장점(?)이 있지만, SQL을 잘 아는 개발자라면 굳이 사용할 필요가 있나 싶었다. 그러나, 공부에 의의를 두고 NodeJS 기반의 프로젝트를 진행하면서 모든 SQL문을 적용해보았다.
▶ 프로젝트에 Sequelize 적용한 후기 :
생각보다 Sequelize Documentation이 다양한 SQL문을 다루지 않았고,
구글 선생님도 그다지 도움되지 않아서 혼자 고군분투해야 했다.
직접 SQL 쿼리를 다이렉트로 작성할 수도 있지만, 포기하지 않고 Sequelize로 모든 쿼리를 변환한것이 참 뿌듯했다.^^
※ MySQL <-> Sequelize 플젝 시 구현한 코드 공유
1. 주문 횟수가 가장 많은 TOP5 회원 구하기
getVipMembers: async function(){
try{
// Member와 OrderMain 조인
// 다(N).findAll
const result = await db.OrderMain.findAll({
attributes: [
'Member.member_name', 'order_memberid', 'Member.member_email',
[db.Sequelize.fn('count', db.Sequelize.col('order_memberid')), 'cnt']
],
include: [{
model: db.Member
}],
group: ['order_memberid'],
order: [[db.Sequelize.literal('cnt'), 'DESC']],
limit: 5
});
return result;
}catch(error){
throw error;
}
}
▶ 주의) include를 사용하려면 모델에 관계설정이 필요하다.
class OrderMain extends Model {
static associate(models) {
// 다 대 일(Member)
models.OrderMain.belongsTo(models.Member, {foreignKey:"order_memberid", targetKey:"member_id"});
}
}
2. 최근 한 달간 가입한 회원 수 구하기
getRecentJoinCount: async function(){
try{
// 한달 전 날짜 구하는 로직
const curr = new Date();
const lastDayofLastMonth = (new Date(curr.getYear(), curr.getMonth(), 0)).getDate();
if(curr.getDate() > lastDayofLastMonth) {
curr.setDate(lastDayofLastMonth);
}
curr.setMonth(curr.getMonth() - 1);
const result = await db.Member.count({
where: {
join_date: { [Op.gte]: curr}
}
});
return result;
}catch(error){
throw error;
}
}
3. 총 판매 금액 구하기
sumtotalprice: async function(){
try{
const result = await db.OrderMain.sum('order_price');
return result;
}catch(error){
throw error;
}
}
4. 최근 한 달 판매량 (해당 월) 구하기
totalpriceByMonth: async function(targetMonth){
try{
const result = await db.OrderMain.findOne({
attributes: [
[sequelize.fn('month', sequelize.col("order_date")), 'month'],
[sequelize.fn('sum', sequelize.col("order_price")), 'sum']
],
where: {
[Op.and]: [
sequelize.where(sequelize.fn('month', sequelize.col('order_date')), targetMonth)
]
}
});
return result.dataValues;
}catch(error){
throw error;
}
}
5. 최근 한 달 주문수 (해당 월) 구하기
salesCountByMonth: async function(targetMonth){
try{
const result = await db.OrderMain.findOne({
attributes: [
[sequelize.fn('month', sequelize.col("order_date")), 'month'],
[sequelize.fn('count', sequelize.col("order_id")), 'count']
],
where: {
[Op.and]: [
sequelize.where(sequelize.fn('month', sequelize.col('order_date')), targetMonth)
]
}
});
return result.dataValues;
}catch(error){
throw error;
}
}
6. 조건에 따른 정렬 구현하기
- 주의) order 속성을 const가 아닌 let으로 선언하기
list: async function(pager, orderType){
try{
let order = [["sabang_buycount", "DESC"]];
if(orderType){
if(orderType == 'view'){
order = [["sabang_viewcount", "DESC"]];
}else if(orderType == 'high'){
order = [["sabang_saleprice", "DESC"]];
}else if(orderType == 'low'){
order = [["sabang_saleprice", "ASC"]];
}
}
const result = await db.Sabang.findAll({
order,
limit: pager.rowsPerPage,
offset: pager.startRowIndex
});
return result;
}catch(error){
throw error;
}
}
7. 삭제할때 delete 아니고 destroy
deleteProduct: async function(product){
try{
// 1. 상품 테이블 삭제
await db.Product.destroy({where: {product_id: product.product_id}});
// 2. 사방 테이블 가격 갱신
await db.Sabang.decrement({
sabang_price: parseInt(product.product_price),
sabang_saleprice: parseInt(product.product_price)
}, {where:{sabang_id: product.sabang_id}});
}catch(error){
throw error;
}
}
8. 생성
createProduct: async function(product){
try{
// 1. 상품 테이블 등록
const dbBoard = await db.Product.create(product);
// 2. 사방 테이블 가격 갱신
await db.Sabang.increment({
sabang_price: parseInt(product.product_price),
sabang_saleprice: parseInt(product.product_price)
}, {where:{sabang_id: product.sabang_id}});
return dbBoard;
}catch(error){
throw error;
}
}
9. 주문 Id를 이용해 주문 정보와 회원 정보 동시에 가져오기
getOrderWithMemberInfo: async function(order_id){
try{
// 다 대 일 associate 관계 설정 이용
const order = await db.OrderMain.findOne({where: {order_id}});
order.dataValues.Member = await order.getMember();
return order;
}catch(error){
throw error;
}
}
▶ 주의) OrderMain 클래스에 "다대일" 관계 설정을 해야 getMember() 메소드(관계 설정 시 자동 생성)를 사용할 수 있다.
class OrderMain extends Model {
static associate(models) {
// 다 대 일(Member)
models.OrderMain.belongsTo(models.Member, {foreignKey:"order_memberid", targetKey:"member_id"});
}
}