[프로그래머스] Node기반 REST API 구현(6)

Lina Hongbi Ko·2024년 10월 8일
0

Programmers_BootCamp

목록 보기
31/76
post-thumbnail

2024년 10월 8일

✏️ 좋아요 추가

💡 db 다이어그램 그리기

💡 likes 테이블 생성 + foreign key 설정

💡 좋아요 추가 API 설계

  • 전체든 개별 도서 단위이든, 좋아요를 하려면 bookId를 들고 가서 INSERT해서 (좋아요)할 수 있음
  • 그럼 누가 좋아요 했는지 어떻게 알 수 있을까?
    • 로그인한 상태여야 좋아요 할 수 있음
    • 로그인한 상태면 토큰을 받았으니까, 토큰가지고 누구인지 알 수 있음
    • 그래서 좋아요를 insert 할 때, 로그인이 된 상태이므로 로그인 할 때 받은 토큰을 들고 있다가 header에 전달해줄 것임
    • 그리고나서 payload값을 읽을 수 있음 → 사용자의 id를 읽어낼수 있음
    • 읽어낸 id로 좋아요를 insert할 것임
    • 하지만 지금은 토큰을 가지고 왔다 갔다 하는 것을 안해봤으니까, 일단 토큰을 직접 주지 않은 상태로 id값이 있다고 가정하고 구현해볼 예정
      • 나중에 토큰 추가 되면 다시 정식으로 바꿀 것임 → 일단 INSERT부터

💡 좋아요 추가 구현

  • 셋팅해주고,
// likes.js

const express = require("express");
const router = express.Router();
router.use(express.json());

const { addLike, removeLike } = require("../controller/LikeController");

// 좋아요 추가
router.post("/:id", addLike);

// 좋아요 삭제
router.delete("/:id", removeLike);

module.exports = router;
// LikeController.js

const conn = require("../mariadb"); // db 모듈
const { StatusCodes } = require("http-status-codes"); // status code 모듈

const addlike = (req, res) => {
  res.json("좋아요 추가");
};

const removeLike = (req, res) => {
  res.json("좋아요 삭제");
};

module.exports = { addlike, removeLike };
  • 워크벤치로 쿼리문이 맞는지 일단 확인해보자


  • 이제 구현해보자

// LikeController.js

const conn = require("../mariadb"); // db 모듈
const { StatusCodes } = require("http-status-codes"); // status code 모듈

