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

갱갱·2024년 1월 15일
0

데브코스 TIL

목록 보기
21/24
post-thumbnail

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

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


밀린 TIL 쓰기...(밀린 시점부터 TIL이 아닌가?) 저번주부터 이런저런 일이 있어서 사실 개발도 그렇게 많이 하지 못했다...

일단 해당 날짜에는 좋아요를 구현했다. 좋아요 추가와 취소를 구현했는데 로직 자체는 어렵지 않았고 유저와 도서가 존재하는지 체크하는 부분(특히 SQL문)을 많이 고민했다.

💡Express로 좋아요 추가 구현하기


  1. LikeController.js
const sqlSelect = `select * from likes where user_id = ? and liked_book_id = ?`;
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 addLike = async (req, res) => {
    const { id } = req.params;
    const { user_id } = req.body;
    const sqlInsert = `insert into likes (user_id, liked_book_id) values (?, ?)`;
    const values = [user_id, id];

    const connection = await conn.getConnection();

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

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

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

        const [result] = await connection.query(sqlSelect, values);

        if (result.length > 0) {
            return res.status(StatusCodes.BAD_REQUEST).json({
                message: '이미 좋아요한 책입니다.',
            });
        }

        const [insertResult] = await connection.query(sqlInsert, values);

        if (insertResult.affectedRows > 0) {
            return res.status(StatusCodes.OK).json({
                message: '좋아요 성공',
            });
        } else {
            return res.status(StatusCodes.BAD_REQUEST).json({
                message: '좋아요 실패',
            });
        }
    } catch (err) {
        console.log(err);
        return res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
            message: '서버 에러',
        });
    } finally {
        connection.release();
    }
};

먼저 유저와 도서가 존재하는지 확인하는 부분은 좋아요 취소 로직에도 반복되기 때문에 하나의 함수로 뺐다. 근데 이렇게 빼더라도 status return은 따로 해줘야해서 그렇게 크게 차이가 나려나 싶다. status code와 반복되는 메시지까지 반환하는 방법이 없는지 찾아봤는데 아직은 찾지 못했다... 아니면 아예 에러 핸들러 파일을 만들어야 하려나🙄

이미 좋아요 한 책이라면 반복해서 좋아요를 할 수 없게끔 하는 예외 처리도 만들었다. 근데 사실 이 좋아요도 그렇고 존재하지 않는 유저, 존재하지 않는 도서에 좋아요를 할 수 없게끔 하는 건 프론트에서 애초에 막아둘 테긴 한데... 요청이 잘못 들어올 수도 있으니까 일단 할 수 있는 예외 처리는 전부 해두었다.

  1. LikeMiddelware.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 validateLikedBookId = param('id')
    .trim()
    .notEmpty()
    .withMessage('도서 아이디를 입력해주세요.')
    .isInt()
    .withMessage('숫자로 입력해주세요.');

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

const validatesLike = [validateLikedBookId, validateUserId, validate];

module.exports = { validatesLike };

이건 좋아요 유효성 검사 미들웨어인데 사실 특별한 건 없다. 그냥 도서 아이디와 유저 아이디를 숫자로 입력받았는지만 확인하는 미들웨어다.

  1. likes.js
const express = require('express');
const router = express.Router();
const { addLike, deleteLike } = require('../controller/LikeController');
const { validatesLike } = require('../middleware/LikeMiddleware');

router.use(express.json());

router
    .route('/:id')
    // 좋아요 추가
    .post(validatesLike, addLike)
    // 좋아요 취소
    .delete(validatesLike, deleteLike);

module.exports = router;

라우터 파일이다. 마찬가지로 다른 라우터 파일과 형식이 동일하다.

포스트맨으로 API 테스트를 하면 아래와 같이 잘 나오는 걸 확인할 수 있다.

💡 Express로 좋아요 취소 구현하기


const deleteLike = async (req, res) => {
    const { id } = req.params;
    const { user_id } = req.body;
    const sqlDelete = `delete from likes where user_id = ? and liked_book_id = ?`;
    const values = [user_id, id];

    const connection = await conn.getConnection();

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

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

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

        const [result] = await connection.query(sqlSelect, values);

        if (result.length === 0) {
            return res.status(StatusCodes.BAD_REQUEST).json({
                message: '좋아요하지 않은 책입니다.',
            });
        }

        const [deleteResult] = await connection.query(sqlDelete, values);

        if (deleteResult.affectedRows > 0) {
            return res.status(StatusCodes.OK).json({
                message: '좋아요 취소 성공',
            });
        } else {
            return res.status(StatusCodes.BAD_REQUEST).json({
                message: '좋아요 취소 실패',
            });
        }
    } catch (err) {
        console.log(err);
        return res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
            message: '서버 에러',
        });
    } finally {
        connection.release();
    }
};

module.exports = {
    addLike,
    deleteLike,
};

좋아요 취소도 좋아요 추가와 로직은 얼추 비슷하다. 그냥 SQL문만 조금 달라지고 이미 좋아요 했던 책이 아니라 좋아요를 하지 않은 책일 때 Bad Request를 날려주는 것 정도?

마찬가지로 포스트맨으로 API 테스트를 한 결과는 아래와 같다.

💫 개선할 사항


같은 팀원 분께서 해당 코드를 보시고 코드 리뷰를 해주셨다. 유저와 도서가 존재하는지 확인하는 부분에서 SQL문을 실행하지 않고 DB에서 던지는 에러를 확인해서 해당 로직을 대체하는 것이 어떻겠냐고 제안해주셨다.
이 코드 리뷰를 보고 든 생각... 이런 방법도 있구나!!! 그러고 보니 DB에서 보내는 에러에 errno라고 에러 번호가 함께 날아오는데 이걸 이용한다면 select SQL문을 실행하지 않고도 유저와 도서의 유무를 확인할 수 있을 것 같다.
근데 이렇게 한다면 다른 에러들도 아예 핸들러로 따로 빼서 정리하는 게 나을 것 같다는 생각도 한다. 일단 지금 있는 기능을 완성시키고 리팩토링을 하면서 고민해보는 걸로!!

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

0개의 댓글

관련 채용 정보