Skip to content

[FIX] 모바일 테스트를 위한 ip허용#125

Merged
icarus0616 merged 4 commits into
devfrom
fix/web/2
Feb 5, 2026
Merged

[FIX] 모바일 테스트를 위한 ip허용#125
icarus0616 merged 4 commits into
devfrom
fix/web/2

Conversation

@icarus0616
Copy link
Copy Markdown
Collaborator

@icarus0616 icarus0616 commented Feb 5, 2026

#124

Summary by CodeRabbit

  • New Features

    • Enhanced reading progress tracking with automatic start/end date management and page count synchronization across reading logs.
  • Improvements

    • Strengthened validation for total and current page entries with detailed error messages.
    • Added mobile testing support via local network access.
  • Bug Fixes

    • Fixed reading progress recalculation to properly reflect cumulative page updates.
    • Improved data consistency between reading logs and shelf items during deletions.

@icarus0616 icarus0616 self-assigned this Feb 5, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 5, 2026

📝 Walkthrough

Walkthrough

This PR introduces comprehensive state management for reading progress tracking by adding entity-level state transition methods to UserBooks, updating repositories to use user-scoped delete operations instead of global ones, extending query capabilities, refactoring service logic to leverage entity methods, and adding validation error statuses. CORS configuration is updated to support local network testing.

Changes

Cohort / File(s) Summary
Project Configuration & Test Setup
booklog/.gitignore, booklog/src/test/java/com/example/booklog/BooklogApplicationTests.java, booklog/global/config/WebConfig.java
Added test source ignoring section; activated "test" Spring profile for test context; replaced localhost CORS origins with local network (192.168..) patterns for mobile testing.
Entity State Management
booklog/src/main/java/com/example/booklog/domain/library/shelves/entity/UserBooks.java
Introduced multi-state progress management with changeStatus(), setTotalPages(), setCurrentPage(), applyReadingProgress(), setStartDateIfNull(), and updateFormat() methods; consolidated progress calculation and date initialization logic with side effects.
Repository Query & Delete Enhancements
booklog/src/main/java/com/example/booklog/domain/library/shelves/repository/BookshelfItemsRepository.java, booklog/src/main/java/com/example/booklog/domain/library/shelves/repository/ReadingLogsRepository.java, booklog/src/main/java/com/example/booklog/domain/library/shelves/repository/UserBooksRepository.java
Shifted delete operations from global scope to user-scoped (deleteByUserId* variants); added new query methods findUserBookIdsByUserIdAndStatus() and findBookIdsByUserIdAndShelfIdAndStatus() for status and shelf-based filtering; introduced user-aware reading log deletion.
Service Logic Refactoring
booklog/src/main/java/com/example/booklog/domain/library/shelves/service/ReadingLogsService.java, booklog/src/main/java/com/example/booklog/domain/library/shelves/service/UserBooksService.java
Integrated ReadingLogsRepository into UserBooksService; replaced manual state/date updates with entity changeStatus() calls; refactored delete workflows to support status-based, shelf-based, and cascading deletions with ownership validation; normalized page reading calculations and simplified progress handling via entity methods.
Error Status Definitions
booklog/global/common/apiPayload/code/status/ErrorStatus.java
Added three new validation error constants (INVALID_TOTAL_PAGE, CURRENT_PAGE_EXCEEDS_TOTAL, CURRENT_PAGE_INVALID) for page-related request validation.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant UserBooksService
    participant UserBooks
    participant ReadingLogsService
    participant ReadingLogsRepository

    Client->>UserBooksService: applyReadingProgress(currentPage, readDate)
    UserBooksService->>ReadingLogsService: apply(userBook, currentPage, readDate)
    ReadingLogsService->>ReadingLogsRepository: save/update reading log
    ReadingLogsRepository-->>ReadingLogsService: persisted log
    ReadingLogsService->>ReadingLogsService: recalcLogsAndUserBook()
    ReadingLogsService->>UserBooks: setCurrentPage(computed)
    UserBooks->>UserBooks: recalcProgressPercent()
    ReadingLogsService->>UserBooks: changeStatus(status)
    UserBooks->>UserBooks: apply state side effects
    UserBooks-->>ReadingLogsService: updated
    ReadingLogsService-->>UserBooksService: updated state
    UserBooksService-->>Client: response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • jaehyeon4406
  • ktg3891
  • millkk04

