프로그래머스 데브코스TIL북 스토어 프로젝트
[week7] 프로젝트 : Node.js 기반의 Rest API 구현 (10)
이규현2026-02-26
![[week7] 프로젝트 : Node.js 기반의 Rest API 구현 (10)](/images/useBlog/TIL.png)
주문 API 구현
주문 관련 테이블 설계
주문 기능을 구현하기 위해 총 세 개의 테이블을 만들었습니다.
delivery (배송 정보 테이블)
배송지 주소, 수령인, 연락처를 저장하는 테이블입니다.
CREATE TABLE delivery (
delivery_id INT NOT NULL AUTO_INCREMENT,
address VARCHAR(255) NOT NULL,
receiver VARCHAR(100) NOT NULL,
contact VARCHAR(100) NOT NULL,
PRIMARY KEY (delivery_id)
)
orders (주문 요약 테이블)
주문 전체를 요약하는 테이블입니다.
CREATE TABLE orders (
order_id INT NOT NULL AUTO_INCREMENT,
book_title VARCHAR(255) NOT NULL,
total_quantity INT NOT NULL,
total_price INT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
user_id INT NOT NULL,
delivery_id INT NOT NULL,
PRIMARY KEY (order_id),
CONSTRAINT fk_orders_users
FOREIGN KEY (user_id)
REFERENCES users (user_id)
ON DELETE CASCADE,
CONSTRAINT fk_orders_delivery
FOREIGN KEY (delivery_id)
REFERENCES delivery (delivery_id)
ON DELETE CASCADE
)
book_title: 주문한 책 중 대표 도서명을 저장합니다. (ex. "어린 왕자 외 2건")total_quantity&total_price: 전체 수량과 총 금액을 저장합니다.user_id&delivery_id: 각각users,delivery테이블을 참조하는 외래키입니다.ON DELETE CASCADE: 사용자가 탈퇴하거나 배송 정보가 삭제되면, 연결된 주문 데이터도 함께 삭제됩니다.
orderedBook (주문 상세 테이블)
주문 한 건에 어떤 책이 몇 권 포함되었는지 상세하게 저장하는 테이블입니다.
CREATE TABLE orderedBook (
ordered_book_id INT NOT NULL AUTO_INCREMENT,
order_id INT NOT NULL,
book_id INT NOT NULL,
quantity INT NOT NULL,
PRIMARY KEY (ordered_book_id),
CONSTRAINT fk_ordered_orders
FOREIGN KEY (order_id)
REFERENCES orders (order_id)
ON DELETE CASCADE,
CONSTRAINT fk_ordered_books
FOREIGN KEY (book_id)
REFERENCES books (book_id)
ON DELETE CASCADE
)
orders와books테이블을 각각 외래키로 참조합니다.orders가 주문의 "요약본"이라면,orderedBook은 그 주문의 "명세서" 역할을 합니다.
주문 API 구현
controller/orderController.js
주문 API는 세 개의 INSERT가 순서대로 실행되어야 합니다.
delivery테이블에 배송 정보를 먼저 저장하고- 반환된
delivery_id로orders테이블에 주문 요약을 저장하고 - 반환된
order_id로orderedBook테이블에 주문 상세를 저장합니다.
앞 단계의 insertId가 다음 단계에 필요하기 때문에, 콜백을 중첩해서 순서를 보장했습니다.
delivery 테이블 Insert
export const order = (req, res) => {
const { items, delivery, totalQuantity, totalPrice, userId, firstBookTitle } =
req.body;
let sql = `INSERT INTO delivery (address, receiver, contact) VALUES (?, ?, ?)`;
let values = [delivery.address, delivery.receiver, delivery.contact];
conn.query(sql, values, (err, results) => {
if (err) return res.status(StatusCodes.INTERNAL_SERVER_ERROR).json(err);
const delivery_id = results.insertId;
req.body에서 받아온 배송 정보를 delivery 테이블에 먼저 INSERT 합니다.
INSERT가 성공하면 results.insertId로 방금 생성된 delivery_id를 꺼내서 다음 단계에 넘겨줍니다.
orders 테이블 Insert
sql = `INSERT INTO orders (book_title, total_quantity, total_price, user_id, delivery_id)
VALUES (?, ?, ?, ?, ?)`;
values = [firstBookTitle, totalQuantity, totalPrice, userId, delivery_id];
conn.query(sql, values, (err, results) => {
if (err) return res.status(StatusCodes.INTERNAL_SERVER_ERROR).json(err);
const order_id = results.insertId;
앞서 받아온 delivery_id를 함께 넣어 주문 요약 정보를 orders 테이블에 INSERT 합니다.
마찬가지로 results.insertId로 order_id를 꺼내서 마지막 단계로 넘겨줍니다.
orderedBook 테이블 Insert
sql = `INSERT INTO orderedBook (order_id, book_id, quantity) VALUES ?`;
const itemValues = items.map((item) => [
order_id,
item.book_id,
item.quantity,
]);
conn.query(sql, [itemValues], (err, results) => {
if (err) return res.status(StatusCodes.INTERNAL_SERVER_ERROR).json(err);
return res
.status(StatusCodes.CREATED)
.json({ message: "주문 완료", order_id });
});
});
});
};
orderedBook은 여러 건의 데이터를 한 번에 INSERT 해야 하기 때문에 items.map()으로 2차원 배열을 만들어 VALUES ? 문법으로 한 번에 넣어줬습니다.



