
영화를 주제로 사용자들이 모임을 주최하고 참여할 수 있도록 커뮤니티 서비스
모임의 모집이 완료됐을 때, 모임의 후기를 남길 수 있도록
리뷰 기능을 기획했다
처음에는 모임후기를 작성할때마다 reviews 테이블에 데이터가 저장되고
모임 후기 평점은 따로 reviews에서
해당 모임ID(club_id) 를 찾아 조회한 후,
평점을 산출하려고 했다
그렇다면...
평점을 조회할때마다 해당 모임ID를 찾아서 reviews 테이블에서 검색해야하고 그 값들을 모아 산출해야되는 복잡한 과정이 계속된다
즉, 쓸데없이 시간이 많이 소요된다
또한, 프로젝트 단위에서는 괜찮지만 -> 서비스가 확장되어 리뷰의 개수와 모임의 개수가 많아진다면 힘들어진다

위와 같이 reviews 테이블에서 grades 테이블을 추가로 만들어 반정규화 시켰다
grades 테이블에서는,
모임ID, 평점, 리뷰개수를 평점 테이블을 하나 더 만들었다
평점을 조회할 때는 평점만 get 할 수 있도록...
그러나 결국 이것도 전의 과정보다 뛰어나게 효율적이지는 않았다..
여러번의 시도와 많은 고민, 논리적인 계산 끝에..😊
(물론 더 좋은 방법이 있을 것이고, 내 방법은 그저 코린이의 방법이니 '이렇게 했구나~'라고만 봐주시길 바라며..)
평점을 계산할때마다 해당 모임에 대한 리뷰를 조회하지 않도록
리뷰가 추가될 때마다 아예 평점 테이블을 업데이트 시키도록 구현해보았다

테이블 구조도와 코드를 같이 보며 정리해보겠다
// 리뷰 작성
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;
};
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 });
}
}
});

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

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

- 모임 한개당 rating 값은 하나만 생성되어야 하므로, 모임이 생성될때 rating도 생성되도록 모임의 create router에 함께 작성했다
- ratings 테이블의 update를 따로 하지 않도록, 리뷰가 작성될 때마다 update되도록 리뷰 create router에 함께 작성했다