본문 바로가기
TIL with Programmers

[TIL] 10/4 도서 데이터베이스, API 구현

by 보먀 2024. 10. 4.
728x90
반응형

1. 데이터베이스 테이블 만들기 - books

CREATE TABLE `book-shop`.`books` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `title` VARCHAR(45) NOT NULL,
  `category_id` INT NOT NULL,
  `form` VARCHAR(45) NOT NULL,
  `isbn` VARCHAR(45) NOT NULL,
  `summary` VARCHAR(500) NULL,
  `detail` LONGTEXT NULL,
  `author` VARCHAR(45) NULL,
  `pages` INT NOT NULL,
  `contents` LONGTEXT NULL,
  `price` INT NOT NULL,
  `pub_date` DATE NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE INDEX `isbn_UNIQUE` (`isbn` ASC) VISIBLE);

 

 

 

2. 도서 API 만들기

 

2.1. books 라우터

const express = require('express');
const router = express.Router();

const {
    allBooks,
    bookDetail,
    booksByCategory
} = require('../controller/bookController');

router.use(express.json());

// 전체 도서 조회
router.get('/', allBooks);

// 개별 도서 조회
router.get('/:id', bookDetail);

// 카테고리별 도서 목록 조회
router.get('/', booksByCategory);

module.exports = router;

 

 

2.2. 전체 도서 조회

 

데이터베이스에 있는 전체 도서 목록을 리스폰스로 다 받으면 된다. 도서들이 json 배열로 리스폰스에 담겨 돌아온다. 

const allBooks = (req, res) => {
    // (요약된) 전체 도서 리스트
    let sql = `SELECT * FROM books`;
    conn.query(sql, (err, results) => {
            if (err) {
                console.log(err);
                return res.status(StatusCodes.BAD_REQUEST).end();
            }
            res.status(StatusCodes.OK).json(results);
        }
    )
}

 

 

2.3. 개별 도서 조회

 

id 에 맞는 도서가 없다면 NOT_FOUND, id 에 맞는 도서가 있다면 OK 상태코드와 함께 results[0] 을 돌려준다.

results 는 기본적으로 json 배열 형태이기 때문에 아래의 모양으로 리스폰스에 담겨온다. 그래서 result[0] 으로 배열을 벗기고 내용물만 가져온다. 

