

fk를 두 개 갖고있는 좋아요 테이블을 만들었어요.
원래라면 헤더에서 추출한 jwt로 user_id를 req 객체에 넣어줘야하지만, jwt를 req 객체에 넣는 기능을 추후에 개발하고 일단은 body로 받기로 했어요.
export const addLike = (req, res) => {
const id = req.params.id;
const { user_id } = req.body;
const sql = `INSERT INTO likes (user_id, liked_book_id)
VALUES (?, ?)`;
const values = [user_id, id];
conn.query(sql, values, (err, results) => {
if (err) return res.status(statusCode.INTERNAL_SERVER_ERROR).json({message: err.message});
return res.status(statusCode.CREATED).json(results);
});
};
export const deleteLike = (req, res) => {
const id = req.params.id;
const { user_id } = req.body;
const sql = `DELETE FROM likes WHERE user_id = ? AND liked_book_id = ?`;
const values = [user_id, id];
conn.query(sql, values, (err, results) => {
if (err) return res.status(statusCode.INTERNAL_SERVER_ERROR).json({message: err.message});
return res.status(statusCode.CREATED).json(results);
});
}
SQL을 사용해서 구현했어요.
count()를 사용하면 테이블의 행 수를 셀 수 있습니다. 개수가 필요한 경우나 존재하는지(0과 1로 구분 가능)를 확인할 수 있습니다.
SELECT count(*) FROM table;
테이블 칼럼명이 마음에 안 드는 경우도 이썽요. 그렇다고 마음대로 테이블의 칼럼을 바꾸면 곤란한 상황이 올 수 있습니다. 그럴 때 사용하는 것이 AS를 통해서 별명(다른 이름)을 만들어서 사용하는 것입니다.
SELECT 기존컬럼명 AS 원하는컬럼명 FROM table;
SELECT *,
(SELECT COUNT(*) FROM likes WHERE liked_book_id = books.id) AS likes
FROM books;

와 같이 하니 좋아요의 개수까지 넣어서 반환되었어요
서브 쿼리란 쿼리 안에 쿼리를 의미합니다. 쉽게 말해 기존 쿼리가 있고 그 안에서 또 다른 쿼리를 사용하는 것입니다. 다양한 예시가 있지만 SELECT문으로 예시를 들 수 있어요..
SELECT *, (SELECT 필요한컬럼 FROM table2) FROM table1;
서브쿼리(Subquery) 서브쿼리(subquery)란 다른 쿼리 내부에 포함되어 있는 SELETE 문을 의미한다.
서브쿼리를 포함하고 있는 쿼리를 외부쿼리(outer query)라고 부르며, 서브쿼리는 내부쿼리(inner query)라고 한다.
export const findBooks = (req, res) => {
const { categoryId, isNew, limit, currentPage } = req.query;
let sql = 'SELECT *, (SELECT count(*) FROM likes WHERE books.id=liked_book_id) AS likes FROM books';
const offset = limit * (currentPage - 1);
let values = [];
if (categoryId && isNew) {
sql += ' WHERE category_id = ? AND pub_date BETWEEN DATE_SUB(NOW(), INTERVAL 1 MONTH) AND NOW()';
values.push(categoryId);
}
else if (categoryId) {
sql += ' WHERE category_id = ?';
values.push(categoryId);
}
else if (isNew) {
sql += ' WHERE pub_date BETWEEN DATE_SUB(NOW(), INTERVAL 1 MONTH) AND NOW()';
}
sql += ' LIMIT ? OFFSET ?';
values.push(parseInt(limit || 3), offset);
db.query(sql, values, (err, results) => {
if (err) return res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({message: err.message});
return res.status(StatusCodes.OK).json(results);
});
};
변경점은 서브쿼리를 포함한 sql로 변경했다는 점이에요. 응답도 잘 돌아왔어요.
[
{
"id": 3,
"title": "백설공주들",
"img": 3,
"form": "종이책",
"isbn": "2",
"pages": 100,
"summary": "사과..",
"detail": "빨간 사과..",
"author": "김사과",
"contents": "목차입니다.",
"price": 20000,
"pub_date": "2026-02-01",
"category_id": 0,
"likes": 3
},
]
개별 도서 조회시 사용자가 좋아요 했는지 여부를 추가해야해요
단어 그대로 값이 존재하는지의 여부를 확인하는 함수입니다. count()로도 확인할 수 있지만 직관적으로 알아보기에는 EXISTS가 좋아 보입니다. 존재 여부는 0은 존재하지 않고, 1은 존재하는 것을 의미합니다.
SELECT EXISTS (SELECT * FROM table WHERE id = id);
카테고리와 책 모두 pk 이름이 id라 덮어씌워지는 문제가 있었어요.
카테고리 테이블의 pk 이름을 category_id 로 변경했어요.
export const findBookById = (req, res) => {
const { user_id } = req.body;
const book_id = req.params.id;
const sql = `SELECT *,
(SELECT COUNT(*) FROM likes WHERE liked_book_id = books.id) AS likes,
(SELECT EXISTS(SELECT * FROM likes WHERE user_id = ? AND liked_book_id=?)) AS liked
FROM books
LEFT JOIN category
ON books.category_id = category.category_id
WHERE books.id = ?;`
const values = [user_id, book_id, book_id];
db.query(sql, values, (err, results) => {
if (err) return res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({message: err.message});
if (results.length === 0) return res.status(StatusCodes.NOT_FOUND).end();
return res.status(StatusCodes.OK).json(results[0]);
});
};
{
"id": 1,
"title": "어린왕자들",
"img": 2,
"category_id": 1,
"form": "종이책",
"isbn": "0",
"pages": 100,
"summary": "어리다..",
"detail": "많이 어리다..",
"author": "김어림",
"contents": "목차입니다.",
"price": 20000,
"pub_date": "2019-01-01",
"category_name": "사회",
"likes": 3,
"liked": 1
}
그리하여 도서 상세 정보 조회시 이러한 응답을 가져오게 됐어요.

현재까지의 DB ERD에요.