depromeet / SPOT-server

⚾️ 야구장 좌석 시야 확인 서비스, SPOT!
38 stars 0 forks source link

[고민] 공감(좋아요) 기능 구현 방법 #132

Closed EunjiShin closed 2 months ago

EunjiShin commented 2 months ago

💁‍♀️ 고민 사항

👀 배경 설명

요구사항

고려사항

✅ 참고 사항

EunjiShin commented 2 months ago

0. 전제

✨ review_likes 테이블 설계

요구사항에 따라, 유저가 어떤 리뷰에 공감했는지 기록을 남겨야 해요. 따라서 review_likes 테이블을 새롭게 만들고 관리하는 건 필수라고 생각되어서 공통 전제로 깔고 시작할게요.

필드명 자료형 제약사항 Description
id BIGINT PK, AI primary key
review_id BIGINT NN review pk
user_id BIGINT NN user pk

(created_at, updated_at 생략 & 삭제=좋아요 취소 이므로 soft delete 적용할 필요 없음)

✨ 좋아요 API 설계 관련

  1. API 하나로 공감 / 공감 취소를 모두 처리하는 방법 (이전에 좋아요 한 리뷰라면 좋아요 취소로 동작)
  2. 공감 API와 공감 취소 API를 각각 관리하는 방법

둘 중 어떤 방식으로 구현할 지는 AOS와의 논의가 필요해요. 일단 1번 방법을 고려하고 있어요.

EunjiShin commented 2 months ago

1. MySQL만 활용하여 구현하기

✨ 설명

review_likes 테이블만을 활용하여 기능을 구현해요.

좋아요 프로세스

  1. 유저가 리뷰에 좋아요를 남긴다 (API call)
  2. review_likes 테이블에 새로운 레코드를 insert 한다.

리뷰 정렬 프로세스

left join 또는 서브 쿼리를 활용, 블록에 포함된 리뷰를 조회할 때 review_likes 테이블에서 count 연산을 수행하고 이를 이용해 리뷰를 정렬한다.

✨ 장점

✨ 단점

EunjiShin commented 2 months ago

2. MySQL만 활용 + reviews 테이블에서 공감 수 직접 관리

✨ 설명

review_likes 테이블에서 유저 좋아요 관련 작업을 수행하고, reviews 테이블에 likes_count 필드를 추가해 공감순 정렬 구현

좋아요 프로세스

  1. 유저가 리뷰에 좋아요를 남긴다 (API call)
  2. review_likes 테이블에 새로운 레코드를 insert 한다.
  3. reviews.likes_count 필드를 증가시킨다.

리뷰 정렬 프로세스

reviews의 likes_count 필드를 이용해 order by 해주면 끝~

✨ 장점

✨ 단점

EunjiShin commented 2 months ago

3. 2번 방법 + redis 분산락

✨ 설명

2번 방법과 전부 동일한데, 동시성 이슈 보완을 위해 어플리케이션 락인 분산락을 적용한다.

좋아요 프로세스

  1. 유저가 리뷰에 좋아요를 남긴다 (API call)
  2. redis를 활용해 락을 획득한다.
  3. review_likes 테이블에 새로운 레코드를 insert 한다.
  4. reviews.likes_count 필드를 증가시킨다.
  5. 락을 반환한다.

리뷰 정렬 프로세스

reviews의 likes_count 필드를 이용해 order by 해주면 끝~22

✨ 장점

✨ 단점

EunjiShin commented 2 months ago

4. mysql + redis + spring batch

✨ 설명

redis는 싱글스레드라 원자성이 보장되므로, 어플리케이션에서 동시성 보장하지말고 redis에서 공감수를 관리하자.

좋아요 프로세스

  1. 유저가 리뷰에 좋아요를 남긴다 (API call)
  2. review_likes 테이블에 새로운 레코드를 insert 한다.
  3. redis에 리뷰별 좋아요 수를 update 한다.
  4. 하루 단위로 batch를 돌면서 redis에 저장된 리뷰 공감수를 mysql에 반영한다.

리뷰 정렬 프로세스

✨ 장점

✨ 단점

EunjiShin commented 2 months ago

~~ 인 마이 어피니언 ~~

