프로그래머스 데브코스, 국비지원교육, 코딩부트캠프
주문하기 관련 기능을 구현했다. 관련 데이터베이스 테이블도 세 개나 연관 관계인데다 post로 보내는 방식도 이래저래 꼬여 있어서 SQL문을 짜는 걸 고민을 많이 했다. 오류도 얼마나 많이 났던지... 그래도 SQL문을 어떻게 만들어야 하는지 고민하는 과정에서 실력이 좀 괜찮아진 것 같다. 마침 과거의 내가 SQL 시험을 신청하는 바람에 공부를 해야했는데...ㅋ
먼저 주문하기 기능과 직접적으로 관련이 있는 테이블은 delivery
, orderedBook
, orders
다. 테이블명에서부터 알 수 있지만 orderedBook
은 주문한 책을 저장하는 테이블이고 delivery
는 배송지 관련 데이터를 저장하는 테이블, orders
는 기타 주문 관련 내용을 저장하는 테이블이다.
즉 주문을 하면 세 테이블에 모두 저장이 되도록 해야한다.
const order = async (req, res) => {
const connection = await conn.getConnection();
const { items, delivery, totalQuantity, totalPrice, userId, firstBookTitle } = camelcaseKeys(req.body);
const sqlInsertDelivery = `insert into delivery (address, receiver, contact) values (?, ?, ?)`;
const valuesDelivery = [delivery.address, delivery.receiver, delivery.contact];
const sqlInsertOrder = `insert into orders (book_title, total_quantity, total_price, user_id, delivery_id) values (?, ?, ?, ?, ?)`;
const sqlInsertOrderedBook = `insert into orderedBook (order_id, book_id, quantity) values (?, ?, ?)`;
try {
const [rowsDelivery] = await connection.query(sqlInsertDelivery, valuesDelivery);
const deliveryId = rowsDelivery.insertId;
const valuesOrder = [firstBookTitle, totalQuantity, totalPrice, userId, deliveryId];
const [rowsOrder] = await connection.query(sqlInsertOrder, valuesOrder);
const orderId = rowsOrder.insertId;
for (const item of items) {
const valuesOrderItem = [orderId, item.book_id, item.quantity];
await connection.query(sqlInsertOrderedBook, valuesOrderItem);
}
return res.status(StatusCodes.OK).json({
message: '주문이 성공적으로 처리되었습니다.',
});
} catch (err) {
console.log(err);
return res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
message: '주문 중 에러가 발생하였습니다.',
});
} finally {
connection.release();
}
};
POST 요청 시 body 값은 위 사진과 같다.
delivery
테이블에 배송지 관련 내용을 저장한다.order
테이블에 주문 관련 데이터를 저장한다.일단은 이렇게 구현을 하긴 했는데... 위 코드에는 문제가 좀 있다.
우선 delivery, order, orderedBook 테이블 각각 insert를 하기 때문에 예를 들어 order 시 문제가 생기면 delivery 테이블에만 데이터가 들어가고 이후 테이블에는 데이터가 입력이 안 된다. 이와 비슷한 문제로 현재 items 내의 주문 도서들이 for문으로 들어가고 있는데 중간에 에러가 발생한다면 도서가 몇 개만 저장이 되고 그대로 종료되게 된다. 이 경우에는 만약 에러가 발생했을 때 아예 처음으로 돌아가 데이터가 입력이 되지 않도록 하는 게 좋을 것 같다고 멘토님이 조언해주셨다.
const getOrders = async (req, res) => {
const connection = await conn.getConnection();
const { userId } = camelcaseKeys(req.body);
const sql = `select * from orders where user_id=?`;
const values = [userId];
try {
const [rows] = await connection.query(sql, values);
if (rows.length === 0) {
return res.status(StatusCodes.NOT_FOUND).json({
message: '주문 내역이 없습니다.',
});
}
return res.status(StatusCodes.OK).json(rows);
} catch (err) {
console.log(err);
return res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
message: '주문 내역 조회 중 에러가 발생하였습니다.',
});
} finally {
connection.release();
}
};
유저의 주문 내역을 조회하는 코드다. 그냥 데이터베이스에서 select만 제대로 해주면 된다. 근데... 이거 쓰면서 생각한 건데 전체 조회 시 주문 내역이 없는게 에러가 될 수 없는데 난 왜 에러로 만든 거냐?😕 그냥 빈 배열만 반환하면 되잖아...
const getOrderDetail = async (req, res) => {
const connection = await conn.getConnection();
const { userId } = camelcaseKeys(req.body);
const orderId = req.params.id;
const sql = `select * from orders where id=? and user_id=?`;
const values = [orderId, userId];
try {
const [rows] = await connection.query(sql, values);
if (rows.length === 0) {
return res.status(StatusCodes.NOT_FOUND).json({
message: '주문 내역이 없습니다.',
});
}
return res.status(StatusCodes.OK).json(rows);
} catch (err) {
console.log(err);
return res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
message: '주문 내역 조회 중 에러가 발생하였습니다.',
});
} finally {
connection.release();
}
};
이것도 위 코드와 비슷하다. 그냥 하나의 주문 내역만 자세히 보여주면 끝이다.
const deleteOrder = async (req, res) => {
const connection = await conn.getConnection();
const orderId = req.params.id;
const sqlSelectOrder = `select * from orders where id=?`;
const sqlDeleteOrderedBook = `delete from orderedBook where order_id=?`;
const sqlDeleteOrder = `delete from orders where id=?`;
const sqlDeleteDelivery = `delete from delivery where id=?`;
try {
const [rowsOrder] = await connection.query(sqlSelectOrder, orderId);
if (rowsOrder.length === 0) {
return res.status(StatusCodes.NOT_FOUND).json({
message: '주문 내역이 존재하지 않습니다.',
});
}
await connection.query(sqlDeleteOrderedBook, orderId);
const deliveryId = rowsOrder[0].delivery_id;
await connection.query(sqlDeleteOrder, orderId);
await connection.query(sqlDeleteDelivery, deliveryId);
return res.status(StatusCodes.OK).json({
message: '주문이 성공적으로 삭제되었습니다.',
});
} catch (err) {
console.log(err);
return res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
message: '주문 삭제 중 에러가 발생하였습니다.',
});
} finally {
connection.release();
}
};
먼저 주문 내역이 존재하는지 확인하고 차례대로 관련 데이터베이스를 모두 삭제해준다. 근데 이것도 중간에 에러가 발생하면 하나 혹은 두 개 테이블에서만 데이터가 삭제될 수 있기 때문에 좀 더 방법을 고민해봐야 할 것 같다.