Skip to content

[FEAT] 다른 유저 공개 서재 전체(전체보기 페이지) API 구현, 다른 유저 공개 서재들 불러오기 API 구현, 다른 유저 프로필 조회 및 팔로잉 여부 조회 API 철회#121

Closed
icarus0616 wants to merge 5 commits into
devfrom
feat/otherUserProfile/1
Closed

[FEAT] 다른 유저 공개 서재 전체(전체보기 페이지) API 구현, 다른 유저 공개 서재들 불러오기 API 구현, 다른 유저 프로필 조회 및 팔로잉 여부 조회 API 철회#121
icarus0616 wants to merge 5 commits into
devfrom
feat/otherUserProfile/1

Conversation

@icarus0616
Copy link
Copy Markdown
Collaborator

@icarus0616 icarus0616 commented Feb 4, 2026

#120
#119
#118

Summary by CodeRabbit

  • New Features
    • View other users' public profiles with activity counts and follow/book visibility flags.
    • Browse other users' public shelves with top-3 recent book previews per shelf.
    • Fetch full shelf books and sort by Latest, Oldest, Title, or Author (author sorting normalized).
    • Public-shelf access checks and total book counts per shelf supported.
  • API
    • New public endpoints for user profiles and public shelves; standardized success responses.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 4, 2026

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

Adds public-facing user profile and public-shelf browsing: repository queries, DTOs, services, and controllers to list public shelves (with top-3 previews) and to fetch full shelf books with multiple sort options; also adds a native profile summary query and ApiResponse helpers for BaseSuccessCode.

Changes

Cohort / File(s) Summary
Shelf item & shelf repos
booklog/src/main/java/com/example/booklog/domain/library/shelves/repository/BookshelfItemsRepository.java, booklog/src/main/java/com/example/booklog/domain/library/shelves/repository/BookshelvesRepository.java
Replaced recent-items query with findTop3ByShelf_IdOrderByAddedAtDesc, removed deleteByShelf_Id, added countByShelf_Id, findByShelf_IdOrderByAddedAtAsc, findByShelf_IdOrderByBook_TitleAsc. Added public-shelf queries findByUser_IdAndIsPublicTrueOrderByIdAsc and existsByIdAndUser_IdAndIsPublicTrue.
Public shelves controller
booklog/src/main/java/com/example/booklog/domain/users/controller/UserPublicShelvesController.java
New controller exposing endpoints to list a user's public shelves and to list books in a public shelf with sort options (LATEST/OLDEST/TITLE/AUTHOR).
User profile controller
booklog/src/main/java/com/example/booklog/domain/users/controller/UsersProfileController.java
New controller endpoint to retrieve another user's profile summary (authenticated), delegating to UserProfileService.
Public shelves service
booklog/src/main/java/com/example/booklog/domain/users/service/UserPublicShelvesService.java
New service providing listPublicShelves and listPublicShelfBooks; builds top-3 previews, validates public access, supports sorting incl. in-memory AUTHOR multi-criteria sort and mapping helpers.
User profile service & query
booklog/src/main/java/com/example/booklog/domain/users/service/UserProfileService.java, booklog/src/main/java/com/example/booklog/domain/users/repository/UserProfileQueryRepository.java, booklog/src/main/java/com/example/booklog/domain/users/repository/projection/UserProfileSummaryProjection.java
Added native SQL projection repository to fetch comprehensive profile summary relative to requester; service maps projection to DTO and throws USER_NOT_FOUND when absent.
DTOs
booklog/src/main/java/com/example/booklog/domain/users/dto/UserPublicShelfListResponse.java, booklog/src/main/java/com/example/booklog/domain/users/dto/UserProfileResponse.java
Added DTO records for public shelf list/books and user profile response.
API response & error codes
booklog/src/main/java/com/example/booklog/global/common/apiPayload/ApiResponse.java, booklog/src/main/java/com/example/booklog/global/common/apiPayload/code/status/ErrorStatus.java
Added ApiResponse.onSuccess overloads accepting BaseSuccessCode. Added ErrorStatus.SHELF_NOT_FOUND_OR_PRIVATE enum constant.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant PublicShelvesCtrl as UserPublicShelvesController
    participant PublicShelvesSvc as UserPublicShelvesService
    participant ShelvesRepo as BookshelvesRepository
    participant ItemsRepo as BookshelfItemsRepository
    participant DB as Database

    Client->>PublicShelvesCtrl: GET /api/v1/users/{userId}/shelves
    PublicShelvesCtrl->>PublicShelvesSvc: listPublicShelves(userId)
    PublicShelvesSvc->>ShelvesRepo: findByUser_IdAndIsPublicTrueOrderByIdAsc(userId)
    ShelvesRepo->>DB: query public shelves
    DB-->>ShelvesRepo: shelves
    loop per shelf
        PublicShelvesSvc->>ItemsRepo: countByShelf_Id(shelfId)
        ItemsRepo->>DB: count query
        DB-->>ItemsRepo: count
        PublicShelvesSvc->>ItemsRepo: findTop3ByShelf_IdOrderByAddedAtDesc(shelfId)
        ItemsRepo->>DB: top-3 query
        DB-->>ItemsRepo: top-3 items
    end
    PublicShelvesSvc-->>PublicShelvesCtrl: UserPublicShelfListResponse
    PublicShelvesCtrl-->>Client: 200 OK