SPOT 서비스의 트래픽과 개발 리소스를 고려했을 때, 3번 방법이 제일 적합할 것 같음! 2번 방법도 충분히 간단하고 효율적이지만, 아키텍처 확장이나 공부 목적(사이드플젝이니까ㅎ.ㅎ)으로 분산락을 적용해봐도 문제 없는 상황이라고 판단했어~ (redisson 라이브러리 사용하면 구현 난이도가 그렇게 높지 않기도 하고)

우리가 mysql 8.0 버전을 사용하니 Materialized view를 이용하거나, 메세지큐를 활용해 비동기 방식으로 구현하거나... 구현 방법은 더 여러가지가 있을 수 있겠으나, 그쯤되면 더 이상 기능 구현이 주가 아닐 것 같아서 😅 일단은 위의 저 방법들만 고려했다!

이제 @pminsung12 @wjdwnsdnjs13 의견 받고, 이견 없으면 작업 고고할게잉

wjdwnsdnjs13 commented 2 months ago

내가 좋아요 구현할 때엔 1, 2번 밖에 안 해보긴 했는데 확실히 2번은 동시성 이슈가 있긴 하더라구

2번 방법도 충분히 간단하고 효율적이지만, 아키텍처 확장이나 공부 목적(사이드플젝이니까ㅎ.ㅎ)으로 분산락을 적용해봐도 문제 없는 상황이라고 판단했어~ (redisson 라이브러리 사용하면 구현 난이도가 그렇게 높지 않기도 하고)

이런 거라면 3번 나도 동의!

근데, 궁금한 거 한 가지는 리뷰가 삭제되었을 때엔 좋아요는 어떻게 처리할 예정이야?? soft 딜리트라 좋아요는 건들지 않고 놔두는 게 나으려나??

pminsung12 commented 2 months ago

와 갓은지... 정리가 진짜 👍 👍 👍 👍 👍 👍

나는 2번, 3번을 고민을 좀 해봤는데 우리 지금 앱의 규모나 지출 등을 고려했을 때 2번이 제일 나은 것 같더라고.

그런데 확장성과 공부를 위해서~ 나도 3번으로 진행하면 제일 좋을 것 같아~. (비용만 괜찮다면..!)

p.s.) spring batch에 대해서 잘 몰라서 질문하는데, 4번 방법은 그러면 redis를 통해서 캐시로 저장하다가 crontab 같이 일정한 주기로 작동하는 스케줄러를 통해서 한번에 업데이트 한다는거지?

EunjiShin commented 2 months ago

@wjdwnsdnjs13

근데, 궁금한 거 한 가지는 리뷰가 삭제되었을 때엔 좋아요는 어떻게 처리할 예정이야??

이거는 이슈내용이랑은 무관한 주제인 것 같아서.. 따로 스레드 팔게! 소프트딜리트 목적이 백업이냐, 내부 데이터 수집 용도냐에 따라 달라질 것 같으니 디자인 파트도 확인해보아야 할 것 같아~

EunjiShin commented 2 months ago

@pminsung12

(비용만 괜찮다면..!)

얍 그래서 redis를 어떻게 세팅할 것인지 한번 찾아보고, 예상 견적 짜서 작업 시작 전에 먼저 공유할게! 만약 금액이 에바라면 2번 방안으로 유턴하면 될듯~

p.s.)

비슷해~ 배치 작업은 일괄적으로 대량의 작업을 처리하는 방식이고, spring batch는 그런 배치 작업을 스프링 프레임워크에서 제공해주는거야. @Scheduler 대신 spring batch를 굳이 쓰는 이유는, spring batch에 이것저것 이점이 있어서! (스케줄러는 정해진 시간에 자동으로 동작하고 개발자가 직접 실행할 수 없지만, 배치는 개발자가 컨트롤 가능 & 실패한 작업이 있다면 그 작업부터 재시작 가능 등등..)

EunjiShin commented 2 months ago
스크린샷 2024-08-14 오후 8 42 03

aws elastiCache (redis oss)를 cache.t2micro로 사용할 경우 750시간 free-tier 사용 가능함을 확인 논클러스터 모드를 이용, 단일 노드로 띄우면 충분히 비용 문제 없이 활용 가능할 것으로 판단

=> 3번 방법으로 진행 확정 땅땅! @pminsung12 @wjdwnsdnjs13

EunjiShin commented 1 month ago

((( 좋아요 수만 관리하는 테이블 따로 관리하는 방법, redis의 pub/sub 이용해서 batch 대신 쓰는 방법, 이벤트 활용하는 방법 등등... 다양하게 고민해보자 )))