-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
*feat : redis 랭킹, 검색 구현 #31
base: dev
Are you sure you want to change the base?
Changes from 3 commits
fa455af
d8b812c
cbb7f7b
d922e1b
ae6df9d
ac8c530
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package playlist.server.mainpagerankingInfo.vo; | ||
|
||
import lombok.Builder; | ||
import lombok.Getter; | ||
import playlist.server.domain.domains.ranking.domain.RankingInfo; | ||
import playlist.server.domain.domains.ranking.domain.RankingType; | ||
|
||
@Getter | ||
@Builder | ||
public class MainPageRankingInfoVo { | ||
private final RankingInfo rankingInfo; | ||
private final RankingType rankingType; | ||
|
||
public static MainPageRankingInfoVo from(RankingInfo rankingInfo, RankingType rankingType) { | ||
return MainPageRankingInfoVo.builder() | ||
.rankingInfo(rankingInfo) | ||
.rankingType(rankingType) | ||
.build(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,69 @@ | ||||||||||||||||||||||||||||||
package playlist.server.ranking.controller; | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
import io.swagger.v3.oas.annotations.Operation; | ||||||||||||||||||||||||||||||
import io.swagger.v3.oas.annotations.tags.Tag; | ||||||||||||||||||||||||||||||
import lombok.RequiredArgsConstructor; | ||||||||||||||||||||||||||||||
import org.springframework.http.ResponseEntity; | ||||||||||||||||||||||||||||||
import org.springframework.web.bind.annotation.GetMapping; | ||||||||||||||||||||||||||||||
import org.springframework.web.bind.annotation.RequestMapping; | ||||||||||||||||||||||||||||||
import org.springframework.web.bind.annotation.RequestParam; | ||||||||||||||||||||||||||||||
import org.springframework.web.bind.annotation.RestController; | ||||||||||||||||||||||||||||||
import playlist.server.mainpagerankingInfo.vo.MainPageRankingInfoVo; | ||||||||||||||||||||||||||||||
import playlist.server.ranking.service.RankingService; | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
@RestController | ||||||||||||||||||||||||||||||
@RequestMapping("/ranking") | ||||||||||||||||||||||||||||||
@RequiredArgsConstructor | ||||||||||||||||||||||||||||||
@Tag(name = "1. [랭킹]") | ||||||||||||||||||||||||||||||
public class RankingController { | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
private final RankingService rankingService; | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
@Operation(summary = "랭킹을 조회합니다.") | ||||||||||||||||||||||||||||||
@GetMapping("/daily") | ||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mapping을 차라리
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. type 매개변수 사용해서 type값에 따라 좋아요랑 조회수 랭킹 정보 가져오는 로직 만들어 봤습니다! |
||||||||||||||||||||||||||||||
public ResponseEntity<MainPageRankingInfoVo> getDailyRanking( | ||||||||||||||||||||||||||||||
@RequestParam(name = "type", defaultValue = "like") String type | ||||||||||||||||||||||||||||||
) { | ||||||||||||||||||||||||||||||
try { | ||||||||||||||||||||||||||||||
MainPageRankingInfoVo rankingInfoVo = rankingService.getRanking("daily", type, "boardId"); | ||||||||||||||||||||||||||||||
if (rankingInfoVo == null) { | ||||||||||||||||||||||||||||||
return ResponseEntity.notFound().build(); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
return ResponseEntity.ok(rankingInfoVo); | ||||||||||||||||||||||||||||||
} catch (Exception e) { | ||||||||||||||||||||||||||||||
return ResponseEntity.badRequest().build(); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
@Operation(summary = "주간 랭킹을 조회합니다.") | ||||||||||||||||||||||||||||||
@GetMapping("/weekly") | ||||||||||||||||||||||||||||||
public ResponseEntity<MainPageRankingInfoVo> getWeeklyRanking( | ||||||||||||||||||||||||||||||
@RequestParam(name = "type", defaultValue = "like") String type | ||||||||||||||||||||||||||||||
) { | ||||||||||||||||||||||||||||||
try { | ||||||||||||||||||||||||||||||
MainPageRankingInfoVo rankingInfoVo = rankingService.getRanking("weekly", type, "boardId"); | ||||||||||||||||||||||||||||||
if (rankingInfoVo == null) { | ||||||||||||||||||||||||||||||
return ResponseEntity.notFound().build(); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
return ResponseEntity.ok(rankingInfoVo); | ||||||||||||||||||||||||||||||
} catch (Exception e) { | ||||||||||||||||||||||||||||||
return ResponseEntity.badRequest().build(); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 다른메소드 동일하지만 service레벨에서 Exception을 내주고 있는것 같아서 badRequest Return의 경우에는 발생시키는 이유를 모르겠네요. 코드를 이런식으로 만들어 볼 수 있지않을까요?
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 두번째 인자값인 type의 경우에도 enum으로 정의가 가능할 것 같아요. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. boardId 이거는 못했습니다..두번째 인자값인 type의 경우에도 enum으로 정의가 가능할 것 같아요. 이건 했습니다! |
||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
@Operation(summary = "월간 랭킹을 조회합니다.") | ||||||||||||||||||||||||||||||
@GetMapping("/monthly") | ||||||||||||||||||||||||||||||
public ResponseEntity<MainPageRankingInfoVo> getMonthlyRanking( | ||||||||||||||||||||||||||||||
@RequestParam(name = "type", defaultValue = "like") String type | ||||||||||||||||||||||||||||||
) { | ||||||||||||||||||||||||||||||
try { | ||||||||||||||||||||||||||||||
MainPageRankingInfoVo rankingInfoVo = rankingService.getRanking("monthly", type, "boardId"); | ||||||||||||||||||||||||||||||
if (rankingInfoVo == null) { | ||||||||||||||||||||||||||||||
return ResponseEntity.notFound().build(); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
return ResponseEntity.ok(rankingInfoVo); | ||||||||||||||||||||||||||||||
} catch (Exception e) { | ||||||||||||||||||||||||||||||
return ResponseEntity.badRequest().build(); | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package playlist.server.ranking.service; | ||
|
||
import org.springframework.data.redis.core.RedisTemplate; | ||
import org.springframework.stereotype.Service; | ||
import lombok.RequiredArgsConstructor; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class RankingLikeService { | ||
|
||
private final RedisTemplate<String, String> redisTemplate; | ||
|
||
public void incrementLikes(String rankingType, String boardId) { | ||
try { | ||
String countsKey = rankingType + "_LIKE_COUNTS"; | ||
redisTemplate.opsForHash().increment(countsKey, boardId, 1L); | ||
} catch (Exception e) { | ||
throw new RuntimeException("좋아요 증가 실패"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. RuntimeException의 사용은 우리 프로젝트에서 사용을 하면 안됩니다. 그래도 Exception을 선언해서 안전한 개발방식을 만든건 좋네요 👍 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. LikeIncrementException 완료! |
||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package playlist.server.ranking.service; | ||
|
||
import org.springframework.stereotype.Service; | ||
import playlist.server.domain.domains.ranking.domain.RankingInfo; | ||
import playlist.server.domain.domains.ranking.domain.RankingType; | ||
import playlist.server.mainpagerankingInfo.vo.MainPageRankingInfoVo; | ||
import lombok.RequiredArgsConstructor; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class RankingService { | ||
|
||
private final RankingLikeService rankingLikeService; | ||
private final RankingViewService rankingViewService; | ||
|
||
public MainPageRankingInfoVo getRanking(String rankingType, String type, String boardId) { | ||
try { | ||
RankingInfo rankingInfo; | ||
if ("daily".equalsIgnoreCase(rankingType)) { | ||
rankingInfo = RankingInfo.DAILY; | ||
} else if ("weekly".equalsIgnoreCase(rankingType)) { | ||
rankingInfo = RankingInfo.WEEKLY; | ||
} else if ("monthly".equalsIgnoreCase(rankingType)) { | ||
rankingInfo = RankingInfo.MONTHLY; | ||
} else { | ||
throw new IllegalArgumentException("유효하지 않은 랭킹 타입"); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Controller레벨에서 rankingType을 문자열로 주지않고, Enum타입을 주면 해당 코드도 필요 없지 않을까요? 그래도 서비스레이어에서 모든 로직처리하는건 괜찮아 보이네요. |
||
|
||
if ("like".equals(type)) { | ||
rankingLikeService.incrementLikes(rankingType, boardId); | ||
} else if ("view".equals(type)) { | ||
rankingViewService.incrementViews(rankingType, boardId); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. like와 view도 enum으로 정의해보는게 좋을것 같아요 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 완료! |
||
} else { | ||
throw new IllegalArgumentException("유효하지 않은 파라미터"); | ||
} | ||
|
||
return MainPageRankingInfoVo.from(rankingInfo, RankingType.valueOf(rankingType.toUpperCase())); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 혹시 Enum만 들어있는 정보를 Return하는 이유가 있을까요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 한 번 해봤습니다! 근데 getLikeRankingInfoList 이거에 대한 레디스에서 가져오는 로직을 잘 모르겠어서 public List getLikeRankingInfoList() {
|
||
} catch (Exception e) { | ||
throw new RuntimeException("데이터 가져오는거 실패"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Custom하게 제작한 Exception을 활용해주세요 👍 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. DataFetchException, InvalidParameterException 완료! |
||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package playlist.server.ranking.service; | ||
|
||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.data.redis.core.RedisTemplate; | ||
import org.springframework.stereotype.Service; | ||
import lombok.RequiredArgsConstructor; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class RankingViewService { | ||
|
||
private final RedisTemplate<String, String> redisTemplate; | ||
|
||
public void incrementViews(String rankingType, String boardId) { | ||
try { | ||
String countsKey = rankingType + "_VIEW_COUNTS"; | ||
redisTemplate.opsForHash().increment(countsKey, boardId, 1L); | ||
} catch (Exception e) { | ||
throw new RuntimeException("조회수 증가 실패"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Exception! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ViewIncrementException 완료! |
||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package playlist.server.search.controller; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RequestParam; | ||
import org.springframework.web.bind.annotation.RestController; | ||
import playlist.server.search.service.SearchService; | ||
import playlist.server.search.vo.SearchVo; | ||
|
||
import java.util.List; | ||
|
||
@RestController | ||
@RequestMapping("/search") | ||
@RequiredArgsConstructor | ||
public class SearchController { | ||
|
||
private final SearchService searchService; | ||
|
||
// 태그 기반으로 검색 | ||
@GetMapping("/tag") | ||
public ResponseEntity<List<SearchVo>> searchByTag(@RequestParam("tag") String tag) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ResponseEntity로 제작하는건 Controller에서 하는게 좋아보이네요. |
||
List<SearchVo> searchResults = searchService.searchByTag(tag); | ||
|
||
if (searchResults.isEmpty()) { | ||
throw new IllegalArgumentException("해당 태그가 없다"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Exception! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TagNotFoundException 했습니다! |
||
} | ||
|
||
return ResponseEntity.ok(searchResults); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package playlist.server.search.service; | ||
|
||
import org.springframework.stereotype.Service; | ||
import playlist.server.domain.domains.search.repository.SearchRepository; | ||
import playlist.server.search.vo.SearchVo; | ||
|
||
import java.util.List; | ||
import java.util.stream.Collectors; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class SearchService { | ||
|
||
private final SearchRepository searchRepository; | ||
|
||
public List<SearchVo> searchByTag(String tag) { | ||
return searchRepository.findByTag(tag).stream() | ||
.map(search -> new SearchVo(search.getTitle(), search.getDescription())) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💯 |
||
.collect(Collectors.toList()); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package playlist.server.search.vo; | ||
|
||
import lombok.Data; | ||
import lombok.NoArgsConstructor; | ||
import lombok.AllArgsConstructor; | ||
|
||
@Data | ||
@NoArgsConstructor | ||
@AllArgsConstructor | ||
public class SearchVo { | ||
private String title; | ||
private String description; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package playlist.server.domain.domains.ranking.domain; | ||
|
||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
|
||
@AllArgsConstructor | ||
@Getter | ||
public enum RankingInfo { | ||
DAILY("like_daily_counts", "like_daily_ranking"), | ||
WEEKLY("like_weekly_counts", "like_weekly_ranking"), | ||
MONTHLY("like_monthly_counts", "like_monthly_ranking"); | ||
|
||
private final String countsKey; | ||
private final String rankingKey; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 변수명 직관적인거 보기 좋네요 |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package playlist.server.domain.domains.ranking.domain; | ||
|
||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
|
||
@AllArgsConstructor | ||
@Getter | ||
public enum RankingType { | ||
DAILY("일간"), | ||
WEEKLY("주간"), | ||
MONTHLY("월간"); | ||
|
||
private final String description; | ||
|
||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package playlist.server.domain.domains.search.domain; | ||
|
||
import jakarta.persistence.Entity; | ||
import jakarta.persistence.GeneratedValue; | ||
import jakarta.persistence.GenerationType; | ||
import jakarta.persistence.Id; | ||
import lombok.Getter; | ||
import lombok.Setter; | ||
import lombok.NoArgsConstructor; | ||
|
||
@Entity | ||
@Getter | ||
@Setter | ||
@NoArgsConstructor | ||
public class Search { | ||
|
||
@Id | ||
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||
private Long id; | ||
|
||
private String title; | ||
private String description; | ||
private String tag; | ||
Comment on lines
+12
to
+24
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Search의 경우에는 다른 팀이 직접 구현한 엔티티를 활용해야 하다보니, Search Entity가 필요한 이유가 납득이 가지 않네용. 왜 생성한 것인지 의도를 풀어줄 수 있을까요? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 다른 팀이 직접 구현한 엔티티를 활용해야 하다보니 사실 이걸 생각한다는 걸 생각하긴 했는데 , 한 번 Search 관련 Api를 쭉 만들어보고 싶어서 해봤습니다! |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package playlist.server.domain.domains.search.repository; | ||
|
||
import org.springframework.data.jpa.repository.JpaRepository; | ||
import playlist.server.domain.domains.search.domain.Search; | ||
import java.util.List; | ||
|
||
public interface SearchRepository extends JpaRepository<Search, Long> { | ||
List<Search> findByTag(String tag); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
태그로 어울리는 정보를 전달하는거 매우 좋아보입니다 ^^