웹 풀사이클 데브코스 TIL 9주차 DAY 3

갱갱·2024년 1월 16일
0

데브코스 TIL

목록 보기
22/24
post-thumbnail

프로그래머스 데브코스, 국비지원교육, 코딩부트캠프

🚩 프로그래머스 데브코스 웹 풀사이클 과정 9주차 DAY 3


장바구니 기능을 구현했다. 마찬가지로 큰 어려움은 없긴 했는데 강의에서는 구현되지 않은 예외 처리를 생각하느라 시간이 조금 걸렸다.

💡 Express로 장바구니 담기 구현하기


  1. CartController.js
const checkExist = `select (select count(*) from users where id = ?) as user_exists, (select count(*) from books where id = ?) as book_exists`;

const checkExistValues = async (connection, values) => {
    const [rows] = await connection.query(checkExist, values);
    return {
        user_exists: rows[0].user_exists === 1,
        book_exists: rows[0].book_exists === 1,
    };
};

const addToCart = async (req, res) => {
    const connection = await conn.getConnection();
    const { bookId, quantity, userId } = camelcaseKeys(req.body);

    const sqlInsertCart = 'insert into cartItems (book_id, quantity, user_id) values (?, ?, ?)';
    const sqlSelectCart = 'select * from cartItems where user_id = ? and book_id = ?';
    const sqlUpdateCart = 'update cartItems set quantity = quantity + ? where user_id = ? and book_id = ?';
    const values = [bookId, quantity, userId];
    const existValues = [userId, bookId];

    try {
        const { user_exists, book_exists } = await checkExistValues(connection, existValues);

        if (!user_exists) {
            return res.status(StatusCodes.NOT_FOUND).json({
                message: '존재하지 않는 유저입니다.',
            });
        }

        if (!book_exists) {
            return res.status(StatusCodes.NOT_FOUND).json({
                message: '존재하지 않는 도서입니다.',
            });
        }

        const [rowsSelect] = await connection.query(sqlSelectCart, existValues);

        if (rowsSelect.length > 0) {
            const [rowsUpdate] = await connection.query(sqlUpdateCart, [quantity, userId, bookId]);

            if (rowsUpdate.affectedRows > 0) {
                return res.status(StatusCodes.OK).json({
                    message:
                        '이미 장바구니에 담긴 도서입니다. 원하시는 수량만큼 장바구니에 담긴 도서의 수량이 증가하였습니다.',
                });
            } else {
                return res.status(StatusCodes.BAD_REQUEST).json({
                    message: '장바구니 추가에 실패하였습니다.',
                });
            }
        }

        const [rowsInsert] = await connection.query(sqlInsertCart, values);

        if (rowsInsert.affectedRows > 0) {
            return res.status(StatusCodes.CREATED).json({
                message: '장바구니에 도서가 추가되었습니다.',
            });
        } else {
            return res.status(StatusCodes.BAD_REQUEST).json({
                message: '장바구니 추가에 실패하였습니다.',
            });
        }
    } catch (err) {
        console.log(err);
        res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
            message: '장바구니 추가 중 문제가 발생하였습니다.',
        });
    } finally {
        connection.release();
    }
};
  1. 먼저 존재하는 유저인지, 존재하는 도서인지 확인한다.
  2. 장바구니에 이미 담겨있는 도서일 때 원하는 수량만큼 장바구니에 담긴 도서의 수량이 증가한다.
  3. 위의 예외 처리가 모두 끝난 후 장바구니에 도서를 추가한다.

처음에는 장바구니에 도서를 다시 못 담게 막아뒀는데 생각해보니까 똑같은 도서를 담지 못하는 건 말도 안되지 않은가. 그래서 같은 도서를 다시 담으려고 할 때 해당 수량만큼 기존 도서 수량에 증가하는 방식을 택했다.

  1. CartMiddleware.js
const { param, body, validationResult } = require('express-validator');

const validate = (req, res, next) => {
    const err = validationResult(req);

    if (!err.isEmpty()) {
        return res.status(400).json(err.array());
    } else {
        return next();
    }
};

const validateUserId = body('user_id')
    .trim()
    .notEmpty()
    .withMessage('유저 아이디를 입력해주세요.')
    .isInt()
    .withMessage('숫자로 입력해주세요.');

const validateBookId = body('book_id')
    .trim()
    .notEmpty()
    .withMessage('도서 아이디를 입력해주세요.')
    .isInt()
    .withMessage('숫자로 입력해주세요.');