🐰 A reader's progress takes shape anew,
With status changes and pages accrued,
State flows through entities, scoped to each user,
No cascades global—each deletion more true,
The logs recalculate, progress shines through! 📚✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title check ⚠️ Warning The PR title '[FIX] 모바일 테스트를 위한 ip허용' (Allow IP for mobile testing) is misleading; while WebConfig.java does add IP allowlist changes, the changeset includes substantial unrelated changes: UserBooks entity refactoring, repository method changes, service logic updates, error status additions, and test configuration modifications. The title focuses on only a minor portion of the actual changes. Revise the title to reflect the comprehensive scope: e.g., '[REFACTOR] UserBooks entity and reading progress management' or split into separate, focused PRs—one for mobile IP allowlist, another for entity/service refactoring.
Docstring Coverage ⚠️ Warning Docstring coverage is 62.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/web/2

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In
`@booklog/src/main/java/com/example/booklog/domain/library/shelves/repository/BookshelfItemsRepository.java`:
- Around line 65-73: The deleteByUserIdAndBookIds method in
BookshelfItemsRepository will throw at runtime if called with an empty bookIds
list; like the fix applied for
ReadingLogsRepository.deleteByUserIdAndUserBookIds, ensure callers verify the
list before invoking the repository: add a guard (e.g., if bookIds == null ||
bookIds.isEmpty()) return 0 (or no-op) in the service/handler that calls
BookshelfItemsRepository.deleteByUserIdAndBookIds so the repository is never
invoked with an empty collection.

In
`@booklog/src/main/java/com/example/booklog/domain/library/shelves/service/UserBooksService.java`:
- Around line 214-228: The inline comment in UserBooksService that suggests
adding a repository method is stale and misleading because
userBooksRepository.findUserBookIdsByUserIdAndStatus already exists; remove or
update that comment (the lines above the call to
findUserBookIdsByUserIdAndStatus) so the block that deletes
readingLogsRepository.deleteByUserIdAndUserBookIds,
bookshelfItemsRepository.deleteByUserIdAndBookIds, and the final
userBooksRepository.deleteByUserIdAndStatus call is accurate and clean, keeping
the surrounding logic intact.
🧹 Nitpick comments (7)
booklog/.gitignore (1)

8-10: Avoid ignoring all src/test/ sources unless tests are intentionally excluded.

This rule hides all tests from version control, which can reduce CI coverage and code review visibility. If the goal is to ignore generated test artifacts only, narrow the pattern (e.g., **/build/, **/out/) or add a rationale in repo docs.

booklog/src/main/java/com/example/booklog/global/common/apiPayload/code/status/ErrorStatus.java (1)

71-72: Inconsistent naming pattern and missing punctuation.

  1. Naming inconsistency: Line 71 uses TOTAL_PAGE_INVALID while line 72 uses INVALID_TOTAL_PAGE. Consider using a consistent pattern (either X_INVALID or INVALID_X).
  2. The message for INVALID_TOTAL_PAGE is missing a period at the end, unlike other messages.
✏️ Suggested fix for consistency
-    INVALID_TOTAL_PAGE(HttpStatus.BAD_REQUEST, "LB004", "총 페이지를 입력해야합니다"),
+    TOTAL_PAGE_REQUIRED(HttpStatus.BAD_REQUEST, "LB004", "총 페이지를 입력해야합니다."),
booklog/src/main/java/com/example/booklog/global/config/WebConfig.java (1)

22-23: Consider restricting these patterns to development/test environments only.

The 192.168.*.*:* patterns are very broad, allowing CORS requests from any device on any 192.168.x.x subnet. Since production domains are configured in the same file, this pattern will likely be active in production as well.

While 192.168.x.x is a private IP range, best practice is to use Spring profiles to conditionally apply such permissive patterns only in non-production environments:

♻️ Suggested approach using profiles
// Option 1: Use `@Profile` annotation with separate config classes
`@Configuration`
`@Profile`("dev")
public class DevWebConfig implements WebMvcConfigurer {
    // Include 192.168.*.* patterns here
}

