Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
7f5ec67
[feat] BookCommandRedisAdapter (#48)
hd0rable Jul 4, 2025
15870ee
[feat] BookJpaRepository.findByIsbnIn (#48)
hd0rable Jul 4, 2025
e3971c2
[test] BookMostSearchedBooksControllerTest(#48)
hd0rable Jul 4, 2025
7eb5073
[test] BookSearchRankServiceTest (#48)
hd0rable Jul 4, 2025
0117897
[test] 책 렝킹 업데이트 스케줄러 작성 (#48)
hd0rable Jul 4, 2025
ebd6d8f
[test] response 관련 dto (#48)
hd0rable Jul 4, 2025
8751a96
[feat] BookMostSearchService.getMostSearchedBooks (#48)
hd0rable Jul 4, 2025
51db2a4
[feat] 가장많이 검색된 책 조회 유즈케이스 (#48)
hd0rable Jul 4, 2025
ecec33c
[feat] 가장많이 검색된 책 조회 컨트롤러 (#48)
hd0rable Jul 4, 2025
5df05e4
[feat] BookQueryPersistenceAdapter.findByIsbnIn (#48)
hd0rable Jul 4, 2025
abd294c
[feat] BookQueryPort.findByIsbnIn (#48)
hd0rable Jul 4, 2025
c3b276b
[feat] BookQueryRedisAdapter (#48)
hd0rable Jul 4, 2025
da1a08d
[feat] 레디스관련 port 작성 (#48)
hd0rable Jul 4, 2025
19184a7
[feat] 책 상세검색시 검색순위 정보 업데이트 추가 (#48)
hd0rable Jul 4, 2025
6f78577
[feat] RedisConfig추가 (#48)
hd0rable Jul 4, 2025
5a23a82
[feat] @ EnableScheduling
hd0rable Jul 4, 2025
0f663b5
[test] test (#48)
hd0rable Jul 4, 2025
31266bd
[test] 테스트 코드 수정 (#48)
hd0rable Jul 4, 2025
ab2e398
Merge branch 'feat/#42-post-book-saved' into feat/#48-get-most-serched
hd0rable Jul 4, 2025
d8377b8
Merge remote-tracking branch 'origin/develop' into feat/#48-get-most-…
hd0rable Jul 5, 2025
47d1201
Merge remote-tracking branch 'origin/develop' into feat/#48-get-most-…
hd0rable Jul 5, 2025
003d72c
[refactor] ci 레디스 탬플릿 수정 (#42)
hd0rable Jul 6, 2025
96a5840
[fix] 누락된 책 검색순위 업데이트 추가 (#48)
hd0rable Jul 7, 2025
cedaba8
[refactor] 메서드명 변경 (#48)
hd0rable Jul 7, 2025
3c56997
[refactor] 책 상세정보 캐싱 로직 추가 (#48)
hd0rable Jul 7, 2025
a826f7d
[refactor] 관련 에러코드 추가 (#48)
hd0rable Jul 7, 2025
691bcbb
[test] 테스트코드 수정 (#48)
hd0rable Jul 7, 2025
9acf60f
[refactor] 이너클래스 위치 수정 (#48)
hd0rable Jul 7, 2025
671ca86
[refactor] 안쓰는 함수 정리 (#48)
hd0rable Jul 7, 2025
80eb90e
[refactor] 서비스 로직 수정 (#48)
hd0rable Jul 7, 2025
de5fd7d
[refactor] 에러코드 수정 (#48)
hd0rable Jul 7, 2025
5cd9e04
[refactor] 레디스 커맨드/쿼리 어댑터 하나로 합침 (#48)
hd0rable Jul 8, 2025
5e53557
[test] 테스트코드 수정 (#48)
hd0rable Jul 8, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 13 additions & 8 deletions .github/workflows/ci-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,26 @@ jobs:
settings-path: ${{ github.workspace }} # location for the settings.xml file


- name: 🧾 Create application.yml from secret
# - name: 🧾 Create application.yml from secret
# run: |
# mkdir -p ${{ env.RESOURCE_PATH }}
# mkdir -p ${{ env.TEST_RESOURCE_PATH }}
# echo "${{ secrets.APPLICATION_YML_DEV }}" > ${{ env.RESOURCE_PATH }}/application.yml
# echo "${{ secrets.APPLICATION_YML_TEST }}" > ${{ env.TEST_RESOURCE_PATH }}/application-test.yml
- name: 🧾 Create application.yml from secret (base64)
run: |
mkdir -p ${{ env.RESOURCE_PATH }}
mkdir -p ${{ env.TEST_RESOURCE_PATH }}
echo "${{ secrets.APPLICATION_YML_DEV }}" > ${{ env.RESOURCE_PATH }}/application.yml
echo "${{ secrets.APPLICATION_YML_TEST }}" > ${{ env.TEST_RESOURCE_PATH }}/application-test.yml

echo "${{ secrets.APPLICATION_YML_DEV }}" | base64 --decode > ${{ env.RESOURCE_PATH }}/application.yml
echo "${{ secrets.APPLICATION_YML_TEST }}" | base64 --decode > ${{ env.TEST_RESOURCE_PATH }}/application-test.yml

Comment on lines 45 to 50

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

echoprintf로 치환해 다중 라인 secret 디코딩 신뢰성 향상
echo는 개행·백슬래시 해석 등으로 secret 내용이 변형될 가능성이 있습니다. printf '%s' 를 사용하면 내용을 그대로 전달할 수 있어 안전합니다.

-echo "${{ secrets.APPLICATION_YML_DEV }}" | base64 --decode > ${{ env.RESOURCE_PATH }}/application.yml
-echo "${{ secrets.APPLICATION_YML_TEST }}" | base64 --decode > ${{ env.TEST_RESOURCE_PATH }}/application-test.yml
+printf '%s' "${{ secrets.APPLICATION_YML_DEV }}" | base64 --decode > ${{ env.RESOURCE_PATH }}/application.yml
+printf '%s' "${{ secrets.APPLICATION_YML_TEST }}" | base64 --decode > ${{ env.TEST_RESOURCE_PATH }}/application-test.yml

추가로, 디코딩 실패 시 조기에 잡을 수 있도록 set -euo pipefail 선언을 고려해 보세요.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
run: |
mkdir -p ${{ env.RESOURCE_PATH }}
mkdir -p ${{ env.TEST_RESOURCE_PATH }}
echo "${{ secrets.APPLICATION_YML_DEV }}" > ${{ env.RESOURCE_PATH }}/application.yml
echo "${{ secrets.APPLICATION_YML_TEST }}" > ${{ env.TEST_RESOURCE_PATH }}/application-test.yml
echo "${{ secrets.APPLICATION_YML_DEV }}" | base64 --decode > ${{ env.RESOURCE_PATH }}/application.yml
echo "${{ secrets.APPLICATION_YML_TEST }}" | base64 --decode > ${{ env.TEST_RESOURCE_PATH }}/application-test.yml
run: |
mkdir -p ${{ env.RESOURCE_PATH }}
mkdir -p ${{ env.TEST_RESOURCE_PATH }}
printf '%s' "${{ secrets.APPLICATION_YML_DEV }}" | base64 --decode > ${{ env.RESOURCE_PATH }}/application.yml
printf '%s' "${{ secrets.APPLICATION_YML_TEST }}" | base64 --decode > ${{ env.TEST_RESOURCE_PATH }}/application-test.yml
🤖 Prompt for AI Agents
In .github/workflows/ci-workflow.yml around lines 45 to 50, replace the use of
echo with printf '%s' to decode the base64 secrets to prevent modification of
multiline secret content due to echo's interpretation of newlines and
backslashes. Additionally, add set -euo pipefail at the start of the run script
to ensure the workflow fails early on any error, including decoding failures.

- name: 👏🏻 grant execute permission for gradlew
run: chmod +x gradlew

# - name: 🚀 Start Redis
# uses: supercharge/redis-github-action@1.7.0
# with:
# redis-version: 7
- name: 🚀 Start Redis
uses: supercharge/redis-github-action@1.7.0
with:
redis-version: 7
Comment on lines +54 to +57

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Redis 액션 버전 태그 고정으로 공급망 위험 완화
supercharge/redis-github-action@1.7.0 태그는 mutable tag 입니다.
SHA-pinned 즉 supercharge/redis-github-action@<commit-sha> 형식으로 고정하면, 제3자 공격으로 태그가 다른 커밋을 가리켜도 빌드가 영향을 받지 않습니다.

🤖 Prompt for AI Agents
In .github/workflows/ci-workflow.yml around lines 54 to 57, the Redis GitHub
Action uses a mutable tag '1.7.0' which poses a supply chain risk. Replace the
version tag with a specific commit SHA to pin the action to an immutable
reference, preventing unexpected changes from affecting the build. Find the
commit SHA corresponding to version 1.7.0 and update the 'uses' field to
'supercharge/redis-github-action@<commit-sha>'.


- name: 🐘 build with Gradle
run: ./gradlew build
4 changes: 4 additions & 0 deletions src/main/java/konkuk/thip/ThipServerApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;

@EnableJpaAuditing
@EnableScheduling
@EnableAsync
@SpringBootApplication
public class ThipServerApplication {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import jakarta.validation.constraints.Pattern;
import konkuk.thip.book.adapter.in.web.response.GetBookDetailSearchResponse;
import konkuk.thip.book.adapter.in.web.response.GetBookMostSearchResponse;
import konkuk.thip.book.adapter.in.web.response.GetBookSearchListResponse;
import konkuk.thip.book.application.port.in.BookSearchUseCase;
import konkuk.thip.book.application.port.in.BookMostSearchUseCase;
import konkuk.thip.common.dto.BaseResponse;
import konkuk.thip.common.security.annotation.UserId;
import lombok.RequiredArgsConstructor;
Expand All @@ -16,6 +18,8 @@
public class BookQueryController {

private final BookSearchUseCase bookSearchUseCase;
private final BookMostSearchUseCase bookMostSearchUseCase;


//책 검색결과 조회
@GetMapping("/books")
Expand All @@ -36,4 +40,11 @@ public BaseResponse<GetBookDetailSearchResponse> getBookDetailSearch(@PathVariab
return BaseResponse.ok(GetBookDetailSearchResponse.of(bookSearchUseCase.searchDetailBooks(isbn,userId)));
}

//가장 많이 검색된 책 조회
@GetMapping("/books/most-searched")
public BaseResponse<GetBookMostSearchResponse> getMostSearchedBooks(@UserId final Long userId) {

return BaseResponse.ok(GetBookMostSearchResponse.of(bookMostSearchUseCase.getMostSearchedBooks(userId)));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package konkuk.thip.book.adapter.in.web.response;

import konkuk.thip.book.application.port.in.dto.BookMostSearchResult;
import lombok.Builder;

import java.util.List;

public record GetBookMostSearchResponse(
List<BookMostSearchResult.BookRankInfo> bookList
) {
public static GetBookMostSearchResponse of(BookMostSearchResult bookMostSearchResult) {
return new GetBookMostSearchResponse(bookMostSearchResult.bookList());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public NaverBookParseResult findBooksByKeyword(String keyword, int start) {
}

@Override
public NaverDetailBookParseResult findDetailBookByKeyword(String isbn) {
public NaverDetailBookParseResult findDetailBookByIsbn(String isbn) {
String xml = naverApiUtil.detailSearchBook(isbn); // 네이버 API 호출
return NaverBookXmlParser.parseBookDetail(xml); // XML 파싱
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
import konkuk.thip.book.adapter.out.jpa.BookJpaEntity;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;
import java.util.Optional;

public interface BookJpaRepository extends JpaRepository<BookJpaEntity, Long> {
Optional<BookJpaEntity> findByIsbn(String isbn);
List<BookJpaEntity> findByIsbnIn(List<String> isbnList);

@buzz0331 buzz0331 Jul 6, 2025

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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,15 @@
package konkuk.thip.book.adapter.out.persistence;

import konkuk.thip.book.adapter.out.mapper.BookMapper;
import konkuk.thip.book.application.port.out.BookQueryPort;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;

@Repository
@RequiredArgsConstructor
public class BookQueryPersistenceAdapter implements BookQueryPort {

private final BookJpaRepository bookJpaRepository;
private final BookMapper bookMapper;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package konkuk.thip.book.adapter.out.persistence;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import konkuk.thip.book.application.port.in.dto.BookMostSearchResult;
import konkuk.thip.book.application.port.out.BookRedisCommandPort;
import konkuk.thip.book.application.port.out.BookRedisQueryPort;
import konkuk.thip.common.exception.ExternalApiException;
import konkuk.thip.common.exception.code.ErrorCode;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import static konkuk.thip.common.exception.code.ErrorCode.JSON_PROCESSING_ERROR;

@Component
@RequiredArgsConstructor
public class BookRedisAdapter implements BookRedisQueryPort, BookRedisCommandPort {

private final RedisTemplate<String, String> redisTemplate;
private final DateTimeFormatter DAILY_KEY_FORMATTER =
DateTimeFormatter.ofPattern("yyyyMMdd");

@Value("${app.redis.search-count-prefix}")
private String searchCountPrefix;

@Value("${app.redis.search-rank-prefix}")
private String searchRankPrefix;

@Value("${app.redis.search-rank-detail-prefix}")
private String searchRankDetailPrefix;

private final ObjectMapper objectMapper;

@Override
public List<Map.Entry<String, Double>> getBookSearchRank(LocalDate date, int topN) {
return getTopNFromZSet(searchRankPrefix, date, topN);
}

@Override
public List<Map.Entry<String, Double>> getBookSearchCountTopN(LocalDate date, int topN) {
return getTopNFromZSet(searchCountPrefix, date, topN);
}

private List<Map.Entry<String, Double>> getTopNFromZSet(String prefix, LocalDate date, int topN) {
String dateStr = date.format(DAILY_KEY_FORMATTER);
String redisKey = prefix + dateStr;
Set<ZSetOperations.TypedTuple<String>> topNSet = redisTemplate.opsForZSet()
.reverseRangeWithScores(redisKey, 0, topN - 1);

if (topNSet == null) return Collections.emptyList();
return topNSet.stream()
.map(tuple -> Map.entry(tuple.getValue(), tuple.getScore()))
.collect(Collectors.toList());
}

@Override
public List<BookMostSearchResult.BookRankInfo> getYesterdayBookRankInfos(LocalDate date) {
String redisKey = searchRankDetailPrefix + date.format(DAILY_KEY_FORMATTER);
String json = redisTemplate.opsForValue().get(redisKey);

if (json == null || json.isBlank()) {
return List.of();
}
try {
return objectMapper.readValue(
json,
new TypeReference<List<BookMostSearchResult.BookRankInfo>>() {}
);
} catch (JsonProcessingException e) {
throw new ExternalApiException(ErrorCode.JSON_PROCESSING_ERROR);
}
}

@Override
public void incrementBookSearchCount(String isbn, LocalDate date) {
String redisKey = makeRedisKey(searchCountPrefix, date);
redisTemplate.opsForZSet().incrementScore(redisKey, isbn, 1.0);
}
Comment on lines +87 to +91

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

검색 카운트 키에도 TTL을 설정하여 데이터 누적을 방지하세요.

스케줄러가 이전 날짜의 카운트를 삭제하지만, 스케줄러 실패 시 키가 무한정 누적될 수 있습니다. 안전성을 위해 TTL을 설정하는 것을 권장합니다.

 @Override
 public void incrementBookSearchCount(String isbn, LocalDate date) {
     String redisKey = makeRedisKey(searchCountPrefix, date);
     redisTemplate.opsForZSet().incrementScore(redisKey, isbn, 1.0);
+    // 스케줄러 실패에 대비한 안전장치로 8일 TTL 설정
+    redisTemplate.expire(redisKey, Duration.ofDays(8));
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Override
public void incrementBookSearchCount(String isbn, LocalDate date) {
String redisKey = makeRedisKey(searchCountPrefix, date);
redisTemplate.opsForZSet().incrementScore(redisKey, isbn, 1.0);
}
@Override
public void incrementBookSearchCount(String isbn, LocalDate date) {
String redisKey = makeRedisKey(searchCountPrefix, date);
redisTemplate.opsForZSet().incrementScore(redisKey, isbn, 1.0);
// 스케줄러 실패에 대비한 안전장치로 8일 TTL 설정
redisTemplate.expire(redisKey, Duration.ofDays(8));
}
🤖 Prompt for AI Agents
In src/main/java/konkuk/thip/book/adapter/out/persistence/BookRedisAdapter.java
around lines 87 to 91, the incrementBookSearchCount method sets the search count
in Redis without a TTL, risking indefinite data accumulation if the scheduler
fails. Modify the method to set an appropriate TTL on the Redis key after
incrementing the score to ensure the key expires automatically and prevents
unbounded growth.


@Override
public void saveBookSearchRank(List<String> isbns, List<Double> scores, LocalDate date) {
String redisKey = makeRedisKey(searchRankPrefix, date);
for (int i = 0; i < isbns.size(); i++) {
redisTemplate.opsForZSet().add(redisKey, isbns.get(i), scores.get(i));
}
redisTemplate.expire(redisKey, Duration.ofDays(7));
}

@Override
public void saveBookSearchRankDetail(List<BookMostSearchResult.BookRankInfo> bookRankDetails, LocalDate date) {
String redisKey = makeRedisKey(searchRankDetailPrefix, date);
String detailJson;
try {
detailJson = objectMapper.writeValueAsString(bookRankDetails);
} catch (JsonProcessingException e) {
throw new ExternalApiException(JSON_PROCESSING_ERROR);
}
redisTemplate.opsForValue().set(redisKey, detailJson);
}
Comment on lines +102 to +112

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

상세 순위 데이터에도 TTL을 설정해야 합니다.

PR 목표에 따르면 순위 데이터는 7일 TTL을 가져야 하는데, 상세 데이터에는 TTL이 설정되지 않아 데이터가 영구적으로 남을 수 있습니다.

 @Override
 public void saveBookSearchRankDetail(List<BookMostSearchResult.BookRankInfo> bookRankDetails, LocalDate date) {
     String redisKey = makeRedisKey(searchRankDetailPrefix, date);
     String detailJson;
     try {
         detailJson = objectMapper.writeValueAsString(bookRankDetails);
     } catch (JsonProcessingException e) {
         throw new ExternalApiException(JSON_PROCESSING_ERROR);
     }
     redisTemplate.opsForValue().set(redisKey, detailJson);
+    redisTemplate.expire(redisKey, Duration.ofDays(7));
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Override
public void saveBookSearchRankDetail(List<BookMostSearchResult.BookRankInfo> bookRankDetails, LocalDate date) {
String redisKey = makeRedisKey(searchRankDetailPrefix, date);
String detailJson;
try {
detailJson = objectMapper.writeValueAsString(bookRankDetails);
} catch (JsonProcessingException e) {
throw new ExternalApiException(JSON_PROCESSING_ERROR);
}
redisTemplate.opsForValue().set(redisKey, detailJson);
}
@Override
public void saveBookSearchRankDetail(List<BookMostSearchResult.BookRankInfo> bookRankDetails, LocalDate date) {
String redisKey = makeRedisKey(searchRankDetailPrefix, date);
String detailJson;
try {
detailJson = objectMapper.writeValueAsString(bookRankDetails);
} catch (JsonProcessingException e) {
throw new ExternalApiException(JSON_PROCESSING_ERROR);
}
redisTemplate.opsForValue().set(redisKey, detailJson);
redisTemplate.expire(redisKey, Duration.ofDays(7));
}
🤖 Prompt for AI Agents
In src/main/java/konkuk/thip/book/adapter/out/persistence/BookRedisAdapter.java
around lines 102 to 112, the saveBookSearchRankDetail method saves detailed rank
data to Redis without setting a TTL, causing the data to persist indefinitely.
Modify the redisTemplate.opsForValue().set call to include a TTL of 7 days so
that the detailed rank data expires appropriately.


@Override
public void deleteBookSearchRank(LocalDate date) {
deleteZSetKey(searchRankPrefix, date);
}

@Override
public void deleteBookSearchCount(LocalDate date) { deleteZSetKey(searchCountPrefix, date); }

@Override
public void deleteBookSearchRankDetail(LocalDate date) { deleteZSetKey(searchRankDetailPrefix, date); }

private void deleteZSetKey(String prefix, LocalDate date) {
String redisKey = makeRedisKey(prefix, date);
redisTemplate.delete(redisKey);
}

private String makeRedisKey(String prefix, LocalDate date) {
String dateStr = date.format(DAILY_KEY_FORMATTER);
return prefix + dateStr;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package konkuk.thip.book.application.port.in;

import konkuk.thip.book.application.port.in.dto.BookMostSearchResult;

public interface BookMostSearchUseCase {
BookMostSearchResult getMostSearchedBooks(Long userId);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p2 : userId 메서드는 사용되지 않고 있습니다!

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package konkuk.thip.book.application.port.in.dto;

import konkuk.thip.book.adapter.in.web.response.GetBookMostSearchResponse;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
import lombok.Builder;

import java.util.List;

public record BookMostSearchResult(
List<BookRankInfo> bookList
) {
@Builder
public record BookRankInfo(
int rank,
String title,
String imageUrl,
String isbn
) {}
Comment on lines +11 to +17

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

엇 전 Result, response 각각의 inner class 를 얘기한 거긴 한데, 완전히 중복되는 코드라 의미없다고 생각하신 거면 지금 상태도 괜찮습니다!!

조회로직시에 result, response 를 어디까지 분리할지를 한번 얘기나눠봐도 좋을 것 같습니다

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

앗 넵!! 제가 착각햇나봐요 한번 얘기해보면 좋을것같습니다!


public static BookMostSearchResult of(List<BookRankInfo> bookList) {
return new BookMostSearchResult(bookList);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@

public interface BookApiQueryPort {
NaverBookParseResult findBooksByKeyword(String keyword, int start);
NaverDetailBookParseResult findDetailBookByKeyword(String isbn);
NaverDetailBookParseResult findDetailBookByIsbn(String isbn);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
package konkuk.thip.book.application.port.out;

public interface BookQueryPort {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package konkuk.thip.book.application.port.out;

import konkuk.thip.book.application.port.in.dto.BookMostSearchResult;

import java.time.LocalDate;
import java.util.List;

public interface BookRedisCommandPort {
void incrementBookSearchCount(String isbn, LocalDate date);
void saveBookSearchRank(List<String> isbns, List<Double> scores, LocalDate date);
void saveBookSearchRankDetail(List<BookMostSearchResult.BookRankInfo> bookRankDetails, LocalDate date);
void deleteBookSearchRank(LocalDate date);
void deleteBookSearchCount(LocalDate date);
void deleteBookSearchRankDetail(LocalDate date);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package konkuk.thip.book.application.port.out;

import konkuk.thip.book.application.port.in.dto.BookMostSearchResult;

import java.time.LocalDate;
import java.util.List;
import java.util.Map;

public interface BookRedisQueryPort {
List<Map.Entry<String, Double>> getBookSearchRank(LocalDate date, int topN);
List<Map.Entry<String, Double>> getBookSearchCountTopN(LocalDate date, int topN);
List<BookMostSearchResult.BookRankInfo> getYesterdayBookRankInfos(LocalDate date);
}
Loading