Loading
sequenceDiagram
    participant Client
    participant PublicShelvesCtrl as UserPublicShelvesController
    participant PublicShelvesSvc as UserPublicShelvesService
    participant ShelvesRepo as BookshelvesRepository
    participant ItemsRepo as BookshelfItemsRepository
    participant DB as Database

    Client->>PublicShelvesCtrl: GET /api/v1/users/{userId}/shelves/{shelfId}/books?sort=AUTHOR
    PublicShelvesCtrl->>PublicShelvesSvc: listPublicShelfBooks(userId,shelfId,sort)
    PublicShelvesSvc->>ShelvesRepo: existsByIdAndUser_IdAndIsPublicTrue(shelfId,userId)
    ShelvesRepo->>DB: existence check
    DB-->>ShelvesRepo: exists/false
    alt exists
        PublicShelvesSvc->>ItemsRepo: findByShelf_IdOrderByAddedAtDesc/Asc/Book_TitleAsc(...)
        ItemsRepo->>DB: fetch books
        DB-->>ItemsRepo: books
        alt sort == AUTHOR
            PublicShelvesSvc->>PublicShelvesSvc: in-memory sort by primary author then addedAt desc
        end
        PublicShelvesSvc-->>PublicShelvesCtrl: UserPublicShelfBooksResponse
    else not exists
        PublicShelvesSvc-->>PublicShelvesCtrl: throw SHELF_NOT_FOUND_OR_PRIVATE
    end
    PublicShelvesCtrl-->>Client: 200 OK / 404
Loading
sequenceDiagram
    participant Client
    participant ProfileCtrl as UsersProfileController
    participant ProfileSvc as UserProfileService
    participant ProfileQry as UserProfileQueryRepository
    participant DB as Database

    Client->>ProfileCtrl: GET /api/v1/users/{userId}/profile (auth)
    ProfileCtrl->>ProfileSvc: getProfile(meId,userId)
    ProfileSvc->>ProfileQry: findUserProfileSummary(meId,targetUserId)
    ProfileQry->>DB: execute native SQL
    DB-->>ProfileQry: projection (counts, flags)
    ProfileQry-->>ProfileSvc: Optional<projection>
    alt found
        ProfileSvc->>ProfileCtrl: UserProfileResponse
    else not found
        ProfileSvc-->>ProfileCtrl: throw USER_NOT_FOUND
    end
    ProfileCtrl-->>Client: 200 OK / 404
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • jaehyeon4406
  • ktg3891
  • millkk04

Poem

🐰 Hopped in code with nimble paws,