`@Configuration`
`@Profile`("prod")
public class ProdWebConfig implements WebMvcConfigurer {
    // Production-only origins
}

Alternatively, externalize the allowed origins to application-{profile}.yml and inject them via @Value.

booklog/src/main/java/com/example/booklog/domain/library/shelves/service/ReadingLogsService.java (1)

76-81: Minor redundancy: log.getCurrentPage() will be overwritten.

Line 79 passes log.getCurrentPage() as the third argument, but recalcLogsAndUserBook at line 81 will immediately recalculate and overwrite all logs' currentPage values. Consider passing 0 or documenting that this value is intentionally ignored.

booklog/src/main/java/com/example/booklog/domain/library/shelves/entity/UserBooks.java (2)

79-100: Inconsistent null handling: silent return vs. throwing exception.

changeStatus(null) silently returns (line 80), while setTotalPages(null) and setCurrentPage(null) throw GeneralException. Consider throwing an exception here too for consistent behavior and to help surface potential bugs at call sites.

♻️ Suggested fix for consistent null handling
 public void changeStatus(ReadingStatus newStatus) {
-    if (newStatus == null) return;
+    if (newStatus == null) {
+        throw new GeneralException(ErrorStatus.STATUS_INVALID); // or similar
+    }
 
     this.status = newStatus;

151-162: Avoid reassigning method parameters.

Line 157 reassigns the currentPage parameter, which can reduce clarity. Consider using a local variable instead.

♻️ Suggested refactor
 public void setCurrentPage(Integer currentPage) {
     if (currentPage == null || currentPage < 0) {
         throw new GeneralException(ErrorStatus.CURRENT_PAGE_INVALID);
     }
 
+    int clamped = currentPage;
     if (this.pageCountSnapshot != null && currentPage > this.pageCountSnapshot) {
-        currentPage = this.pageCountSnapshot; // clamp 정책
+        clamped = this.pageCountSnapshot; // clamp 정책
     }
 
-    this.currentPage = currentPage;
+    this.currentPage = clamped;
     recalcProgressPercent();
 }
booklog/src/main/java/com/example/booklog/domain/library/shelves/service/UserBooksService.java (1)

54-59: Redundant changeStatus calls after builder already sets status.

The builder at line 51 already sets the status. changeStatus is then called again, which while it sets startDate/endDate as side effects, also reassigns this.status unnecessarily. Consider extracting only the date-setting logic or documenting why the double assignment is intentional.

Comment on lines +65 to +73
/** ✅ (필수) 내 서재들에서만 bookIds 제거 */
@Modifying(clearAutomatically = true, flushAutomatically = true)
@Query("""
delete from BookshelfItems bi
where bi.book.id in :bookIds
and bi.shelf.user.id = :userId
""")
int deleteByUserIdAndBookIds(@Param("userId") Long userId,
@Param("bookIds") List<Long> bookIds);
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 | 🟡 Minor

Same empty-list concern applies here.

Similar to ReadingLogsRepository.deleteByUserIdAndUserBookIds, passing an empty bookIds list will cause a runtime exception. Ensure callers check for emptiness before calling.

🤖 Prompt for AI Agents
In
`@booklog/src/main/java/com/example/booklog/domain/library/shelves/repository/BookshelfItemsRepository.java`
around lines 65 - 73, The deleteByUserIdAndBookIds method in
BookshelfItemsRepository will throw at runtime if called with an empty bookIds
list; like the fix applied for
ReadingLogsRepository.deleteByUserIdAndUserBookIds, ensure callers verify the
list before invoking the repository: add a guard (e.g., if bookIds == null ||
bookIds.isEmpty()) return 0 (or no-op) in the service/handler that calls
BookshelfItemsRepository.deleteByUserIdAndBookIds so the repository is never
invoked with an empty collection.

Comment on lines 214 to 228
if (status != null) {
List<Long> bookIds = userBooksRepository.findBookIdsByUserIdAndStatus(userId, status);
if (!bookIds.isEmpty()) {
// ✅ 모든 서재에서 제거
bookshelfItemsRepository.deleteByBookIds(bookIds);
// ✅ 4-1) status에 해당하는 userBookIds를 먼저 구해서 로그 삭제
// (지금 repo에 userBookIds 조회가 없으니 간단히 하나 추가하는 걸 추천)
// 아래는 "추가할 repo 메서드" 참고

List<Long> userBookIds = userBooksRepository.findUserBookIdsByUserIdAndStatus(userId, status);
if (!userBookIds.isEmpty()) {
readingLogsRepository.deleteByUserIdAndUserBookIds(userId, userBookIds);

List<Long> bookIds = userBooksRepository.findBookIdsByUserIdAndStatus(userId, status);
if (!bookIds.isEmpty()) {
bookshelfItemsRepository.deleteByUserIdAndBookIds(userId, bookIds);
}
}
return userBooksRepository.deleteByUserIdAndStatus(userId, status);
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 | 🟡 Minor

Stale comment: the repository method already exists.

Lines 216-217 suggest adding a repo method, but findUserBookIdsByUserIdAndStatus is already defined and used at line 219. Remove the misleading comment.

🧹 Suggested cleanup
         // 4) [상태 기준 완전 삭제] status 있는 경우
         if (status != null) {
-            // ✅ 4-1) status에 해당하는 userBookIds를 먼저 구해서 로그 삭제
-            // (지금 repo에 userBookIds 조회가 없으니 간단히 하나 추가하는 걸 추천)
-            // 아래는 "추가할 repo 메서드" 참고
-
             List<Long> userBookIds = userBooksRepository.findUserBookIdsByUserIdAndStatus(userId, status);
📝 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
if (status != null) {
List<Long> bookIds = userBooksRepository.findBookIdsByUserIdAndStatus(userId, status);
if (!bookIds.isEmpty()) {
// ✅ 모든 서재에서 제거
bookshelfItemsRepository.deleteByBookIds(bookIds);
// ✅ 4-1) status에 해당하는 userBookIds를 먼저 구해서 로그 삭제
// (지금 repo에 userBookIds 조회가 없으니 간단히 하나 추가하는 걸 추천)
// 아래는 "추가할 repo 메서드" 참고
List<Long> userBookIds = userBooksRepository.findUserBookIdsByUserIdAndStatus(userId, status);
if (!userBookIds.isEmpty()) {
readingLogsRepository.deleteByUserIdAndUserBookIds(userId, userBookIds);
List<Long> bookIds = userBooksRepository.findBookIdsByUserIdAndStatus(userId, status);
if (!bookIds.isEmpty()) {
bookshelfItemsRepository.deleteByUserIdAndBookIds(userId, bookIds);
}
}
return userBooksRepository.deleteByUserIdAndStatus(userId, status);
if (status != null) {
List<Long> userBookIds = userBooksRepository.findUserBookIdsByUserIdAndStatus(userId, status);
if (!userBookIds.isEmpty()) {
readingLogsRepository.deleteByUserIdAndUserBookIds(userId, userBookIds);
List<Long> bookIds = userBooksRepository.findBookIdsByUserIdAndStatus(userId, status);
if (!bookIds.isEmpty()) {
bookshelfItemsRepository.deleteByUserIdAndBookIds(userId, bookIds);
}
}
return userBooksRepository.deleteByUserIdAndStatus(userId, status);
🤖 Prompt for AI Agents
In
`@booklog/src/main/java/com/example/booklog/domain/library/shelves/service/UserBooksService.java`
around lines 214 - 228, The inline comment in UserBooksService that suggests
adding a repository method is stale and misleading because
userBooksRepository.findUserBookIdsByUserIdAndStatus already exists; remove or
update that comment (the lines above the call to
findUserBookIdsByUserIdAndStatus) so the block that deletes
readingLogsRepository.deleteByUserIdAndUserBookIds,
bookshelfItemsRepository.deleteByUserIdAndBookIds, and the final
userBooksRepository.deleteByUserIdAndStatus call is accurate and clean, keeping
the surrounding logic intact.

@icarus0616 icarus0616 merged commit 7f0f275 into dev Feb 5, 2026
1 check passed
@coderabbitai coderabbitai Bot mentioned this pull request Feb 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant