26. 주문 API 구현

JSLEE·2024년 5월 29일

주문API 구상, ERD 설계

저번 장바구니 API에 이어, 이번에는 주문을 하는 API를 구현해보았다.
주문하기 페이지에는 주문자가 배송지 정보를 입력하도록 되어있고, 대표 책 1권에 대한 정보와, 총 금액 등에 대해 표시되어 있다.
주문자가 결제하기 버튼을 누르면, 주문이 요청된다.

주문은 총 3가지의 테이블이 서로 연관되어 있도록 데이터베이스를 구상하였다.

  1. orders : 주문을 다루는 테이블이다. 컬럼은 다음과 같다.
    • id : Primary Key
    • delivery_id : deliveries 테이블을 참조하는 FK
    • total_price : 주문할 상품들의 총계
    • total_quantity : 주문할 상품들의 총 수량
    • created_at : 주문 일시
    • rep_book : 주문페이지에서 주문자에게 대표적으로 보여줄 책의 제목
    • user_id : users 테이블을 참조하는 FK
  2. ordered_books : 주문된 상품들을 다루는 테이블이다. 컬럼은 다음과 같다.
    • id : Primary Key
    • order_id : orders 테이블을 참조하는 FK
    • book_id : books 테이블을 참조하는 FK
    • quantity : 해당 상품의 주문 수량
  3. deliveries :
    • id : Primary Key
    • address : 주문자의 주소
    • receiver : 주문자의 이름
    • contact : 주문자의 연락처

위의 ERD를 기반으로 Workbench에서 데이터베이스 테이블들을 생성한다.


주문 API 구현

사용자가 주문페이지에서 결제하기 버튼을 누른다.
프론트엔드는, 주문자의 배송지정보와 책정보, userId 등을 백엔드에게 전송할 것이다.
백엔드는 프론트엔드가 보낸 데이터를 가지고, DB에 값을 채워넣어야 한다.
하지만, 한번의 Request요청에 세 테이블(orders, ordered_books, deliveries)에 모두 INSERT 하여야 한다. 게다가, 테이블을 살펴보면 orders는 deliveries의 id를 참조하고 있으므로, 뭔가 많이 복잡해보인다.

차근차근 생각해보자.

  1. orders테이블은 deliveries 테이블을 참조하여야 하므로,
    deliveries 테이블에 먼저 INSERT 해야한다.

  2. ordered_books 테이블은 orders 테이블을 참조하여야 하므로,
    orders 테이블에 먼저 INSERT 해야한다.

즉, 테이블을 처리하는 순서는 deliveries - orders - ordered_books 가 되겠다.

이제 SQL문을 적절히 이용해서 데이터를 INSERT하면 될텐데, orders 테이블에는 delivery_id가 요구된다.
이것은 현재 세션에서 가장 마지막에 요구된 deliveries 테이블의 id 값을 필요로 한다.
( 주문자가 정보를 입력 -> deliveries 테이블에 데이터 생성 -> 해당 id값을 orders에 전달 )

여기에는 총 3가지 방법이 있다.

  1. SQL 이용 - LAST_INSERT_ID( ) :
    가장 마지막에 추가된 ID값을 돌려주는 함수이다. 문제점이 있어서 자주 사용하지는 않는다.
    문제점은 다음과 같다.
INSERT INTO table1 VALUES (1,1) ;
INSERT INTO table1 VALUES (2,2) ;
INSERT INTO table1 VALUES (3,3) ;

SELECT LAST_INSERT_ID(); // 3

위 처럼 작성했을때에는 가장 마지막에 작성한 id 값인 3을 돌려주므로, 이상이 없다.
하지만 다음처럼 작성했을 때는 오류가 발생한다.

INSERT INTO table1 VALUES (1,1), (2,2), (3,3) ;

SELECT LAST_INSERT_ID(); // 1

이렇게 한번에 여러 데이터를 INSERT하게 될 경우, 가장 처음의 id 값인 1을 돌려준다.
이러한 이슈가 있어서 다른 방법을 사용하는 것이 권장된다.

  1. SQL 이용 - MAX(id) :
    DB에서 id값을 Auto Increment로 설정해놓았다면, 가장 최근에 작성된 DB의 값은 곧 가장 큰 값일 것이다. 이 점을 이용해서, MAX()함수를 사용하여 방금 deliveries 테이블에 추가된 id값을 알아낸다.
SELECT MAX(id) FROM table1 ;
  1. mysql2 모듈 이용 - connection.query(sql, values, (err,results)=>{} 에서 results값 이용

mysql2모듈에서는 정상적으로 INSERT 쿼리가 수행되었을 때, results 값에 다음과 같은 객체를 반환한다.

이 객체에서, insertId key값이 가장 마지막에 추가된 id 값을 의미한다.
따라서, 다음의 코드와 같이 방금 추가된 id 값을 이용할 수 있다.

let order_id ;
connection.query(sql, values, (err,results)=>{
  if (err){
  return res.stauts(400).end();
  } 
  order_id = results.insertId ; 
}

Node.js에서는 이중배열도 알아서 처리를 해준다.

다음과 같은 상황을 생각해보자.
주문자는 여러개의 상품들을 주문한다.
그러면, 프론트엔드가 아래의 값을 백엔드에게 전달한다.
items : [{"bookTitle" : "책1", "quantity" : 1}, {"bookTitle" : "책2", "quantity" : 2 }]
백엔드는 이 데이터들을 한번에 데이터베이스에 저장하고자 한다. 이 때에 다음과 같이 이중배열을 통해 쿼리문의 작성이 가능하다.

const {items} = req.body
sql = "INSERT INTO orderedBooks (bookTitle, quantity) VALUES ?"
values = []
items.forEach(val => {
  values.push([val.bookTitle, val.quantity])
}) // values = [ ["책1",1] , ["책2", 2] ]

connenction.query(sql,[values], callback)

특징으로는, 이중배열을 통해 값을 저장하고 conn.query에서 values에 대괄호를 사용했다.
이를 통해 한번의 SQL문으로 여러개의 데이터를 한번에 INSERT 할 수 있다.
이 방법을 Bulk Insert 라고 부른다.

이 방법 외에도, 그냥 items 배열의 길이만큼 for문을 돌려서 sql문을 반복해도 된다.
하지만 for문을 돌려서 sql문을 요청하게 되면, 만약 DB서버와 WAS서버가 분리되어 있는 경우 for문의 개수만큼 DB서버와 WAS서버를 여러번 통신해야 해서 성능 면에서 좋지 않을것 같다는 생각이 든다.

profile
공부한 내용들을 정리하기 위해 사용하는 블로그입니다.

0개의 댓글