웹 풀사이클 데브코스 TIL [Day 39] - 주문 API 구현 & 코드 리팩토링

JaeKyung Hwang·2024년 1월 11일
0
post-thumbnail

2024.01.11(목)

🔄Table 초기화

  • TRUNCATE tbl_name;
  • Drop & Create
    👉 AUTO_INCREMENT로 설정된 PK의 index 번호도 초기화됨

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;

🛍️주문 API 구현

  • TRUNCATE로 주문 API와 관련된 table들을 모두 깔끔하게 초기화하고 테스트를 진행했다.
  • 테스트도 계속하면 record들이 많아져서 복잡해지기도 하고 postman으로 일일이 request를 보내서 테스트하는 게 기능이 많아지다 보니 불편한 것 같다. 좀 정리해야 될 것 같은데 slack 채널을 보니 어떤 분은 test code를 따로 짜셨다. JavaScript에서 TypeScript로도 벌써 전환하셨던데 대단한 것 같다. 경험이 많으시던데 역시 경력직은 다르다.🥲
    너무 조급해하지 말고 일단 나는 내 페이스대로 참고해서 코드를 개선해 나가보고자 한다.

  • 일단 기존에 아도겐을 맞은 듯 휘어 있던 submitOrderasync/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도 진행했다.

📦Modularize

  • 먼저 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);
    • Router 전에 있는 getConnection middleware는 req.connection에 connection 객체를 가져와 담은 후 다음 middleware를 호출한다.
    • Router 후에 있는 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 객체를 상속하여 만든 객체로 statusCodemessage를 받을 수 있다. 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를 보낸다.
      (error가 발생하면 releaseConnection을 거치지 않고 errorHandler로 jump하기 때문에 일단 errorHandler 내부에서도 반납을 해줬는데 중복되는 것 같아 더 좋은 방법이 없을까 생각해보고 있다.)

👉 https://github.com/do0ori/book-store-project/pull/8

profile
이것저것 관심 많은 개발자👩‍💻

0개의 댓글