Skip to content

Commit

Permalink
refactor(HotBoards): 인기 게시글 저장에 Redis를 사용하도록 변경
Browse files Browse the repository at this point in the history
  • Loading branch information
SongJaeHoonn committed Nov 27, 2024
1 parent 2560a09 commit 901ee99
Show file tree
Hide file tree
Showing 9 changed files with 96 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.constraints.Min;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
Expand All @@ -25,16 +24,13 @@ public class HotBoardsRetrievalController {
private final RetrieveHotBoardsUseCase retrieveHotBoardsUseCase;

@Operation(summary = "[G] 커뮤니티 인기 게시글 목록 조회", description = "ROLE_GUEST 이상의 권한이 필요함<br>" +
"반응(이모지), 댓글 수를 합친 결과가 높은 순으로 size만큼 조회 가능<br>" +
"인기 게시글 선정 타입 설정 가능")
@PreAuthorize("hasRole('GUEST')")
@GetMapping("/hot")
public ApiResponse<List<BoardListResponseDto>> retrieveHotBoards(
@Min(message = "min.board.size", value = 0)
@RequestParam(name = "size", defaultValue = "5") int size,
@RequestParam(name = "type") HotBoardStrategyType type
) {
List<BoardListResponseDto> boards = retrieveHotBoardsUseCase.retrieveHotBoards(size, type);
List<BoardListResponseDto> boards = retrieveHotBoardsUseCase.retrieveHotBoards(type);
return ApiResponse.success(boards);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package page.clab.api.domain.community.board.adapter.out.persistence;

import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import page.clab.api.domain.community.board.application.port.out.RegisterHotBoardPort;
import page.clab.api.domain.community.board.application.port.out.RemoveHotBoardPort;
import page.clab.api.domain.community.board.application.port.out.RetrieveHotBoardPort;

import java.util.List;

@Component
@RequiredArgsConstructor
public class RedisHotBoardPersistenceAdapter implements
RegisterHotBoardPort,
RetrieveHotBoardPort,
RemoveHotBoardPort {

private static final String HOT_BOARDS_KEY = "hotBoards";

private final RedisTemplate<String, String> redisTemplate;

@Override
public void save(String boardId) {
redisTemplate.opsForList().rightPush(HOT_BOARDS_KEY, boardId);
}

@Override
public List<String> findAll() {
List<String> hotBoards = redisTemplate.opsForList().range(HOT_BOARDS_KEY, 0, -1);
return (hotBoards != null) ? hotBoards : List.of();
}

@Override
public void clearHotBoard() {
redisTemplate.delete(HOT_BOARDS_KEY);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
import java.util.List;

public interface RetrieveHotBoardsUseCase {
List<BoardListResponseDto> retrieveHotBoards(int size, HotBoardStrategyType type);
List<BoardListResponseDto> retrieveHotBoards(HotBoardStrategyType type);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package page.clab.api.domain.community.board.application.port.out;

public interface RegisterHotBoardPort {
void save(String boardId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package page.clab.api.domain.community.board.application.port.out;

public interface RemoveHotBoardPort {
void clearHotBoard();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package page.clab.api.domain.community.board.application.port.out;

import java.util.List;

public interface RetrieveHotBoardPort {
List<String> findAll();
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@

import com.drew.lang.annotations.NotNull;
import lombok.RequiredArgsConstructor;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import page.clab.api.domain.community.board.application.dto.mapper.BoardDtoMapper;
import page.clab.api.domain.community.board.application.dto.response.BoardListResponseDto;
import page.clab.api.domain.community.board.application.port.out.RegisterHotBoardPort;
import page.clab.api.domain.community.board.application.port.out.RemoveHotBoardPort;
import page.clab.api.domain.community.board.application.port.out.RetrieveBoardEmojiPort;
import page.clab.api.domain.community.board.application.port.out.RetrieveBoardPort;
import page.clab.api.domain.community.board.application.port.out.RetrieveHotBoardPort;
import page.clab.api.domain.community.board.domain.Board;
import page.clab.api.domain.memberManagement.member.application.dto.shared.MemberDetailedInfoDto;
import page.clab.api.external.community.comment.application.port.ExternalRetrieveCommentUseCase;
Expand All @@ -22,41 +26,59 @@

@Service("default")
@RequiredArgsConstructor
public class DefaultHotBoardsRetrievalService implements HotBoardsStrategy {
public class DefaultHotBoardService implements HotBoardsStrategy {

private final RetrieveBoardPort retrieveBoardPort;
private final RetrieveHotBoardPort retrieveHotBoardPort;
private final RegisterHotBoardPort registerHotBoardPort;
private final RemoveHotBoardPort removeHotBoardPort;
private final RetrieveBoardEmojiPort retrieveBoardEmojiPort;
private final ExternalRetrieveCommentUseCase externalRetrieveCommentUseCase;
private final ExternalRetrieveMemberUseCase externalRetrieveMemberUseCase;
private final BoardDtoMapper mapper;

@Transactional
@Override
public List<BoardListResponseDto> retrieveHotBoards(int size) {
List<Board> hotBoards = getHotBoards(size);
public List<BoardListResponseDto> retrieveHotBoards() {
List<String> hotBoardIds = retrieveHotBoardPort.findAll();

return hotBoards.stream()
return hotBoardIds.stream()
.map(hotBoardId -> retrieveBoardPort.getById(Long.parseLong(hotBoardId)))
.map(board -> mapToBoardListResponseDto(board, getMemberDetailedInfoByBoard(board)))
.toList();
}

private List<Board> getHotBoards(int size) {
// 만약 게시글의 총 개수가 size보다 적다면 모든 게시글 반환
@Transactional
@Scheduled(cron = "0 0 0 * * MON") // 매주 월요일 00:00 실행
public void saveHotBoards() {
clearHotBoards(); // 저장된 지난 인기 게시글 초기화

List<String> hotBoardIds = getHotBoards().stream()
.map(Board::getId)
.map(String::valueOf)
.toList();

hotBoardIds.forEach(registerHotBoardPort::save);
}

private List<Board> getHotBoards() {
// 만약 게시글의 총 개수가 5개보다 적다면 모든 게시글 반환
List<Board> allBoards = retrieveBoardPort.findAll();
if (allBoards.size() < size) {
if (allBoards.size() < 5) {
return sortBoardsByReactionAndDateWithLimit(allBoards.size(), allBoards);
}

List<Board> hotBoards = getHotBoardsForWeek(1, size);
List<Board> hotBoards = getHotBoardsForWeek(1, 5);

int weeksAgo = 2;
// 필요한 수량을 확보할 때까지 반복해서 이전 주로 이동하여 인기 게시글 보충
while (hotBoards.size() < size) {
List<Board> additionalBoards = getLatestHotBoardForWeek(weeksAgo++, size - hotBoards.size());
while (hotBoards.size() < 5) {
List<Board> additionalBoards = getLatestHotBoardForWeek(weeksAgo++, 5 - hotBoards.size());
if (additionalBoards != null && !additionalBoards.isEmpty()) {
hotBoards.addAll(additionalBoards);
}
}

return hotBoards;
}

Expand Down Expand Up @@ -108,4 +130,8 @@ private BoardListResponseDto mapToBoardListResponseDto(Board board, MemberDetail

return mapper.toListDto(board, memberInfo, commentCount);
}

private void clearHotBoards() {
removeHotBoardPort.clearHotBoard();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ public class HotBoardsRetrievalService implements RetrieveHotBoardsUseCase {
private final Map<String, HotBoardsStrategy> strategies;

@Override
public List<BoardListResponseDto> retrieveHotBoards(int size, HotBoardStrategyType type) {
public List<BoardListResponseDto> retrieveHotBoards(HotBoardStrategyType type) {
HotBoardsStrategy hotBoardsStrategy = strategies.get(type.getKey());
return hotBoardsStrategy.retrieveHotBoards(size);
return hotBoardsStrategy.retrieveHotBoards();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@
import java.util.List;

public interface HotBoardsStrategy {
List<BoardListResponseDto> retrieveHotBoards(int size);
List<BoardListResponseDto> retrieveHotBoards();
}

0 comments on commit 901ee99

Please sign in to comment.