본문 바로가기

개발/프로그래머스 데브코스

프로그래머스 데브코스 40일차 with. TS 웹 풀스택

📚요약

지난 시간은 비동기에 대해 배웠습니다. 이번 시간에는 이를 활용해 주문 API를 구현해 보겠습니다.

 

📖SQL

📄데이터 삭제

  1. DELETE : 조건(WHERE)을 사용할 경우 조건에 해당되는 데이터 삭제. 조건이 없는 경우 데이터 전부 삭제.
  2. DROP : 테이블 삭제.
  3. TRUNCATE : DELETE의 조건이 없는 경우와 동일하게 데이터를 전부 삭제한다. 하지만 추가적으로 auto_increment 옵션까지 초기화시켜 준다.
DELETE FROM table;

DROP TABLE table;

TRUNCATE table;

 

DELETE와 TRUNCATE의 큰 차이점은 Rollback의 가능 여부와 인덱스까지 삭제하는가의 차이입니다. 또한 일부 데이터를 삭제할 때는 무조건 DELETE만 가능합니다.

 

🍯tip! 데이터를 삭제하려고 하는 데 FK 때문에 삭제가 안 되는 에러가 발생할 수 있다. 그때는 이 글을 참고하면 좋을 것 같다.
 

[MySQL Error 1701] 외래키 설정이 되어 있는 테이블 데이터가 삭제가 안 되네?

에러 배경orders의 데이터를 TRUNCATE sql을 사용해 데이터를 삭제하려는 상황에서 발생했다.에러 그 잡채 TRUNCATE ordersError Code: 1701.Cannot truncate a table referenced in a foreign key constraint(`BookShop`.`orderedBook`,

nulzi-dev.tistory.com

 

📖주문 API

📄구현

주문하기 성공
주문 목록 조회 & 목록 상세 조회

 

📑주문하기

주문하기는 지금까지 요청들과 다르게 여러 SQL을 처리해야 했기 때문에 비동기적으로 처리하거나 다중 쿼리를 사용하는 방법을 이용해야 합니다. 이번에는 Promise와 async/await를 사용해서 비동기적으로 구현했습니다.

 

1. 테이블에 insert 하는 순서

    delivery → orders   orderedBook

    왜냐하면 orders에는 delivery의 id가 필요하고, orderedBook에는 orders의 id가 필요하기 때문입니다.

deliveryInsertSql = `INSERT INTO delivery (address, receiver, ph_num) VALUES (?,?,?)`

orderInsertSql = `INSERT INTO orders
(delivery_id, book_title, total_price, total_quantity, user_id)
VALUES (?,?,?,?,?)`

orderedBookInsertSql = `INSERT INTO orderedBook (order_id, book_id, quantity) VALUES ?`

 

2. 주문에 성공한 장바구니 목록 삭제하기

basketDeleteSql = `DELETE FROM baskets WHERE id IN (?)`

 

비동기를 활용해서 구현했기 때문에 이전과 조금 다르게 예외처리를 진행했습니다.

const order = async (req, res) => { 
  const catchError = (error) => {
    res.status(StatusCodes.BAD_REQUEST).end();
    throw error;
  };
  ...
  await addOrder(conn, values).catch(catchError);
  // 이렇게 에러를 처리하면 서버가 정지하지 않고 400을 응답해준다.
  ...
}
더보기

구현 코드의 일부

const order = async (req, res) => {
  const catchError = (error) => {
    res.status(StatusCodes.BAD_REQUEST).end();
    throw error;
  };
  const {
    basketItemIds,
    delivery,
    totalQuantity,
    totalPrice,
    firstBookTitle,
    userId,
  } = req.body;
  const mariadbP = require("mysql2/promise");
  const conn = await mariadbP.createConnection({
    host: "localhost",
    port: process.env.DBPORT,
    user: process.env.USER,
    password: process.env.PW,
    database: process.env.DATABASE,
    dateStrings: true,
  });
  let values = [delivery.address, delivery.receiver, delivery.ph_num];
  let results = await addDelivery(conn, values).catch(catchError);
  let deliveryId = results.insertId;

  values = [deliveryId, firstBookTitle, totalPrice, totalQuantity, userId];
  results = await addOrder(conn, values).catch(catchError);

  let orderId = results.insertId;

  sql = `SELECT book_id, quantity FROM baskets WHERE id IN (?)`;

  let [basketItems] = await conn.query(sql, [basketItemIds]).catch(catchError);

  values = [];
  basketItems.forEach((item) => {
    values.push([orderId, item.book_id, item.quantity]);
  });
  results = await addOrderedBook(conn, values).catch(catchError);
  results = await deleteSelectedBaskets(conn, basketItemIds)
    .then((result) => {
      if (!result.affectedRows)
        return res.status(StatusCodes.BAD_REQUEST).end();
    })
    .catch(catchError);

  res.status(StatusCodes.OK).json(results);
};

 

📑주문 목록 조회

sql = `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 = ?`;

 

📑목록 상세 조회

sql = `SELECT book_id, title, img, price, author, quantity
FROM orderedBook LEFT JOIN books
ON book_id = books.id
WHERE order_id = ?`;

 

자세한 코드는 Github에서 확인할 수 있습니다.

 

ProgrammersSchool/PROJECT-BOOKSHOP at main · nulzi/ProgrammersSchool

프로그래머스 데브코스에서 학습하는 것들을 모아두는 레포. Contribute to nulzi/ProgrammersSchool development by creating an account on GitHub.

github.com

 

❔▪❓

Q. 주문하기를 구현할 때 중간에 API를 수정했는데 어떤 방식이 더 옳고 좋은지 모르겠다.

  1. FE에서 장바구니 목록에 담긴 정보(장바구니 id, 책 id, 수량)를 넘겨주기
  2. FE에서 장바구니 목록을 장바구니 id로만 넘겨주어 BE에서 SELECT로 처리하기

A. 개인적인 생각으로는 SQL의 요청이 많아지면 많아질수록 부담이 될 수 있기 때문에 간단한 SQL문이라도 사용을 덜 하는 편이 좋지 않을까 하는 생각이다.

 

Q. 주문 API - 주문하기에서 중간에 에러가 나서 delivery에만 입력된 경우나 orders에만 입력된 경우에는 어떻게 취소 처리 하고 다시 진행할 것인가?

A. SQL의 rollback 기능 활용??

 

다음 시간에 계속...

 

출처 & 참고

김송아 강사님의 강의

delete, truncate, drop 비교, 갓대희, 2024.05.31