지난 시간에는 비동기 처리에 대해 알아봤어요. 이번 시간에는 비동기 개념을 본격적으로 활용해 주문 API 를 구현해 볼게요
본격적인 구현에 앞서 데이터를 지우는 3가지 SQL 명령어에 대해 먼저 정리해 볼게요.
WHERE 조건을 사용해 조건에 맞는 데이터만 지울 때 써요. (조건을 안 적으면 데이터가 전부 삭제해요.)DELETE 에서 조건 없이 데이터를 전부 지우는 것과 같은 효과지만, auto_increment 옵션 값까지 깔끔하게 초기화시켜줘요.DELETE 와 TRUNCATE 의 가장 큰 차이점은 Rollback 가능 여부 와 인덱스 초기화 여부 예요.
그리고 데이터 전체가 아닌 일부 데이터만 골라서 지울 때는 무조건 DELETE 만 써야해요.
orders 의 데이터를 TRUNCATE sql 을 사용해 데이터를 삭제하려는 상황에서 FK 제약조건 이슈가 있었어요.
저라면 보통 참조키를 nullable로 바꿔서 해결하는데, 이번에는 다른 방법을 써봤어요.
TRUNCATE orders
Error Code: 1701.
Cannot truncate a table referenced in a foreign key constraint
(`BookShop`.`orderedBook`, CONSTRAINT `FK_orderedBook_orders_id`
FOREIGN KEY (`order_id`) REFERENCES `BookShop`.`orders` (`id`))
외래키 설정을 OFF 하고 삭제 한 다음 다시 ON 해서 해결했어요.
// 외래키 설정 OFF
SET FOREIGN_KEY_CHECKS = 0;
// 원하는 테이블 데이터 삭제
// 외래키 설정 ON
SET FOREIGN_KEY_CHECKS = 1;
간단한 테이블의 초기화이기에 가능했다는 느낌이었어요.
관련된 테이블이 많은 경우 추가적인 문제가 있을 수 있기에 설정을 건드리는 부분은 조심해야 할 필요가 있어보여요.
주문하기 요청은 지금까지 만들었던 기능들과 다르게 여러 SQL을 순서대로 처리해야 해요.
그래서 이번에는 Promise 와 async / await 를 사용해서 비동기적으로 안전하게 구현해 봤어요.
1. 테이블 Insert 순서
테이블에 데이터를 넣을 때는 외래키 의존성에 맞춰 delivery → orders → orderedBook 순서로 들어가야 해요.
(orders 에는 배송지 id가 필요하고, orderedBook 에는 주문 id가 하니까)
-- 1. 배송지 정보 추가
INSERT INTO delivery (address, receiver, ph_num) VALUES (?, ?, ?);
-- 2. 주문 정보 추가
INSERT INTO orders (delivery_id, book_title, total_price, total_quantity, user_id) VALUES (?, ?, ?, ?, ?);
-- 3. 주문 상세 목록(책들) 추가
INSERT INTO orderedBook (order_id, book_id, quantity) VALUES ?;
2. 주문에 성공한 장바구니 목록 삭제하기
주문이 끝났다면 장바구니에 들어있던 아이템들은 IN (?) 을 사용해서 한 번에 삭제했어요.
DELETE FROM baskets WHERE id IN (?);
async / await 를 활용해 비동기로 구현했기 때문에, 중간에 쿼리가 실패했을 때 서버가 멈추지 않도록 예외 처리 로직도 조금 다르게 작성했어요.
const order = async (req, res) => {
// 에러 발생 시 공통으로 처리해 줄 함수를 만들었어요.
const catchError = (error) => {
res.status(StatusCodes.BAD_REQUEST).end();
throw error;
};
// ... 생략 ...
// await 뒤에 .catch()를 달아서 에러가 발생하면 catchError 함수가 실행되게 해요.
// 이렇게 에러를 던져주면 서버가 강제 종료되지 않고 안전하게 400 에러를 응답해줘요.
await addOrder(conn, values).catch(catchError);
// ... 생략 ...
}
주문 내역을 확인하는 API는 JOIN 을 활용해서 프론트엔드에 필요한 데이터만 뽑아왔어요.
-- 주문 목록 조회 (orders + delivery)
SELECT orders.id, created_at, address, receiver, ph_num, book_title, total_quantity, total_price
FROM orders LEFT JOIN delivery
ON orders.delivery_id = delivery.id
WHERE user_id = ?;
-- 주문 상세 조회 (orderedBook + books)
SELECT book_id, title, img, price, author, quantity
FROM orderedBook LEFT JOIN books
ON book_id = books.id
WHERE order_id = ?;