2024.01.11(목)
TRUNCATE tbl_name;
⛔ Foreign Key가 존재하는 table을 초기화 할 때 다음과 같은 에러 메시지 발생
Error Code: 1701. Cannot truncate a table referenced in a foreign key constraint (…
이때 FK Check option을 껐다가 다시 켜는 방식을 사용해야 함!
SET FOREIGN_KEY_CHECKS = 0; TRUNCATE tbl_name; SET FOREIGN_KEY_CHECKS = 1;
TRUNCATE
로 주문 API와 관련된 table들을 모두 깔끔하게 초기화하고 테스트를 진행했다.일단 기존에 아도겐을 맞은 듯 휘어 있던 submitOrder를 async/await
을 이용해 깔끔하게 정리했다.
const { HttpError } = require('../utils/errorHandler');
const { StatusCodes } = require('http-status-codes');
// 나중에 transaction으로
const submitOrder = async (req, res, next) => {
try {
const { userId, items, delivery, totalQuantity, totalPrice, firstBookTitle } = req.body;
let sql = "INSERT INTO delivery (address, recipient, contact) VALUES (?, ?, ?)";
let values = [delivery.address, delivery.recipient, delivery.contact];
let [result] = await req.connection.query(sql, values);
const delivery_id = result.insertId;
sql = "INSERT INTO orders (user_id, delivery_id, book_title, total_quantity, total_price) VALUES (?, ?, ?, ?, ?)";
values = [userId, delivery_id, firstBookTitle, totalQuantity, totalPrice];
[result] = await req.connection.query(sql, values);
const order_id = result.insertId;
sql = "INSERT INTO ordered_book (order_id, book_id, quantity) VALUES ?";
values = [];
items.forEach((item) => values.push([order_id, item.bookId, item.quantity]));
[result] = await req.connection.query(sql, [values]);
sql = "DELETE FROM cart WHERE item_id IN (?)";
values = [items.map(item => item.cartItemId)];
[result] = await req.connection.query(sql, values);
if (result.affectedRows) {
res.status(StatusCodes.OK).json(result);
} else {
throw new HttpError(StatusCodes.BAD_REQUEST);
}
next();
} catch (error) {
next(error);
}
};
나머지 기능들은 쉬워서 금방 구현했다.
const getOrderList = async (req, res, next) => {
try {
const { userId } = req.body;
const sql = `
SELECT
orders.id AS order_id,
ordered_at,
address,
recipient,
contact,
book_title,
total_quantity,
total_price
FROM orders
LEFT JOIN delivery
ON orders.delivery_id = delivery.id
WHERE user_id = ?
`;
const [rows] = await req.connection.query(sql, userId);
res.status(StatusCodes.OK).json(rows);
next();
} catch (error) {
next(error);
}
};
const getOrderDetails = async (req, res, next) => {
try {
const { orderId } = req.params;
const sql = `
SELECT
book_id,
title,
author,
price,
quantity
FROM ordered_book
LEFT JOIN books
ON ordered_book.book_id = books.id
WHERE order_id = ?
`;
const [rows] = await req.connection.query(sql, orderId);
if (rows.length) {
res.status(StatusCodes.OK).json(rows);
} else {
throw new HttpError(StatusCodes.NOT_FOUND, "존재하지 않는 주문입니다.");
}
next();
} catch (error) {
next(error);
}
};
module.exports = {
submitOrder,
getOrderList,
getOrderDetails
};
코드를 보면 알겠지만 async/await을 사용했을 뿐 아니라 개인적으로 대대적인 code refactoring도 진행했다.
먼저 express middleware의 기능을 최대한 이용하고자 pool에서 connection을 가져오고(get) 반납(release)하는 기능을 따로 빼서 middleware로 만들었다. 다음과 같이 routing 전과 후에 각각 middleware를 사용하도록 설정해주었다.
app.use(getConnection);
app.use('/users', userRouter);
app.use('/books', bookRouter);
app.use('/category', categoryRouter);
app.use('/likes', likeRouter);
app.use('/cart', cartRouter);
app.use('/orders', orderRouter);
app.use(releaseConnection);
getConnection
middleware는 req.connection
에 connection 객체를 가져와 담은 후 다음 middleware를 호출한다.releaseConnection
middleware는 req.connection
객체를 반납한다.controller에서 try-catch문을 사용하므로 catch문에서 error를 잡고 next(error)
로 error handler middleware를 호출할 수 있다. 이를 이용해 error handler middleware를 따로 만들고 controller에서 error를 throw하는 방식으로 error 처리를 넘겼다.
try {
// do something
throw new HttpError(StatusCode, message); // exception handling
next(); // call next middleware
} catch (error) {
next(error); // pass error to errorHandler middleware
}
HttpError
객체는 Error
객체를 상속하여 만든 객체로 statusCode
와 message
를 받을 수 있다. message가 따로 없으면 해당 StatusCode의 Reason Phrase를 사용한다.app.use(getConnection);
app.use('/users', userRouter);
app.use('/books', bookRouter);
app.use('/category', categoryRouter);
app.use('/likes', likeRouter);
app.use('/cart', cartRouter);
app.use('/orders', orderRouter);
app.use(releaseConnection);
app.use(errorHandler);
errorHandler
middleware는 releaseConnection
과 마찬가지로 DB와의 connection을 반납하고 error에 맞게 response를 보낸다.releaseConnection
을 거치지 않고 errorHandler
로 jump하기 때문에 일단 errorHandler 내부에서도 반납을 해줬는데 중복되는 것 같아 더 좋은 방법이 없을까 생각해보고 있다.)