웹 풀사이클 데브코스 TIL 8주차 DAY 5

갱갱·2024년 1월 7일
0

데브코스 TIL

목록 보기
19/24
post-thumbnail

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

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


테이블을 조인하여 도서를 조회할 때 카테고리 명을 반환할 수 있게끔 했다. 신간 도서를 조회할 수 있도록 했고, 또한 페이징을 구현하였다.

💡 Express로 카테고리명 반환 구현


  1. CategoryController.js
const conn = require('../mariadb');
const { StatusCodes } = require('http-status-codes');

const allCategory = (req, res) => {
    const sql = 'select * from category';

    conn.query(sql, (err, results) => {
        if (err) {
            return res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
                error: '서버 에러',
            });
        }

        if (results.length === 0) {
            return res.status(StatusCodes.NOT_FOUND).json({
                message: '카테고리가 없습니다.',
            });
        }

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

module.exports = { allCategory };
  1. category.js
const express = require('express');
const router = express.Router();
const { allCategory } = require('../controller/CategoryController');
router.use(express.json());

// 전체 카테고리 조회
router.get('/', allCategory);

module.exports = router;

카테고리는 말 그대로 전체 카테고리만 받아오면 되기 때문에 그냥 데이터베이스에 저장 된 모든 카테고리를 반환할 수 있게 구현하였다.

💡 Express로 조인한 테이블 데이터 반환


먼저 books 테이블과 category 테이블의 관계는 위와 같다. 도서를 반환할 때 카테고리명도 함께 반환하게 하기 위해서 테이블을 조인한다. 기존 코드에서 크게 달라지지 않고 SQL문만 조금 수정하면 된다.

values = [];

const sql = 'select * from books left join category on books.category_id = category.id where category_id = ?';

values.push(category_id);

conn.query(sql, values, (err, results) => {
	// SQL 쿼리 실행
}

조인 조건은 books 테이블의 category_id가 category 테이블의 id와 같을 때이다. 이후 쿼리로 받아온 category_id를 필터링해서 결과값을 보여준다.

도서 조회를 해보면 위처럼 name으로 카테고리명이 함께 반환되는 것을 볼 수 있다.

💡Express로 신간 도서 조회 구현


현재 날짜를 기준으로 최근 한 달 이내 책이 발간되었을 때 해당 책만 필터링해서 보여준다. 요청 쿼리로 newstrue 일 때만 신간 도서가 필터링되어 보인다.

const allBooks = (req, res) => {
  const { category_id, news } = req.query;

  let sql = 'select * from books';

  if (category_id && news) {
       sql +=
           ' left join category on books.category_id = category.id where category_id = ? and pub_date between date_sub(now(), interval 1 month) and now()';
       values.push(category_id);
   } else if (category_id) {
       sql += ' left join category on books.category_id = category.id where category_id = ?';
       values.push(category_id);
   } else if (news) {
       sql += ' where pub_date between date_sub(now(), interval 1 month) and now()';
   }

  conn.query(sql, values, (err, results) => {
       if (err) {
           console.log(err);
           return res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
                  error: '서버 에러',
          });
          }

       if (results.length === 0) {
           const message = category_id ? '해당하는 도서가 없습니다.' : '도서가 없습니다.';
           return res.status(StatusCodes.NOT_FOUND).json({
               message: message,
           });
       }

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

SQL문

'select * from books where pub_date between date_sub(now(), interval 1 month) and now()'

DATE_SUB를 이용하여 한달 이전 날짜와 현재 날짜 사이에 출간한 도서만 조회하는 SQL문이다.

  1. select * from books가 반복되고 있기 때문에 해당 SQL을 밖으로 빼고 필요한 SQL문은 조건에 따라 추가하였다.
  2. 요청 쿼리에 카테고리명, 신간 도서 여부를 받아오기 때문에 카테고리명만 받아올 때, 혹은 신간 여부만 받아올 때 또는 둘 다 받아올 때를 나누어서 SQL문을 더해준다.
  3. 해당하는 도서가 없을 때의 에러 메시지는 전체 도서를 받아올 때 도서가 하나도 없을 때 (이런 경우는 없겠지만), 카테고리명을 입력했을 때 해당하는 카테고리의 도서가 없을 때, 혹은 신간이 존재하지 않을 때를 나누어서 다른 메시지를 받아오게 했다.
    조건부 삼항 연산자를 활용해 category_id가 있다면 해당하는 도서가 없다는 메시지를, category_id가 없다면 도서가 없다는 메시지를 출력한다.
  4. 모든 연산이 끝난 후 결과를 반환한다.

예시 사진은 내가 페이징까지 전부 구현한 후라서 limitcurrent_page를 요청 쿼리로 함께 받아 온다. news가 true일 때, 즉 현재 날짜에서 한 달 이내에 출간한 책만 잘 반환되는 걸 볼 수 있다.

💡 Express로 페이징 구현


const allBooks = (req, res) => {
    const { category_id, news, limit, current_page } = req.query;
    // limit : 페이지 당 도서 수
    // currentPage : 현재 페이지
    // offset : 페이지 당 도서 수 * (현재 페이지 - 1)

    const parsedLimit = parseInt(limit);
    const parsedCurrentPage = parseInt(current_page);

    if (!parsedLimit || !parsedCurrentPage || parsedLimit < 1 || parsedCurrentPage < 1) {
        return res.status(StatusCodes.BAD_REQUEST).json({
            error: 'limit 혹은 currentPage가 잘못되었습니다.',
        });
    }

    const offset = parsedLimit * (parsedCurrentPage - 1);
    const values = [];

    let sql = 'select * from books';

    if (category_id && news) {
        sql +=
            ' left join category on books.category_id = category.id where category_id = ? and pub_date between date_sub(now(), interval 1 month) and now()';
        values.push(category_id);
    } else if (category_id) {
        sql += ' left join category on books.category_id = category.id where category_id = ?';
        values.push(category_id);
    } else if (news) {
        sql += ' where pub_date between date_sub(now(), interval 1 month) and now()';
    }

    sql += ' limit ?, ?';
    values.push(offset, parsedLimit);

    conn.query(sql, values, (err, results) => {
        if (err) {
            console.log(err);
            return res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
                error: '서버 에러',
            });
        }

        if (results.length === 0) {
            const message = category_id ? '해당하는 도서가 없습니다.' : '도서가 없습니다.';
            return res.status(StatusCodes.NOT_FOUND).json({
                message: message,
            });
        }

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

드디어 대망의 페이징이다! 페이징을 요청할 때는 페이지 당 몇 개의 데이터를 보여줄 것인지, 그리고 현재 페이지가 어디인지, 또한 데이터베이스에서 가져올 도서 목록의 시작 위치를 나타낼 변수 또한 필요하다.

먼저 해당 코드에서 limit 요청 쿼리는 페이지 당 도서 수를 몇 개로 할지 보낸다. current_page는 현재 페이지를 보낸다.
그리고 offset은, 가져온 도서 목록에서 시작 위치를 나타낸다. 예를 들어서 페이지 당 도서 수를 5로 잡고, 현재 페이지가 3이라면 offset은 15가 된다. 즉, 데이터베이스에서 15번째 도서부터 가져와야 한다는 것을 뜻한다.
따라서 offset = 페이지 당 도서 수 * (현재 페이지 -1)으로 계산할 수 있다.

SQL문

select * from books limit ?, ?;
// offset, limit 순으로 들어간다.
select * from books limit ? offset ?;
// 위의 쿼리와 같다. 다만 이 쿼리는 limit, offset 순으로 들어간다.

limit은 반환하는 데이터의 수를 제한해준다. 건너뛸 데이터의 수, 그리고 반환할 최대 데이터 수를 차례대로 넣어준다.

🚨 서버 에러 발생


1. 첫번째 에러

서버를 실행하고 postman으로 API 테스트를 하려고 했는데 아뿔싸! 갑자기 서버 에러가 발생하고 말았다.

그리고 콘솔로 에러 로그를 찍어보니 위와 같은 메시지가 나왔다. 이유는 정말 간단했다... 처음에는 내가 첫 초기화를 한 SQL문에 select * from books limit ?, ? 식으로 변수 저장을 해두었다. 문제는 이쪽이었다.
찾아보니 LIMIT 문은 SQL 문의 가장 끝에 들어가야 한다고 한다. 그래서 모든 조건문이 실행된 이후 새롭게 LIMIT가 추가될 수 있도록 수정하였다.

다시 서버를 실행해보면 에러 없이 데이터를 잘 반환하는 것을 볼 수 있다.

  1. 두 번째 에러

두 번째 에러는 정말 단순했다. 그냥 limitcurrent_page 요청 쿼리를 받아오지 않으면 서버 에러가 발생하는 거였는데, 사실 도서를 조회할 때 필수적으로 페이징이 들어가긴 하겠지만... 그래도 정말 혹시나 하는 상황이 있을 수 있으니 예외 처리를 추가해줬다. limitcurrent_page는 무조건 1 이상이어야 하니 1보다 작은 수가 오거나 아예 limitcurrent_page가 오지 않을 때 404 Bad Request 에러를 반환했다.

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

0개의 댓글