주문하기컨트롤러를 설계하면서 3개의sql문을 한번에 실행해야하는 코드가 있습니다.
const order = (req,res)=>{
const {items,delivery,totalQuantity,totalPrice,userId,firstBookTitle}=req.body;
let delivery_id;
let order_id;
let sql ="INSERT INTO delivery(address,receiver,contact) VALUES(?,?,?)"
let values = [delivery.address, delivery.receiver, delivery.contact];
conn.query(sql,values,
(err,results,fields)=>{
if(err){
console.log(err);
return res.status(StatusCodes.BAD_REQUEST).end();
}
console.log(results);
delivery_id=results.insertId; //여기
})
sql = `INSERT INTO orders(book_title, total_quantity, total_price, user_id, delivery_id)
VALUE(?,?,?,?,?)`;
values =[firstBookTitle,totalQuantity,totalPrice,userId,delivery_id];
conn.query(sql,values,
(err,results,fields)=>{
if(err){
console.log(err);
return res.status(StatusCodes.BAD_REQUEST).end();
}
console.log(results);
order_id=results.insertId;//여기
})
sql =`INSERT INTO orderedBook(order_id,book_id,quantity) VALUE(?)`;
values=[];
items.forEach((item)=>{
values.push([order_id,item.book_id,item.quantity]);
})
conn.query(sql,values,
(err,results,fields)=>{
if(err){
console.log(err);
return res.status(StatusCodes.BAD_REQUEST).end();
}
return res.status(StatusCodes.OK).json(results);
})
};
현재 위 코드를 실행시키면 delivery_id와 order_id를 할당하기 전에 아래 sql문이 실행되어 값이 할당되지 않아 null이라는 값이 들어가 있다고 나옵니다. 이러한이유는 node의 논블로킹이라는 특징때문에 그렇습니다. 간단하게 말해 논블로킹이란 어떤 함수가 실행되는 중에도 다른 작업을 동시에 진행할 수 있다는 것입니다. 따라서 위의코드에서도 값을 할당하는것을 기다리지 않고 바로 아래의 sql문으로 진행되었던 것 입니다.
따라서, 하나의 작업(sql문)이 끝날때 다음 작업(sql문)을 실행시키는 즉, 순서를 맞춰서 코드를 실행시켜주는 비동기 처리를 해야 합니다. 비동기처리의 방식은 대표적으로 4가지가 있습니다.
- 콜백 함수 : 할 일 다하고, 이거 실행해줘(순서 맞춰서 실행)
- promise(resolve, reject)
- then & catch
- EX2017 promise => async & await
1번의 콜백함수는 지금까지 사용했던 방식이였고 4번 async & await방식을 사용해보겠습니다. async-await는 비동기로 실행되는 것들을 끝날 때 까지 기다린다는 의미로 callback처럼 동기방식으로 처리가 가능하게 해주는 문법입니다. 즉, Promise객체를 좀 더 쉽게 편하게 사용하는 문법입니다.
let promise = new Promise(function(resolve,reject){
setTimeout(() => resolve('완료!'),3000);
});
promise.then(
function(result){
console.log(result);
},
function(error){}
);
위의 코드를 실행하면 3초뒤에 완료!가 출력됩니다.위의 코드에서는 promise.then을 코드 밖에 선언을해서 사용해줬지만 아래의 코드에서는 await를 사용해 똑같이 Promise객체 일이 끝날 때까지 기다릴 수 있게 해줍니다. 아래의코드를 실행해도 3초뒤에 완료!가 출력됩니다.
async function f(){
let promise=new Promise(function(resolve,reject){
setTimeout(()=>resolve("완료!"),3000);
});
let result = await promise;
console.log(result);
};
f();
async-await 적용
async-await를 적용한 주문하기 api입니다. 기존에 작성하던 api설계에서 다른점은 크게 3가지 입니다.
- mysql2/promise : 기존에는 mysql2만 사용
- conn : 상단부에서 require해서 사용해오던 방식을 api함수안에서 선언하여 사용
- conn.query : conn.execute로 변경하여 사용
먼저 asnyc-await를 사용하기 위해서는 mysql2/promise를 사용해야합니다. 그리고 conn을 require해서 사용했었는데 비동기처리를 해주기 위해 api함수안에서 정의하여 사용했습니다. 마지막으로 conn.query()대신 conn.execute()를 사용해줍니다. 코드를 보면 마지막부분쯤에 conn.query()를 사용한 부분이 있는데 이유는 SQL의 insert문에서 여러개의 행들(데이터)를 넣어줘야 하는데 conn.execute()는 insert가 되지 않아서 query를 사용했습니다. (WHERE의 IN과 같은 곳에서도 안됨, 즉 여러개의 값들을 이용할때 안되는것같음)
const mysql = require('mysql2/promise');
//const conn = require('../mariadb');
const {StatusCodes} = require('http-status-codes');
const order = async (req,res)=>{
const conn = await mysql.createConnection({
host:"localhost",
user:'root',
password:'root',
database:'Bookshop',
dateStrings:true
});
//배송지
const { items, delivery, totalQuantity, totalPrice, firstBookTitle } = req.body;
let sql = "INSERT INTO delivery(address,receiver,contact) VALUE(?,?,?)";
let values = [delivery.address, delivery.receiver, delivery.contact];
let [results] = await conn.execute(sql, values);
let delivery_id = results.insertId;
//주문
sql = `INSERT INTO orders(book_title, total_quantity, total_price, user_id, delivery_id)
VALUE(?,?,?,?,?)`;
values = [firstBookTitle, totalQuantity, totalPrice, decodedJwt.id, delivery_id];
[results] = await conn.execute(sql, values);
let order_id = results.insertId;
console.log("주문번호");
console.log(order_id);
//장바구니에서 선택한 상품 목록 조회
sql = `SELECT book_id,quantity FROM cartItems WHERE id IN(?)`;
let [orderItems, fields] = await conn.query(sql, [items]);
console.log(orderItems);
//주문된 책 목록 조회
sql = `INSERT INTO orderedBook(order_id,book_id,quantity) VALUES ?`;
values = [];
orderItems.forEach((item) => {
values.push([order_id, item.book_id, item.quantity]);
});
console.log(values);
[results] = await conn.query(sql, [values]);
console.log(results);
//장바구니에서 선택한 상품 삭제
let result = await deleteCartItems(conn, items);
console.log(result);
return res.status(StatusCodes.OK).json(results[0]);
};
그래서 아래코드에서도 conn.execute()대신 conn.query()를 사용해주었습니다.
const deleteCartItems = async(conn,items)=>{
let sql =`DELETE FROM cartItems WHERE id IN(?)`;
let values=items;
let result = await conn.query(sql,values);
return result;
}
추가적으로 이전에는 conn.query()를 한후 결과값을 단순히 results라는 변수에 받아서 사용했는데 conn.execute()처리를 하고나서 결과값들은 [rows,fields]에 담아줘야 활용할 수 있습니다.(rows, fields 이름은 바꿔도 상관없습니다). 그거에 대한 예시로 주문목록조회, 주문상세조회코드입니다.
const getOrders = async (req,res)=>{
const conn = await mysql.createConnection({
host:"localhost",
user:'root',
password:'root',
database:'Bookshop',
dateStrings:true
});
// let {id}=req.body;
let sql = `SELECT orders.id,book_title,total_quantity,total_price,created_at,address,receiver,contact
FROM orders LEFT OUTER JOIN delivery ON orders.delivery_id=delivery.id`;
let [rows,fields] = await conn.execute(sql,[]);
console.log(rows);
return res.status(StatusCodes.OK).json(rows);
};
const getOrderDetail = async (req,res)=>{
const conn = await mysql.createConnection({
host:"localhost",
user:'root',
password:'root',
database:'Bookshop',
dateStrings:true
});
let {id}=req.params;
let sql = `SELECT books.id,books.title,books.author,books.price,orderedBook.quantity
FROM orderedBook LEFT OUTER JOIN books
ON orderedBook.book_id=books.id
WHERE order_id=?`;
let values = [id];
let [rows,fields] = await conn.execute(sql,values);
console.log(rows);
return res.status(StatusCodes.OK).json(rows);
};
여러개의 VALUES, WHERE IN
insert into에서 id값을 제외한 모든값들을 삽입해야하는 경우가 있습니다. 그럴때는 아래처럼 사용합니다. SQL문을 보면 VALUES에 ?를 적어주고 values라는 배열에 값들을 넣은뒤 conn.query문에서 처리해주면됩니다. (어떻게 보면 Values가 2차배열이 된다고 볼 수 있겠네요)
//주문된 책 목록 조회
sql = `INSERT INTO orderedBook(order_id,book_id,quantity) VALUES ?`;
values = [];
orderItems.forEach((item) => {
values.push([order_id, item.book_id, item.quantity]);
});
console.log(values);
[results] = await conn.query(sql, [values]);
console.log(results);
그래고 WHERE IN에서도 (?)를 적어준뒤 아래에서 items라는 배열을 한번더 배열을 씌워서 처리해줍니다.
//장바구니에서 선택한 상품 목록 조회
sql = `SELECT book_id,quantity FROM cartItems WHERE id IN(?)`;
let [orderItems, fields] = await conn.query(sql, [items]);
console.log(orderItems);
'백엔드 > node.js(express)' 카테고리의 다른 글
도서주문관리 프로젝트 - 5. JWT적용, 예외처리(토큰만료) (2) | 2024.01.12 |
---|---|
도서주문관리 프로젝트 - 3. 도서,좋아요 API(SQL시간범위, 페이지네이션,서브쿼리) (1) | 2024.01.11 |
도서주문관리 프로젝트 - 2. 유저API(컨트롤러, 단뱡향암호화(crypto),jwt token) (1) | 2024.01.03 |
도서주문관리 프로젝트 - 1.API 설계(API,테이블) (0) | 2023.12.28 |
JWT (0) | 2023.12.26 |