[
    {
        "id": 2,
        "title": "신데렐라들",
        "form": "종이책",
        "isbn": "1",
        "summary": "유리구두",
        "detail": "투명한 유리구두",
        "author": "김구두",
        "pages": 100,
        "contents": "목차입니다",
        "price": 20000,
        "pub_date": "2022-01-01"
    }
// 개별 도서 조회
const bookDetail = (req, res) => {
    const {id} = req.params; 

    let sql = `SELECT * FROM books WHERE id = ?`;
    conn.query(sql, id,
        (err, results) => {
            if (err) {
                console.log(err);
                return res.status(StatusCodes.BAD_REQUEST).end();
            }

            if (results[0])
                return res.status(StatusCodes.OK).json(results[0]);
            else
                return res.status(StatusCodes.NOT_FOUND).end();   
        }
    )
}

 

 

2.4. 카테고리별 도서 조회

// 카테고리별 도서 조회
const booksByCategory = (req, res) => {
    const {category_id} = req.query;

    let sql = `SELECT * FROM books WHERE category_id = ?`;
    conn.query(sql, category_id,
        (err, results) => {
            if (err) {
                console.log(err);
                return res.status(StatusCodes.BAD_REQUEST).end();
            }
            if (results.length) 
                res.status(StatusCodes.OK).json({results});
            else
                res.status(StatusCodes.NOT_FOUND).end();
        }
    )
}

 

코드를 짜고 포스트맨으로 결과가 잘 날라오나 테스트를 해보았는데, 이상한 리스폰스가 왔다.

쿼리로 보낸 카테고리에 해당하는 결과가 아니라 전체 데이터가 다 들어있는 리스폰스가 돌아온 것이다. 

 

라우터 파일을 보면 전체 도서 조회와 카테고리별 도서 목록 조회가 같은 url 을 사용하고 있는 것을 알 수 있다. 

그래서 카테고리별 도서 목록 조회 요청이 날라와도 전체 도서 조회에 걸리기 때문에 발생하는 문제였다. 

// books.js -> 라우터 파일

// 전체 도서 조회
router.get('/', allBooks);

// 개별 도서 조회
router.get('/:id', bookDetail);

// 카테고리별 도서 목록 조회
router.get('/', booksByCategory);

 

그렇다면 카테고리별 도서 목록 조회를 전체 도서 조회보다 위에 위치시키면 문제가 해결되지 않을까 했지만 문제는 해결되지 않았다.

카테고리별 도서 목록 조회는 잘 되었지만, 전체 도서를 조회하려고 했을 때 에러가 catagory_id 쿼리가 없다는 에러가 발생했다. 

 

그렇다면 어떻게 해결할 수 있을까?

-> 전체 도서 조회와 카테고리별 도서 목록 조회 합치기

// books.js -> 라우터 파일
router.get('/', allBooks);
const allBooks = (req, res) => {
    const {category_id} = req.query;

    if (category_id) { // 쿼리가 있다면 카테고리별 도서 목록 조회
        let sql = `SELECT * FROM books WHERE category_id = ?`;
        conn.query(sql, catagory_id,
            (err, results) => {
                if (err) {
                    console.log(err);
                    return res.status(StatusCodes.BAD_REQUEST).end();
                }
                if (results.length) 
                    res.status(StatusCodes.OK).json({results});
                else
                    res.status(StatusCodes.NOT_FOUND).end();
            }
        )
    } else { // 쿼리가 없다면 전체 도서 조회
    let sql = `SELECT * FROM books`;
        conn.query(sql, (err, results) => {
            if (err) {
                console.log(err);
                return res.status(StatusCodes.BAD_REQUEST).end();
            }
            res.status(StatusCodes.OK).json(results);
            }
        )
    }    
}

 

 

2.5. 카테고리 테이블 만들기

 

카테고리별 도서 조회할 때 catagory_id 를 사용해서 카테고리를 구별했다. 근데 상세 페이지에서 보여줄 카테고리는 숫자로 된 id 값이 아니라 동화, 소설과 같은 문자열이다. 그렇기 때문에 따로 카테고리 테이블을 만들어서 매칭 시켜주는 것이 필요하다.

 

번거롭게 카테고리 테이블을 만들어서 매칭시켜주는 것이 아니라 바로 카테고리 문자열을 넣어주는 것이 더 낫지 않을까? 라고 생각할 수 있는데, 카테고리가 많고 복잡하여 관리가 어렵기 때문에 따로 문자열 대신 숫자를 넣어서 관리하고 카테고리 테이블을 따로 두는 것이 더 낫다. 

 

- 데이터베이스에 카테고리 테이블 만들기

CREATE TABLE `book-shop`.`category` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(100) NOT NULL);

 

- 카테고리 API 

 

카테고리 전체 목록 조회

Method GET
URI /catagory
HTTP status code 성공 200
Request Body  
Response Body {
    {
        id: 0,
        name: "동화"
    },
    {
        id: 1,
        name: "소설"
    }
    ...
}

 

 

3. 이미지 경로 추가

 

3.1. 데이터베이스 테이블에 img 행 추가하기

 

picsum 이라는 사이트에서 사진을 무료로 쓸 수 있는데, 아래 url 에서 id 문자열 뒤에 값이 사진의 id 값이다. 

데이터베이스에 사용할 사진의 id 값만 저장해서 사용하기 위해 img 행을 추가하고 사진의 id 값을 저장해주었다. 

https://picsum.photos/id/1/200/300

 

 

Lorem Picsum

 

Lorem Picsum

Lorem Ipsum... but for photos

picsum.photos

 

 

 

728x90
반응형