[node.js] 평점, 리뷰 구현하기

suji·2022년 8월 8일
post-thumbnail

🎬Cluvie

영화를 주제로 사용자들이 모임을 주최하고 참여할 수 있도록 커뮤니티 서비스

모임의 모집이 완료됐을 때, 모임의 후기를 남길 수 있도록
리뷰 기능을 기획했다

처음 고민

처음에는 모임후기를 작성할때마다 reviews 테이블에 데이터가 저장되고
모임 후기 평점은 따로 reviews에서
해당 모임ID(club_id) 를 찾아 조회한 후,
평점을 산출하려고 했다

그렇다면...
평점을 조회할때마다 해당 모임ID를 찾아서 reviews 테이블에서 검색해야하고 그 값들을 모아 산출해야되는 복잡한 과정이 계속된다

즉, 쓸데없이 시간이 많이 소요된다
또한, 프로젝트 단위에서는 괜찮지만 -> 서비스가 확장되어 리뷰의 개수와 모임의 개수가 많아진다면 힘들어진다

"테이블 반정규화"

위와 같이 reviews 테이블에서 grades 테이블을 추가로 만들어 반정규화 시켰다

grades 테이블에서는,
모임ID, 평점, 리뷰개수를 평점 테이블을 하나 더 만들었다
평점을 조회할 때는 평점만 get 할 수 있도록...

  • 의도했던 과정 평점
    • 5점이 추가된다면 리뷰에는 행 하나 추가
    • 평점 테이블은 점수 추가
    • 평점 계산
    • grades 테이블에서 grade 값 update

그러나 결국 이것도 전의 과정보다 뛰어나게 효율적이지는 않았다..

리뷰, 평점 테이블 완성

여러번의 시도와 많은 고민, 논리적인 계산 끝에..😊
(물론 더 좋은 방법이 있을 것이고, 내 방법은 그저 코린이의 방법이니 '이렇게 했구나~'라고만 봐주시길 바라며..)

평점을 계산할때마다 해당 모임에 대한 리뷰를 조회하지 않도록
리뷰가 추가될 때마다 아예 평점 테이블을 업데이트 시키도록 구현해보았다

테이블 구조도와 코드를 같이 보며 정리해보겠다

service.js

// 리뷰 작성
  static writeReview = async ({ user_id, club_id, star_rating, contents }) => {
    const club = await Clubs.findOne({ where: { id: club_id } });
    const user = await Users.findOne({ where: { id: user_id } });
    if (!club) {
      const errorMessage = "존재하지 않는 모임입니다.";
      return { errorMessage };
    }
    if (!user) {
      const errorMessage = "해당 사용자를 찾을 수 없습니다";
      return { errorMessage };
    } else {
      const review = await Reviews.create({
        user_id,
        club_id,
        star_rating,
        contents,
      });
      return review;
    }
  };

// 평점 테이블 update
  static setReviewRating = async ({ club_id, star }) => {
    const rating = await Ratings.findOne({ where: { club_id: club_id } });
    await rating.increment({ count: 1 });
    await rating.increment("star_sum", {
      by: star,
    });
  };

// 평점계산 (총 별점 합 ÷ 리뷰개수)
  static calculateRating = async ({ club_id }) => {
    const ratingData = await Ratings.findOne({ where: { club_id: club_id } });
    const rating = ratingData.star_sum / ratingData.count;

    const result = await ratingData.update({ rating: rating });
    return result;
  };

router.js

clubRouter.post("/:club_id/review", verifyToken, async (req, res) => {
  try {
    const user_id = req.user;
    const club_id = req.params.club_id;
    // star_rating: 별점, contents: 리뷰작성
    const { star_rating, contents } = req.body;

    const review = await clubService.writeReview({
      club_id,
      user_id,
      star_rating,
      contents,
    });
    const star = review.star_rating;
    await clubService.setReviewRating({ club_id, star });
    await clubService.calculateRating({ club_id });

    // 에러 처리
    if (review.errorMessage) {
      res.status(403).json({ success: false, err: review.errorMessage });
    }
    res.status(200).json({ success: true });
  } catch (err) {
    if (err.message == "Validation error") {
      res.status(403).json({
        success: false,
        message: "모임 후기는 한 번만 작성할 수 있습니다",
      });
    } else {
      res.json({ success: false, message: err.message });
    }
  }
});
  1. 모임을 생성하면 rating 테이블 행이 추가된다
    리뷰개수, 모임 별점 합, 평점은 default 0

  1. 모임의 후기를 작성하면, ratings테이블에서 해당 모임에 대한
    리뷰개수, 모임 별점 합, 평점이 update 된다
    평점까지 계산되어 update!

  2. 리뷰가 추가될 때마다 ratings 테이블 업데이트 반복

  • 모임 한개당 rating 값은 하나만 생성되어야 하므로, 모임이 생성될때 rating도 생성되도록 모임의 create router에 함께 작성했다
  • ratings 테이블의 update를 따로 하지 않도록, 리뷰가 작성될 때마다 update되도록 리뷰 create router에 함께 작성했다
profile
문제를 해결하는 백엔드 개발자

0개의 댓글