Public shelves now out of doors.
Profiles counted, books arranged,
Top-three peeks and sorts exchanged.
Read, hop, and share—the rabbit applause!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 48.94% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title comprehensively summarizes all major changes: APIs for viewing other users' public shelves (full list and individual shelf), and user profile viewing with follow status.

✏️ 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 feat/otherUserProfile/1

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/users/dto/UserProfileResponse.java`:
- Around line 3-7: The UserProfileResponse record currently exposes email (PII)
and must not be returned for other-user public profile views; remove the email
field from the public record UserProfileResponse and update any builders/mappers
that construct UserProfileResponse (e.g., UserMapper.toUserProfileResponse or
similar factory methods) to stop populating or expecting email; if you still
need to return email for the account owner, introduce a separate record/type
(e.g., SelfUserProfileResponse) or add an explicit method that returns a
self-only response including email and update controller/service endpoints so
public profile endpoints use the sanitized UserProfileResponse while
self-profile endpoints use the self-only type.

In
`@booklog/src/main/java/com/example/booklog/domain/users/service/UserPublicShelvesService.java`:
- Around line 66-71: The switch on the variable sort in UserPublicShelvesService
can throw an NPE if sort is null; before the switch expression, defensively set
sort to the default (LATEST) when null (e.g., assign sort =
Objects.requireNonNullElse(sort, SortType.LATEST) or an equivalent
null-coalescing assignment) so the switch always receives a non-null value and
then proceed to call the repository methods (findByShelf_IdOrderByAddedAtDesc,
findByShelf_IdOrderByAddedAtAsc, findByShelf_IdOrderByBook_TitleAsc) as
currently written.
🧹 Nitpick comments (4)
booklog/src/main/java/com/example/booklog/domain/users/dto/UserPublicShelfListResponse.java (1)

6-15: Use long for count fields to avoid narrowing.

Counts are derived from long in the repository/service; using int risks overflow and forces casts. Prefer long for totalCount and bookCount, and update the service mapping accordingly.

🔧 Proposed DTO fix
 public record UserPublicShelfListResponse(
-        int totalCount,
+        long totalCount,
         List<UserPublicShelfItem> items
 ) {
     /** 서재 카드 1개 */
     public record UserPublicShelfItem(
             Long shelfId,
             String name,
-            int bookCount,
+            long bookCount,
             List<ShelfBookPreview> topBooks
     ) {}
@@
     /** 특정 서재 도서 전체 목록 응답(페이징 없음) */
     public record UserPublicShelfBooksResponse(
-            int totalCount,
+            long totalCount,
             List<UserPublicShelfBookItem> items
     ) {}

Also applies to: 26-30

booklog/src/main/java/com/example/booklog/domain/users/service/UserPublicShelvesService.java (2)

31-41: Reduce N+1 queries when building shelf cards.

countByShelf_Id and findTop3... are executed per shelf, which can degrade performance for users with many shelves. Consider a batch query (e.g., fetch counts per shelf in one query and top3 via window/limit per shelf, or prefetch items then group in-memory) to reduce DB round-trips.


44-48: Avoid narrowing longint for book counts.

countByShelf_Id returns long but is cast to int, which can overflow. Align with long counts in the DTO.

🔧 Proposed fix
                     return new UserPublicShelfListResponse.UserPublicShelfItem(
                             shelf.getId(),
                             shelf.getName(),
-                            (int) bookCount,
+                            bookCount,
                             top3
                     );
booklog/src/main/java/com/example/booklog/domain/users/repository/UserProfileQueryRepository.java (1)

26-33: Consider performance if this pattern is reused in list queries.

The multiple correlated subqueries work fine for a single-user lookup since the WHERE u.user_id = :targetUserId limits results to one row. However, if a similar query pattern is adapted for fetching multiple users, consider refactoring to use JOINs with GROUP BY or aggregate in the application layer to avoid N+1-like overhead.

Comment on lines +3 to +7
public record UserProfileResponse(
Long userId,
String nickname,
String email,
String avatarUrl,
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 | 🟠 Major

Do not expose email in other-user profiles.

The PR targets public/other-user profile viewing; returning email is a PII leak and a compliance/privacy risk. Remove it from the public response or gate it to “self only.”

🔐 Proposed fix (public response)
 public record UserProfileResponse(
         Long userId,
         String nickname,
-        String email,
         String avatarUrl,
📝 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
public record UserProfileResponse(
Long userId,
String nickname,
String email,
String avatarUrl,
public record UserProfileResponse(
Long userId,
String nickname,
String avatarUrl,
🤖 Prompt for AI Agents
In
`@booklog/src/main/java/com/example/booklog/domain/users/dto/UserProfileResponse.java`
around lines 3 - 7, The UserProfileResponse record currently exposes email (PII)
and must not be returned for other-user public profile views; remove the email
field from the public record UserProfileResponse and update any builders/mappers
that construct UserProfileResponse (e.g., UserMapper.toUserProfileResponse or
similar factory methods) to stop populating or expecting email; if you still
need to return email for the account owner, introduce a separate record/type
(e.g., SelfUserProfileResponse) or add an explicit method that returns a
self-only response including email and update controller/service endpoints so
public profile endpoints use the sanitized UserProfileResponse while
self-profile endpoints use the self-only type.

Comment on lines +66 to +71
List<BookshelfItems> rows = switch (sort) {
case OLDEST -> bookshelfItemsRepository.findByShelf_IdOrderByAddedAtAsc(shelfId);
case TITLE -> bookshelfItemsRepository.findByShelf_IdOrderByBook_TitleAsc(shelfId);
case AUTHOR -> bookshelfItemsRepository.findByShelf_IdOrderByAddedAtDesc(shelfId); // 가져온 뒤 자바 정렬
default -> bookshelfItemsRepository.findByShelf_IdOrderByAddedAtDesc(shelfId); // LATEST
};
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

Guard against null sort to avoid NPE in switch.

If sort is null (e.g., service called outside controller), the switch will throw. Default to LATEST defensively.

🛡️ Proposed fix
-        List<BookshelfItems> rows = switch (sort) {
+        PublicShelfBookSort safeSort = (sort != null) ? sort : PublicShelfBookSort.LATEST;
+        List<BookshelfItems> rows = switch (safeSort) {
             case OLDEST -> bookshelfItemsRepository.findByShelf_IdOrderByAddedAtAsc(shelfId);
             case TITLE  -> bookshelfItemsRepository.findByShelf_IdOrderByBook_TitleAsc(shelfId);
             case AUTHOR -> bookshelfItemsRepository.findByShelf_IdOrderByAddedAtDesc(shelfId); // 가져온 뒤 자바 정렬
             default     -> bookshelfItemsRepository.findByShelf_IdOrderByAddedAtDesc(shelfId); // LATEST
         };
🤖 Prompt for AI Agents
In
`@booklog/src/main/java/com/example/booklog/domain/users/service/UserPublicShelvesService.java`
around lines 66 - 71, The switch on the variable sort in
UserPublicShelvesService can throw an NPE if sort is null; before the switch
expression, defensively set sort to the default (LATEST) when null (e.g., assign
sort = Objects.requireNonNullElse(sort, SortType.LATEST) or an equivalent
null-coalescing assignment) so the switch always receives a non-null value and
then proceed to call the repository methods (findByShelf_IdOrderByAddedAtDesc,
findByShelf_IdOrderByAddedAtAsc, findByShelf_IdOrderByBook_TitleAsc) as
currently written.

@icarus0616 icarus0616 closed this Feb 4, 2026
@icarus0616 icarus0616 changed the title [FEAT] 다른 유저 공개 서재 전체(전체보기 페이지) API 구현, 다른 유저 공개 서재들 불러오기 API 구현, 다른 유저 프로필 조회 및 팔로잉 여부 조회 API [FEAT] 다른 유저 공개 서재 전체(전체보기 페이지) API 구현, 다른 유저 공개 서재들 불러오기 API 구현, 다른 유저 프로필 조회 및 팔로잉 여부 조회 API 철회 Feb 4, 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