프로젝트/도서구매사이트
1022 좋아요 API 구현 / 좋아요 개수 출력 - COUNT(), AS, 서브쿼리
thinktank911
2025. 10. 21. 17:04
<좋아요 API 구현>
좋아요 추가 구현
- 좋아요 DB 테이블 만들기
- liked_book_id : books 테이블의 id를 Foreign Key 참조
- user_id : users 테이블의 id 를 Foreign Key 참조

※ 고려해야 할 사항 :
로그인할 때 받은 token을 header “Authorization”에 넣고,payload값을 읽어 사용자의 id를 가져와야 한다.
➡️ 추후에 구현하기로 하고 일단 user_id는 req.body값으로 넘겨준다.
- 좋아요 추가 API 구현
- user_id = req.body로 임시 전달
- liked_book_id = req.params로 전달
const addLike = (req, res)=>{
const {user_id} = req.body;
let {id} = req.params;
id = parseInt(id);
// 좋아요 추가
let sql = `INSERT INTO likes (user_id, liked_book_id) VALUES (?, ?)`;
let values = [user_id, id];
// INSERT 쿼리문
conn.query(sql, values,
function (err, results) {
if(err){
console.log(err)
return res.status(StatusCodes.BAD_REQUEST).end(); // Bad Request(400)
}
return res.status(StatusCodes.CREATED).json(results); // 201
}
);
}
좋아요 삭제 구현
- user_id, liked_book_id 둘 다 가져와서 where 조건을 걸고 삭제
const removeLike = (req, res)=>{
const {user_id} = req.body;
let {id} = req.params;
id = parseInt(id);
let sql = `DELETE FROM likes WHERE user_id = ? AND liked_book_id = ?`;
let values = [user_id, id];
// DELETE 쿼리문
conn.query(sql, values,
function (err, results) {
if(err){
console.log(err)
return res.status(StatusCodes.BAD_REQUEST).end(); // Bad Request(400)
}
return res.status(StatusCodes.OK).json(results); // 200
}
);
}
개별 도서 조회 시 좋아요 개수 컬럼 조회 - COUNT(), AS, 서브쿼리
- 개별 도서를 상세 조회할 때, 해당 도서의 좋아요 개수를 출력하고 싶다.
➡️ 도서 테이블 전체 조회 + 'likes' 컬럼 1개 추가
SELECT *,
(각 행마다 likes 테이블에 books.id를 liked_book_id로 가지고 있는 행 수) AS likes
FROM books;
- 각 행마다 likes 테이블에 books.id를 liked_book_id로 가지고 있는 행 수
- 서브 쿼리 : 쿼리 안에 쿼리
- count() : 행 개수
- AS : 컬럼 별칭
-- 조건을 만족하는 행 갯수
SELECT count(*) FROM likes WHERE liked_book_id = books.id;
-- 서브쿼리 조회 / 좋아요 개수가 포함된 books 테이블 조회
SELECT *,
(SELECT count(*)
FROM likes
WHERE liked_book_id = books.id) AS likes
FROM books;
개별 도서 조회 시, 사용자가 좋아요를 했는지 여부 조회
- EXISTS 를 활용한다.
- SELECT EXISTS (조건 셀레트 쿼리) : 해당 조건에 맞는 행이 존재하면 1, 존재하지 않으면 0 출력
-- 조건에 맞는 행이 존재하는지 여부 표현
SELECT EXISTS
(SELECT *
FROM likes
WHERE user_id =1
AND liked_book_id=1);
- 개별 도서 조회 시, 사용자가 좋아요를 했는지 여부 포함
-- 개별 도서 조회 시, 좋아요 개수와 사용자가 좋아요를 했는지 여부 포함
SELECT *,
(SELECT count(1)
FROM likes
WHERE liked_book_id = books.id
) AS likes, -- 좋아요 갯수
(SELECT EXISTS
(SELECT *
FROM likes
WHERE user_id =1
AND liked_book_id=1)
) AS liked -- 사용자가 좋아요 했는지 여부
FROM books
WHERE books.id=1;
개별도서 상세 조회 API 수정 - 좋아요 갯수와 좋아요 했는지 여부 추가
- category 테이블을 조인한 최종 쿼리를 넘겨준다.
- liked_book_id와 books.id 값을 req.params로 받은 id값을 동일하게 넣어준다.
[이슈] req.params 값을 id로 넘겨줬기 때문에 구조분해 할당 시 반드시 id로 받아야 한다.
만약 book_id로 새로운 변수명을 선언하고 싶다면, 구조분해 할당이 아닌 req.params.id 값을 변수명 book_id로 받아야 한다.
// 개별 도서 조회
router.get('/:id', bookDetail);
// 개별 도서 상세 조회
const bookDetail = (req, res)=>{
let {user_id} = req.body;
// let {id} = req.params.id; // 에러 : 구조분해 할당 안됨
let id = req.params.id;
book_id = parseInt(id);
let sql = `SELECT *,
(SELECT count(1)
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 = ?`;
let values = [user_id, book_id, book_id];
// SELECT 쿼리문
conn.query(sql, values,
(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();
}
)
}
category_id의 id가 중복으로 나오는 문제
- 개별도서 조회 시 해당 id로 출력이 안되는 문제가 발생했다.
- 이유는 books 테이블과 category 테이블을 조인하면서 각각의 PK명이 id로 동일하여, book.id를 나중에 조회되는 category가 덮어버리게 된다.
- 해결방안1 : 각각의 컬럼을 전부 AS로 별칭을 줘서 검색한다.
- 해결방안2 : category.id의 컬럼명을 category_id로 변경한다.
➡️ 현업이었다면 나라면 데이터의 명확성을 위해 방안1을 선택했겠지만, 공부하는 과정이니까 간단하게 방안2를 택했다.
// 변경 전
FROM books
LEFT JOIN category ON books.category_id = category.id
WHERE books.id = ?`;
// 변경 후
FROM books
LEFT JOIN category ON books.category_id = category.category_id
WHERE books.id = ?`;
➡️ 테이블 컬럼명 변경 후 쿼리에서도 category.id 를 category.category_id롤 변경해준다.