❤️ 찜하기(좋아요) 기능, 어떻게 설계할까?
중고 거래 플랫폼을 만들다 보면 꼭 들어가는 기능이 있죠. 바로 “찜하기”, 또는 “좋아요” 기능입니다.
사용자가 관심 있는 상품을 표시하고, 나중에 쉽게 다시 찾을 수 있도록 도와주는 이 UX는 단순해 보여도 설계 단계에서 고민할 게 은근 많았습니다.
이번 MoneyTalk 프로젝트에서도 해당 기능을 구현하며 두 가지 방식 사이에서 꽤 많은 고민을 했습니다. 어떤 기술이 현재 팀과 프로젝트의 상황에 적합할까? 확장성은 어떻게 가져갈 수 있을까?
✅ 찜 기능, 어떤 방식으로 구현할까?
처음엔 당연하게 RDB에 저장해야겠다고 생각했는데, 성능이나 실시간 반영 이슈를 고려하면서 Redis 기반 방식도 후보에 올랐습니다.
| DB 기반 | favorite_products 테이블을 만들어 찜 정보를 저장 |
| Redis 기반 | Redis의 Set 또는 SortedSet을 활용해 실시간으로 찜 정보 관리 |
📊 왜 우리는 DB 기반을 선택했을까?
결론부터 말하자면, 처음엔 DB 기반이 더 적절하다는 판단을 내렸습니다. 그 이유는 다음과 같습니다:
📌 장점
- 데이터 영속성: 서버가 재시작되어도 찜 정보는 안전하게 유지됩니다.
- 복합 쿼리 가능: 유저의 찜 목록 조회, 찜 순위 통계 등 다양한 활용이 가능합니다.
- 기존 인프라와의 연동이 쉬움: Spring JPA, QueryDSL 등 현재 프로젝트에 이미 도입된 기술과 궁합이 좋습니다.
- 알림, 백오피스, 통계 기능으로 확장 쉬움
⚠️ 고려한 단점
- 유저 수와 찜 데이터가 많아지면 쓰기 성능 이슈가 발생할 수 있습니다.
- 실시간 트렌드 반영에는 약간의 지연이 생길 수도 있습니다.
🧠 Redis 기반은 언제 고려할까?
물론 Redis 기반이 더 적합한 상황도 있습니다.
- 실시간 인기 상품 노출이 중요한 서비스
- 찜 수에 따라 상품을 정렬하거나, 트렌드를 빠르게 반영해야 하는 피드 구성
- DB 쓰기 병목 완화, 캐싱 레이어 분리
아직 우리 프로젝트는 이런 실시간 피드가 핵심은 아니고, 데이터 안정성이 더 중요한 단계라 Redis는 차후 확장용 후보로 남겨두기로 했습니다.
📐 실제 설계는 이렇게 했습니다
Table favorite_products {
id BIGINT [pk, increment]
user_id BIGINT [ref: > users.id]
product_id BIGINT [ref: > products.id]
created_at DATETIME [default: `CURRENT_TIMESTAMP`]
}
- user_id + product_id 조합에는 유니크 제약조건을 걸어서 중복 찜 방지
- 찜하기는 토글 방식으로 처리: 없으면 생성, 있으면 삭제
- 특정 상품의 찜 수는 count(*), 유저의 찜 목록은 where user_id = ?로 간단하게 조회
🚀 앞으로의 확장 방향은?
찜하기 기능은 단순히 저장만 하고 끝나는 기능이 아닙니다. 우리는 이 기능을 기반으로 여러 기능으로 확장해나갈 계획이에요:
- Redis SortedSet 기반으로 실시간 인기 상품 캐싱
- Kafka와 같은 메시지 큐를 활용한 비동기 싱크 처리
- Elasticsearch 연계로 찜 수 기반 정렬이 가능한 검색 고도화
✍️ 마치며
기능 하나를 설계할 때도 다양한 기술적 선택지가 존재하고, 각 선택지에는 그 나름의 트레이드오프가 있습니다.
중요한 건 “지금 우리 프로젝트의 상황에 맞는 결정”을 내리는 것이고, 찜하기 기능은 지속적으로 발전 가능한 구조로 시작하는 것이 중요하다고 생각합니다.
찜하기 기능은 단순한 버튼 이상이었고,
유저 경험, 데이터 구조, 성능, 확장성까지 생각하게 만든 작은 but 중요한 기능이었습니다.