const addlike = (req, res) => {

	const {liked_book_id} = req.params;
	const {user_id} = req.body; // 지금은 토큰이 없으므로 Body값으로 확인해보자	
	
  let sql = "INSERT INTO likes (user_id, liked_book_id) VALUES(?, ?)";
  let values = [user_id, liked_book_id];
  conn.query(sql, values, (err, results) => {
    if (err) {
      console.log(err);
      return res.status(StatusCodes.BAD_REQUEST).end();
    }

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

POSTMAN) POST + localhost:9999/likes/2 + {”user_id” : 1}

400 에러코드 화면을 보고 에러를 확인해보면,

iked_book_id가 null값이 들어온다는 얘기 → liked_boo_id가 parameter로 들어올 때, liked_book_id가 아니라 id로 들어오기 때문

// LikeController.js

const conn = require("../mariadb"); // db 모듈
const { StatusCodes } = require("http-status-codes"); // status code 모듈

const addlike = (req, res) => {

	const {id} = req.params;
	const {user_id} = req.body; // 지금은 토큰이 없으므로 Body값으로 확인해보자	
	
  let sql = "INSERT INTO likes (user_id, liked_book_id) VALUES(?, ?)";
  let values = [user_id, id];
  conn.query(sql, values, (err, results) => {
    if (err) {
      console.log(err);
      return res.status(StatusCodes.BAD_REQUEST).end();
    }

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

POSTMAN) POST + localhost:9999/likes/2 + {”user_id” : 1}

POSTMAN) POST + localhost:9999/likes/3 + {”user_id” : 1}

✏️ 좋아요 취소

  • 일단, 쿼리문이 맞는지 확인해보자! → user_id, liked_book_id 모두 조건이 맞아야 삭제가능!

💡 좋아요 취소 구현

// LikeController.js

const conn = require("../mariadb"); // db 모듈
const { StatusCodes } = require("http-status-codes"); // status code 모듈

const removeLike = (req, res) => {
  const { id } = req.params; // book_id
  const { user_id } = req.body;

  let sql = "DELETE FROM likes WHERE user_id = ? AND liked_book_id = ?";
  let values = [user_id, id];
  conn.query(sql, values, (err, results) => {
    if (err) {
      console.log(err);
      return res.status(StatusCodes.BAD_REQUEST).end();
    }

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

POSTMAN) DELETE + localhost:9999/likes/2 + {”user_id” : 1 }

✏️ 서브쿼리, count(), AS

  • books테이블에 likes 개수가 몇 개인지 넣어야함

    • 각 도서의 likes 개수를 likes테이블에서 liked_book_id에 해당하는 행의 개수를 세면 되니깐 likes 테이블을 이용해도 되지만,

    • 개별 도서 조회 설계 단계에서 book의 정보에 ‘likes: 좋아요수, liked: boolean’을 넣어주기로 했으므로 새로운 쿼리를 넣어 요청할 예정

  • 각 도서가 좋아요를 몇 개 받았는지 알아보자

    • count()를 사용할 예정

      *워크벤치에서 read only 되는 경우 : PK가 없으면 read only 상태로 모드가 자동으로 묶여 나옴

  • 일단 수동으로 INSERT 해보자

  • 1번 책을 좋아요한 갯수 쿼리문 : SELECT * FROM likes WHERE liked_book_id = 1;

  • 4개가 나오는 것을 알 수 있는데, 그냥 4개야 라고만 알고 싶으면
    : SELECT count(*) FROM likes WHERE liked_book_id = 1; → 조건을 만족하는 행 갯수

  • 이제 센 값을 books 테이블에 넣으면 되는데, 테이블에 직접 넣지는 않고, 프론트엔드에게 좋아요 개수를 주는 것만 생각하면 됨
    • JOIN은 두 개의 테이블을 엮어서 보여줬다면
    • JOIN이 되지 않은 상태에서 두 개의 테이블을 엮어서 데이터를 보여줄 것임
  • 일단 쿼리부터 보자
    • 위의 쿼리문의 결과값을 books 테이블에 뿌려주기만 할 것임

      SELECT *, (SELECT count(*) FROM likes WHERE liked_book_id=1) AS likes FROM books
    • id가 1인 책의 좋아요 개수를 선택해서 book테이블의 likes 컬럼에 추가한다

    • 그런데 우리는, book id마다 좋아요 개수가 다른 데이터를 보여준다. 어떻게 해야할까?

    • 위에서는 id=1인 책의 좋아요 개수를 모든 행에서 보여줬다. 그런데 우리는 books 테이블의 모든 행마다 id가 있으므로, books의 id가 liked_book_id와 일치하는 애들의 개수를 각각 보여주면 됨

    • SELECT , (SELECT count() FROM likes WHERE liked_book_id=books.id) AS likes FROM books

  • 서브 쿼리 : 쿼리 안에 쿼리
    • 쿼리의 결과값을 바깥의 쿼리에 사용할때 씀
    • 바깥의 쿼리 : 메인 쿼리
  • count() : 행 개수 셈
  • AS : 컬럼 별칭

✏️ EXIST()

💡 전체 도서 목록 조회 좋아요 개수 넣기 구현

// BookController.js

const conn = require("../mariadb"); // db 모듈
const { StatusCodes } = require("http-status-codes"); // status code 모듈

// (카테고리별, 신간 여부) 전체 도서 목록 조회
const allBooks = (req, res) => {
  let { category_id, news, limit, currentPage } = req.query;
  let offset = limit * (currentPage - 1);

  let sql =
    "SELECT *, (SELECT count(*) FROM likes WHERE liked_book_id=books.id) AS likes FROM books";
  let values = [];
  if (category_id && news) {
  ... 생략 ...

POSTMAN) GET + localhost:9999/books?limit=4¤tPage=1&category_id=0

💡 개별 도서 목록 조회 좋아요 boolean, 개수 넣기 구현

  • 개별도서 조회에 liked: boolean 넣기
    • 사용자가 좋아요를 했는지 여부를 포함
    • 쿼리문
      • SELECT * FROM likes WHERE user_id=1 AND liked_book_id=2;

      • likes 테이블에서 user id가 1이고, book id가 2인 애를 선택하면 그 애만 나옴

      • 그런데 행이 필요한게 아니라 개수가 필요함

      • SELECT count(*) FROM likes WHERE user_id = 1 AND liked_book_id = 2;

      • 그런데 어차피 갯수는 1 아니면 0 이므로 존재하는지만 물어보면 됨

      • SELECT EXISTS (SELECT * FROM likes WHERE user_id = 1 AND liked_book_id = 2); -> 불리언 True값 : 1

      • SELECT EXISTS (SELECT * FROM likes WHERE user_id = 1 AND liked_book_id = 9); -> 불리언 Fals값 : 0

  • 이제 구현해보자

  • 먼저, 좋아요 했는지에 대한 여부의 쿼리문을 생각해 보면

    • SELECT ,
      (SELECT EXISTS (SELECT
      FROM likes WHERE user_id = 1 AND liked_book_id = 2)) AS liked
      FROM books WHERE books.id = 2;

  • 좋아요 갯수까지 넣어서 생각해보면

    • SELECT ,
      (SELECT count(
      ) FROM likes WHERE liked_book_id=books.id) AS likes,
      (SELECT EXISTS (SELECT * FROM likes WHERE user_id = 1 AND liked_book_id = 8)) AS liked
      FROM books WHERE books.id = 8;

  • 원래 있던 쿼리문에 적용해서 구현해보자

    • SELECT ,
      (SELECT count(
      ) FROM likes WHERE liked_book_id = books.id AS likes,
      (SELECT EXISTS (SELECT * FROM likes WHERE user_id = 2 AND liked_book_id = 2)) AS liked
      FROM books
      LEFT JOIN category
      ON books.category_id = category.id
      WHERE books.id = 2;
// BookController.js

const conn = require("../mariadb"); // db 모듈
const { StatusCodes } = require("http-status-codes"); // status code 모듈


const bookDetail = (req, res) => {
  let { user_id } = req.body;
  let book_id = req.params.id;

  let 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.id 
            WHERE books.id = ?;`;
  let values = [user_id, book_id, book_id];
  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();
    }
  });
};

module.exports = { allBooks, bookDetail };

POSTMAN) GET + localhost:9999/books/2 + {”user_id” : 1}

POSTMAN) GET + localhost:9999/books/2 + {”user_id” : 4}

좋아요 갯수와 좋아요 불리언을 알 수 있음

POSTMAN) GET + localhost:9999/books/1 + {”user_id” : 1}

그런데 book의 id값이 이상하게 나옴 → book의 id값은 1부터 시작인데 0으로 출력된것을 볼 수 있음

  • 오류를 잡아보자.
    : id가 두개 있는 것을 알 수 있음 → json 형태에서는 id개 2개라서 id가 덮어쓰여짐

컬럼명을 그냥 간단히 바꿔보자

  • 소스코드도 바꾸자
// BookContoller.js

... 생략 ...

const bookDetail = (req, res) => {
  let { user_id } = req.body;
  let book_id = req.params.id;

  let 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 = ?;`;

POSTMAN) GET + localhost:9999/books/1 + {”user_id” : 1}

id가 1로 이제 잘 들어가는 것을 볼 수 있다

🍎🍏 오늘의 느낀점 : 오늘은 서브쿼리, count(), exist(), 등을 배운 날이었다. 쿼리문으로 테이블을 손보지 않고 손쉽게 원하는 데이터만 뾱뾱 뽑아쓰니깐 뭔가 신기하고 명쾌한 느낌이 들었다. 쿼리문에 대해서도 많이 알고 있으면, 데이터를 쉽게 뽑아 쓸 수 있으니깐 잘 알고 있어야 겠다는 생각이 들었다.

profile
프론트엔드개발자가 되고 싶어서 열심히 땅굴 파는 자

0개의 댓글