const validateQuantity = body('quantity')
    .trim()
    .notEmpty()
    .withMessage('수량을 입력해주세요.')
    .isInt()
    .withMessage('숫자로 입력해주세요.')
    .custom((value) => value > 0)
    .withMessage('수량은 1 이상이어야 합니다.');

const validatesAddToCart = [validateUserId, validateBookId, validateQuantity, validate];
const validatesGetCartItems = [validateUserId, validate];

module.exports = { validatesAddToCart, validatesGetCartItems };

다른 유효성 검사와 기본적으로 비슷하기는 한데 수량 유효성 검사는 기본적으로 1 이상만 올 수 있게 막아뒀다.

  1. Carts.js
const express = require('express');
const router = express.Router();
const { addToCart, getCartItems, deleteCartItem } = require('../controller/CartController');
const { validatesAddToCart, validatesGetCartItems } = require('../middleware/CartMiddleware');

router.use(express.json());
router
    .route('/')
    // 장바구니 담기
    .post(validatesAddToCart, addToCart)
    // 장바구니 조회, 선택된 장바구니 조회
    .get(validatesGetCartItems, getCartItems);
// 장바구니 도서 삭제
router.delete('/:id', deleteCartItem);

module.exports = router;

다른 라우터 파일과 비슷하다.

💡 Express로 장바구니 조회 구현하기


const getCartItems = async (req, res) => {
    const connection = await conn.getConnection();
    const { userId, selected } = camelcaseKeys(req.body);

    const sqlSelectUser = 'select * from users where id = ?';
    const sqlSelectAllCart = `select cartItems.id, book_id, title, summary, quantity, price
    from cartItems left join books on cartItems.book_id = books.id where user_id = ?`;
    const sqlSelectSelectedCart = `select cartItems.id, book_id, title, summary, quantity, price
    from cartItems left join books on cartItems.book_id = books.id where user_id = ? and cartItems.id in (?)`;

    let sqlSelectCart;
    let values;

    if (selected) {
        sqlSelectCart = sqlSelectSelectedCart;
        values = [userId, selected];
    } else {
        sqlSelectCart = sqlSelectAllCart;
        values = [userId];
    }

    try {
        const [rowsUser] = await connection.query(sqlSelectUser, userId);

        if (rowsUser.length === 0) {
            return res.status(StatusCodes.NOT_FOUND).json({
                message: '존재하지 않는 유저입니다.',
            });
        }

        const [rowsSelect] = await connection.query(sqlSelectCart, values);

        if (rowsSelect.length > 0) {
            return res.status(StatusCodes.OK).json(rowsSelect);
        } else {
            return res.status(StatusCodes.NOT_FOUND).json({
                message: '장바구니에 담긴 도서가 없습니다.',
            });
        }
    } catch (err) {
        console.log(err);
        res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
            message: '장바구니 조회 중 문제가 발생하였습니다.',
        });
    } finally {
        connection.release();
    }
};

기본적으로 selected가 들어오지 않고 userId만 들어온다면 해당 유저가 장바구니에 담은 모든 도서를 반환하고, selected가 배열로 함께 들어오면 선택한 장바구니만 반환한다.
강의에서는 selected가 들어오지 않을 때의 예외 처리를 하지 않으셔서 이 부분은 내가 따로 추가했다.

💡 Express로 장바구니 삭제 구현하기


const deleteCartItem = async (req, res) => {
    const connection = await conn.getConnection();
    const { id } = req.params;

    const sqlDeleteCart = 'delete from cartItems where id = ?';

    try {
        const [rowsDelete] = await connection.query(sqlDeleteCart, id);

        if (rowsDelete.affectedRows > 0) {
            return res.status(StatusCodes.OK).json({
                message: '장바구니에서 도서가 삭제되었습니다.',
            });
        } else {
            return res.status(StatusCodes.NOT_FOUND).json({
                message: '존재하지 않는 장바구니 도서입니다.',
            });
        }
    } catch (err) {
        console.log(err);
        res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
            message: '장바구니 도서 삭제 중 문제가 발생하였습니다.',
        });
    } finally {
        connection.release();
    }
};

딱히 특별한 건 없고 다른 delete 요청과 마찬가지로 그냥 삭제하는 로직이다.

마찬가지로 에러 처리 부분에서 생각이 조금 많아진다. 좀 더 나은 방법이 있을 것 같긴 한데 일단 지금 기능 구현하는 것에 바빠서 리팩토링을 잘 못하고 있다... 당장 수목금 여행을 가서 최대한 빨리 끝내놔야 하는데!!!!
남은 부분 열심히 해서 마무리 지어보자

profile
괜찮은 개발자가 되어 보자

0개의 댓글