diff --git a/src/main/java/page/clab/api/domain/community/board/adapter/in/web/HotBoardsRetrievalController.java b/src/main/java/page/clab/api/domain/community/board/adapter/in/web/HotBoardsRetrievalController.java
index 2d69d45fb..bd7151a5e 100644
--- a/src/main/java/page/clab/api/domain/community/board/adapter/in/web/HotBoardsRetrievalController.java
+++ b/src/main/java/page/clab/api/domain/community/board/adapter/in/web/HotBoardsRetrievalController.java
@@ -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;
@@ -25,16 +24,13 @@ public class HotBoardsRetrievalController {
private final RetrieveHotBoardsUseCase retrieveHotBoardsUseCase;
@Operation(summary = "[G] 커뮤니티 인기 게시글 목록 조회", description = "ROLE_GUEST 이상의 권한이 필요함
" +
- "반응(이모지), 댓글 수를 합친 결과가 높은 순으로 size만큼 조회 가능
" +
"인기 게시글 선정 타입 설정 가능")
@PreAuthorize("hasRole('GUEST')")
@GetMapping("/hot")
public ApiResponse> retrieveHotBoards(
- @Min(message = "min.board.size", value = 0)
- @RequestParam(name = "size", defaultValue = "5") int size,
@RequestParam(name = "type") HotBoardStrategyType type
) {
- List boards = retrieveHotBoardsUseCase.retrieveHotBoards(size, type);
+ List boards = retrieveHotBoardsUseCase.retrieveHotBoards(type);
return ApiResponse.success(boards);
}
}
diff --git a/src/main/java/page/clab/api/domain/community/board/adapter/out/persistence/RedisHotBoardPersistenceAdapter.java b/src/main/java/page/clab/api/domain/community/board/adapter/out/persistence/RedisHotBoardPersistenceAdapter.java
new file mode 100644
index 000000000..8ebd20644
--- /dev/null
+++ b/src/main/java/page/clab/api/domain/community/board/adapter/out/persistence/RedisHotBoardPersistenceAdapter.java
@@ -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 redisTemplate;
+
+ @Override
+ public void save(String boardId) {
+ redisTemplate.opsForList().rightPush(HOT_BOARDS_KEY, boardId);
+ }
+
+ @Override
+ public List findAll() {
+ List hotBoards = redisTemplate.opsForList().range(HOT_BOARDS_KEY, 0, -1);
+ return (hotBoards != null) ? hotBoards : List.of();
+ }
+
+ @Override
+ public void clearHotBoard() {
+ redisTemplate.delete(HOT_BOARDS_KEY);
+ }
+}
diff --git a/src/main/java/page/clab/api/domain/community/board/application/port/in/RetrieveHotBoardsUseCase.java b/src/main/java/page/clab/api/domain/community/board/application/port/in/RetrieveHotBoardsUseCase.java
index 59b4f0a79..2278e44ab 100644
--- a/src/main/java/page/clab/api/domain/community/board/application/port/in/RetrieveHotBoardsUseCase.java
+++ b/src/main/java/page/clab/api/domain/community/board/application/port/in/RetrieveHotBoardsUseCase.java
@@ -6,5 +6,5 @@
import java.util.List;
public interface RetrieveHotBoardsUseCase {
- List retrieveHotBoards(int size, HotBoardStrategyType type);
+ List retrieveHotBoards(HotBoardStrategyType type);
}
diff --git a/src/main/java/page/clab/api/domain/community/board/application/port/out/RegisterHotBoardPort.java b/src/main/java/page/clab/api/domain/community/board/application/port/out/RegisterHotBoardPort.java
new file mode 100644
index 000000000..20e66b609
--- /dev/null
+++ b/src/main/java/page/clab/api/domain/community/board/application/port/out/RegisterHotBoardPort.java
@@ -0,0 +1,5 @@
+package page.clab.api.domain.community.board.application.port.out;
+
+public interface RegisterHotBoardPort {
+ void save(String boardId);
+}
diff --git a/src/main/java/page/clab/api/domain/community/board/application/port/out/RemoveHotBoardPort.java b/src/main/java/page/clab/api/domain/community/board/application/port/out/RemoveHotBoardPort.java
new file mode 100644
index 000000000..c0fbe82c6
--- /dev/null
+++ b/src/main/java/page/clab/api/domain/community/board/application/port/out/RemoveHotBoardPort.java
@@ -0,0 +1,5 @@
+package page.clab.api.domain.community.board.application.port.out;
+
+public interface RemoveHotBoardPort {
+ void clearHotBoard();
+}
diff --git a/src/main/java/page/clab/api/domain/community/board/application/port/out/RetrieveHotBoardPort.java b/src/main/java/page/clab/api/domain/community/board/application/port/out/RetrieveHotBoardPort.java
new file mode 100644
index 000000000..c356f8d6e
--- /dev/null
+++ b/src/main/java/page/clab/api/domain/community/board/application/port/out/RetrieveHotBoardPort.java
@@ -0,0 +1,7 @@
+package page.clab.api.domain.community.board.application.port.out;
+
+import java.util.List;
+
+public interface RetrieveHotBoardPort {
+ List findAll();
+}
diff --git a/src/main/java/page/clab/api/domain/community/board/application/service/DefaultHotBoardsRetrievalService.java b/src/main/java/page/clab/api/domain/community/board/application/service/DefaultHotBoardService.java
similarity index 72%
rename from src/main/java/page/clab/api/domain/community/board/application/service/DefaultHotBoardsRetrievalService.java
rename to src/main/java/page/clab/api/domain/community/board/application/service/DefaultHotBoardService.java
index f0370345b..d668537df 100644
--- a/src/main/java/page/clab/api/domain/community/board/application/service/DefaultHotBoardsRetrievalService.java
+++ b/src/main/java/page/clab/api/domain/community/board/application/service/DefaultHotBoardService.java
@@ -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;
@@ -22,9 +26,12 @@
@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;
@@ -32,31 +39,46 @@ public class DefaultHotBoardsRetrievalService implements HotBoardsStrategy {
@Transactional
@Override
- public List retrieveHotBoards(int size) {
- List hotBoards = getHotBoards(size);
+ public List retrieveHotBoards() {
+ List 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 getHotBoards(int size) {
- // 만약 게시글의 총 개수가 size보다 적다면 모든 게시글 반환
+ @Transactional
+ @Scheduled(cron = "0 0 0 * * MON") // 매주 월요일 00:00 실행
+ public void saveHotBoards() {
+ clearHotBoards(); // 저장된 지난 인기 게시글 초기화
+
+ List hotBoardIds = getHotBoards().stream()
+ .map(Board::getId)
+ .map(String::valueOf)
+ .toList();
+
+ hotBoardIds.forEach(registerHotBoardPort::save);
+ }
+
+ private List getHotBoards() {
+ // 만약 게시글의 총 개수가 5개보다 적다면 모든 게시글 반환
List allBoards = retrieveBoardPort.findAll();
- if (allBoards.size() < size) {
+ if (allBoards.size() < 5) {
return sortBoardsByReactionAndDateWithLimit(allBoards.size(), allBoards);
}
- List hotBoards = getHotBoardsForWeek(1, size);
+ List hotBoards = getHotBoardsForWeek(1, 5);
int weeksAgo = 2;
// 필요한 수량을 확보할 때까지 반복해서 이전 주로 이동하여 인기 게시글 보충
- while (hotBoards.size() < size) {
- List additionalBoards = getLatestHotBoardForWeek(weeksAgo++, size - hotBoards.size());
+ while (hotBoards.size() < 5) {
+ List additionalBoards = getLatestHotBoardForWeek(weeksAgo++, 5 - hotBoards.size());
if (additionalBoards != null && !additionalBoards.isEmpty()) {
hotBoards.addAll(additionalBoards);
}
}
+
return hotBoards;
}
@@ -108,4 +130,8 @@ private BoardListResponseDto mapToBoardListResponseDto(Board board, MemberDetail
return mapper.toListDto(board, memberInfo, commentCount);
}
+
+ private void clearHotBoards() {
+ removeHotBoardPort.clearHotBoard();
+ }
}
diff --git a/src/main/java/page/clab/api/domain/community/board/application/service/HotBoardsRetrievalService.java b/src/main/java/page/clab/api/domain/community/board/application/service/HotBoardsRetrievalService.java
index 90ec3c9ad..b358965fc 100644
--- a/src/main/java/page/clab/api/domain/community/board/application/service/HotBoardsRetrievalService.java
+++ b/src/main/java/page/clab/api/domain/community/board/application/service/HotBoardsRetrievalService.java
@@ -16,8 +16,8 @@ public class HotBoardsRetrievalService implements RetrieveHotBoardsUseCase {
private final Map strategies;
@Override
- public List retrieveHotBoards(int size, HotBoardStrategyType type) {
+ public List retrieveHotBoards(HotBoardStrategyType type) {
HotBoardsStrategy hotBoardsStrategy = strategies.get(type.getKey());
- return hotBoardsStrategy.retrieveHotBoards(size);
+ return hotBoardsStrategy.retrieveHotBoards();
}
}
diff --git a/src/main/java/page/clab/api/domain/community/board/application/service/HotBoardsStrategy.java b/src/main/java/page/clab/api/domain/community/board/application/service/HotBoardsStrategy.java
index 7a9f0311d..f438cfe60 100644
--- a/src/main/java/page/clab/api/domain/community/board/application/service/HotBoardsStrategy.java
+++ b/src/main/java/page/clab/api/domain/community/board/application/service/HotBoardsStrategy.java
@@ -5,5 +5,5 @@
import java.util.List;
public interface HotBoardsStrategy {
- List retrieveHotBoards(int size);
+ List retrieveHotBoards();
}