프로젝트/도서구매사이트

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롤 변경해준다.