Skip to content

[feat] 피드 조회 시 [내 정보 or 특정 유저 정보] 조회하는 api 개발#119

Merged
seongjunnoh merged 14 commits intodevelopfrom
feat/#115-feed-get-info-of-user
Jul 31, 2025
Merged

[feat] 피드 조회 시 [내 정보 or 특정 유저 정보] 조회하는 api 개발#119
seongjunnoh merged 14 commits intodevelopfrom
feat/#115-feed-get-info-of-user

Conversation

@seongjunnoh
Copy link
Collaborator

@seongjunnoh seongjunnoh commented Jul 31, 2025

#️⃣ 연관된 이슈

closes #115

📝 작업 내용

피드 조회 화면의 상단 부분을 구성하는 [유저 정보, 유저의 팔로워 정보, 유저가 작성한 피드 개수] 등을 반환하는 api 를 개발하였습니다.
현재 유저의 피드 조회 api가 [내 피드 조회, 특정 유저의 피드 조회] 이렇게 2가지로 있어서, 해당 api도 2개로 분리하였습니다!!

  • api 주요 로직

    • 동일 service class 에서 메서드를 분리하여 api 2개를 구현하였습니다
    • 해당 유저를 팔로우하는 사람의 프로필 이미지 url을 조회해야하는 요구사항이 있는데, 이를 QueryDSL 을 사용해 구현하였습니다
    • 유저가 작성한 피드의 개수를 구하는 2개의 메서드는 모두 jpql + full scan 방식의 count(*) 를 활용하여 구현하였습니다
      • 이는 추후에 인덱스 등을 도입하여 성능 개선의 포인트가 될 수 있을 것 같습니다!!
  • 그 외 수정사항

    • UserJpaEntity 에서 imageUrl 필드를 제거하였습니다

📸 스크린샷

image image postman 을 통해 api 호출을 테스트 해봤습니다! (로컬 테스트용 데이터를 완벽한 시나리오로 구성하지 않아 캡쳐본에서는 response 끼리의 정합성이 잘 맞지는 않습니다. api 통합 테스트 코드를 통해 확인해주시면 될 것 같습니다!! ^^7)

💬 리뷰 요구사항

리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요

📌 PR 진행 시 이러한 점들을 참고해 주세요

* P1 : 꼭 반영해 주세요 (Request Changes) - 이슈가 발생하거나 취약점이 발견되는 케이스 등
* P2 : 반영을 적극적으로 고려해 주시면 좋을 것 같아요 (Comment)
* P3 : 이런 방법도 있을 것 같아요~ 등의 사소한 의견입니다 (Chore)

Summary by CodeRabbit

  • 신규 기능

    • 피드에서 사용자 정보를 조회할 수 있는 API 엔드포인트가 추가되었습니다. (내 정보 및 특정 사용자 정보)
    • 피드 사용자 정보에는 프로필 이미지, 닉네임, 별명, 별명 색상, 팔로워 수, 전체/공개 피드 수, 최근 팔로워 프로필 이미지(최대 5명)가 포함됩니다.
  • 버그 수정

    • 사용자 프로필 이미지 URL이 별명 정보에서 제공되도록 일관성 있게 변경되었습니다.
  • 테스트

    • 피드 사용자 정보 조회 API에 대한 통합 테스트가 추가되었습니다.
    • 기존 테스트 및 테스트 데이터에서 imageUrl 관련 코드가 제거되었습니다.
  • 리팩토링

    • 피드 및 팔로워 수 집계 관련 메서드 명칭 및 쿼리 개선, 관련 인터페이스 및 서비스 구조 개선이 이루어졌습니다.

@seongjunnoh seongjunnoh linked an issue Jul 31, 2025 that may be closed by this pull request
2 tasks
@coderabbitai
Copy link

coderabbitai bot commented Jul 31, 2025

Walkthrough

피드 상단 화면 정보를 제공하는 새로운 API가 추가되었습니다. 내 피드와 특정 유저의 피드 상단 정보를 조회할 수 있는 GET 엔드포인트가 구현되었으며, 관련 도메인 서비스, 포트, 어댑터, DTO 및 테스트 코드가 함께 도입·수정되었습니다. 기존의 유저 이미지 URL 필드는 별도의 alias 엔티티로 이동되었습니다.

Changes

Cohort / File(s) Change Summary
Feed 상단 정보 API 및 서비스 도입
src/main/java/konkuk/thip/feed/adapter/in/web/FeedQueryController.java, src/main/java/konkuk/thip/feed/application/service/FeedShowUserInfoService.java, src/main/java/konkuk/thip/feed/application/port/in/FeedShowUserInfoUseCase.java, src/main/java/konkuk/thip/feed/adapter/in/web/response/FeedShowUserInfoResponse.java
내 피드 및 특정 유저 피드 상단 정보 조회를 위한 API, 서비스, 포트, DTO 신설 및 연동
Feed 수 조회 관련 포트/어댑터/리포지터리 변경
src/main/java/konkuk/thip/feed/application/port/out/FeedQueryPort.java, src/main/java/konkuk/thip/feed/adapter/out/persistence/FeedQueryPersistenceAdapter.java, src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/FeedJpaRepository.java
전체/공개 피드 수 구분을 위한 메서드 추가 및 기존 메서드 명 변경
팔로워 프로필 이미지 조회 기능 추가
src/main/java/konkuk/thip/user/application/port/out/FollowingQueryPort.java, src/main/java/konkuk/thip/user/adapter/out/persistence/FollowingQueryPersistenceAdapter.java, src/main/java/konkuk/thip/user/adapter/out/persistence/repository/following/FollowingQueryRepository.java, src/main/java/konkuk/thip/user/adapter/out/persistence/repository/following/FollowingQueryRepositoryImpl.java
최신 팔로워 프로필 이미지 URL을 조회하는 기능 포트, 어댑터, 리포지터리, 구현체에 추가
UserJpaEntity 이미지 URL 필드 제거 및 관련 코드 정리
src/main/java/konkuk/thip/user/adapter/out/jpa/UserJpaEntity.java, src/main/java/konkuk/thip/user/adapter/out/mapper/UserMapper.java
UserJpaEntity의 imageUrl 필드 및 매핑 제거, 관련 setter 삭제
피드/레코드 쿼리에서 프로필 이미지 경로 변경
src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/FeedQueryRepositoryImpl.java, src/main/java/konkuk/thip/record/adapter/out/persistence/repository/RecordQueryRepositoryImpl.java
유저 이미지 URL 참조를 aliasForUserJpaEntity로 변경
피드 상단 정보 API 통합 테스트 추가
src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowUserInfoApiTest.java
피드 상단 정보 API의 정상 동작 및 팔로워 5명 제한 등 다양한 시나리오 테스트
UserJpaEntity imageUrl 필드 제거에 따른 테스트 코드 정리
src/test/java/konkuk/thip/book/adapter/in/web/*, src/test/java/konkuk/thip/common/util/TestEntityFactory.java, src/test/java/konkuk/thip/record/adapter/in/web/RecordSearchApiTest.java, src/test/java/konkuk/thip/room/adapter/in/web/*, src/test/java/konkuk/thip/user/adapter/in/web/UserFollowApiTest.java, src/test/java/konkuk/thip/user/adapter/in/web/UserVerifyNicknameControllerTest.java, src/test/java/konkuk/thip/vote/adapter/in/web/VoteCreateControllerTest.java
UserJpaEntity의 imageUrl 관련 테스트 데이터 생성 코드 일괄 삭제 및 정리

Sequence Diagram(s)

sequenceDiagram
actor Client
participant FeedQueryController
participant FeedShowUserInfoService
participant UserCommandPort
participant FollowingQueryPort
participant FeedQueryPort

Client->>FeedQueryController: GET /feeds/mine/info (userId)
FeedQueryController->>FeedShowUserInfoService: showMyInfoInFeeds(userId)
FeedShowUserInfoService->>UserCommandPort: findUserById(userId)
FeedShowUserInfoService->>FollowingQueryPort: getLatestFollowerProfileImageUrlsByUserId(userId, 5)
FeedShowUserInfoService->>FeedQueryPort: countAllFeedsByUserId(userId)
FeedShowUserInfoService-->>FeedQueryController: FeedShowUserInfoResponse
FeedQueryController-->>Client: BaseResponse<FeedShowUserInfoResponse>

Client->>FeedQueryController: GET /feeds/users/{userId}/info
FeedQueryController->>FeedShowUserInfoService: showAnotherUserInfoInFeeds(userId)
FeedShowUserInfoService->>UserCommandPort: findUserById(userId)
FeedShowUserInfoService->>FollowingQueryPort: getLatestFollowerProfileImageUrlsByUserId(userId, 5)
FeedShowUserInfoService->>FeedQueryPort: countPublicFeedsByUserId(userId)
FeedShowUserInfoService-->>FeedQueryController: FeedShowUserInfoResponse
FeedQueryController-->>Client: BaseResponse<FeedShowUserInfoResponse>
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Assessment against linked issues

Objective Addressed Explanation
내 피드 상단 화면 조회 api 개발 (#115)
특정 유저 피드 상단 화면 조회 api 개발 (#115)

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
없음 해당 PR의 모든 주요 변경 사항은 연결된 이슈의 요구사항(피드 상단 정보 API 개발, 관련 도메인/인프라/테스트 반영)에 부합합니다. 기능과 직접적으로 관련 없는 의미 있는 코드 변경은 발견되지 않았습니다.

Suggested labels

🛠️ feat, 👻 성준

Suggested reviewers

  • hd0rable
  • buzz0331

Poem

🐇
피드의 상단, 새 API로 반짝
내 정보, 남 정보, 모두 한눈에 착!
팔로워 다섯, 프로필도 쏙
alias로 이미지, 깔끔하게 쏙
테스트도 빵빵, 리뷰는 금방!
봄바람 타고, 코드도 산뜻하게 Hop!

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 92d2c23 and 333e001.

📒 Files selected for processing (2)
  • src/main/java/konkuk/thip/feed/application/mapper/FeedQueryMapper.java (2 hunks)
  • src/main/java/konkuk/thip/feed/application/service/FeedShowUserInfoService.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/konkuk/thip/feed/application/service/FeedShowUserInfoService.java
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#113
File: src/main/java/konkuk/thip/recentSearch/adapter/out/persistence/RecentSearchCommandPersistenceAdapter.java:38-44
Timestamp: 2025-07-30T14:05:04.945Z
Learning: seongjunnoh는 코드 최적화 제안에 대해 구체적인 기술적 근거와 효율성 차이를 이해하고 싶어하며, 성능 개선 방식에 대한 상세한 설명을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#93
File: src/main/java/konkuk/thip/room/adapter/out/persistence/RoomQueryPersistenceAdapter.java:49-114
Timestamp: 2025-07-28T16:44:31.224Z
Learning: seongjunnoh는 코드 중복 문제에 대한 리팩토링 제안을 적극적으로 수용하고 함수형 인터페이스를 활용한 해결책을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#112
File: src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/FeedQueryRepositoryImpl.java:272-272
Timestamp: 2025-07-30T10:44:34.115Z
Learning: seongjunnoh는 피드 커서 페이지네이션에서 LocalDateTime 단일 커서 방식을 선호하며, 복합 키 기반 커서보다 구현 단순성과 성능을 우선시한다.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (2)
src/main/java/konkuk/thip/feed/application/mapper/FeedQueryMapper.java (2)

6-6: LGTM! 새로운 기능을 위한 import 추가

피드 사용자 정보 조회 기능을 위한 필요한 import들이 적절히 추가되었습니다.

Also applies to: 8-8, 13-13


41-48: 맵핑 안전 보장됨
User.alias는 enum 타입으로, JPA @JoinColumn(nullable = false) 제약을 통해 항상 값이 존재하므로 null이 될 수 없습니다. 따라서 MapStruct가 생성하는 feedOwner.getAlias().get…() 호출에서 NPE 발생 우려가 없습니다.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/#115-feed-get-info-of-user

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@seongjunnoh seongjunnoh requested review from buzz0331 and hd0rable and removed request for hd0rable July 31, 2025 09:33
Copy link

@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: 0

♻️ Duplicate comments (1)
src/main/java/konkuk/thip/feed/adapter/out/persistence/FeedQueryPersistenceAdapter.java (1)

77-80: 동일한 형변환 이슈 적용

countAllFeedsByUserId 메서드와 동일한 long→int 형변환 이슈가 적용됩니다. 일관성을 위해 같은 해결책을 적용하는 것이 좋겠습니다.

🧹 Nitpick comments (5)
src/main/java/konkuk/thip/user/adapter/out/persistence/repository/following/FollowingQueryRepository.java (1)

16-16: 메서드명 단순화 및 파라미터 검증 고려

메서드명이 매우 길어 가독성이 떨어집니다. findLatestFollowerImageUrls(Long userId, int size) 정도로 단순화하는 것을 고려해보세요.

또한 size 파라미터에 대한 범위 제한(예: 최대 10개)을 문서화하거나 구현체에서 검증하는 것이 좋겠습니다.

- List<String> findTopFollowerProfileImageUrlsByUserIdOrderByCreatedAtDesc(Long userId, int size);
+ List<String> findLatestFollowerImageUrls(Long userId, int size);
src/main/java/konkuk/thip/feed/application/port/in/FeedShowUserInfoUseCase.java (1)

5-10: 메서드명 개선 및 JavaDoc 추가 권장

  1. 메서드명에서 "Info" 중복을 제거하는 것을 고려해보세요:

    • showMyInfoInFeedsshowMyFeeds
    • showAnotherUserInfoInFeedsshowUserFeeds
  2. 각 메서드의 구체적인 동작과 예외 상황에 대한 JavaDoc 추가를 권장합니다.

public interface FeedShowUserInfoUseCase {
+   /**
+    * 현재 사용자의 피드 정보를 조회합니다 (전체 피드 포함)
+    * @param userId 조회할 사용자 ID
+    * @return 사용자 피드 정보
+    */
-   FeedShowUserInfoResponse showMyInfoInFeeds(Long userId);
+   FeedShowUserInfoResponse showMyFeeds(Long userId);

+   /**
+    * 다른 사용자의 피드 정보를 조회합니다 (공개 피드만 포함)
+    * @param anotherUserId 조회할 다른 사용자 ID
+    * @return 사용자 피드 정보
+    */
-   FeedShowUserInfoResponse showAnotherUserInfoInFeeds(Long anotherUserId);
+   FeedShowUserInfoResponse showUserFeeds(Long anotherUserId);
}
src/main/java/konkuk/thip/user/adapter/out/persistence/repository/following/FollowingQueryRepositoryImpl.java (1)

93-109: QueryDSL 구현이 올바르게 작성되었습니다

최신 팔로워들의 프로필 이미지 URL을 조회하는 로직이 정확하게 구현되었습니다. 조인 관계와 필터링 조건이 적절하며, 생성일 기준 내림차순 정렬로 최신 팔로워를 가져오도록 했습니다.

성능 최적화를 위해 following.followingUserJpaEntity.userIdfollowing.createdAt에 대한 인덱스 추가를 고려해볼 수 있습니다.

src/main/java/konkuk/thip/feed/adapter/in/web/FeedQueryController.java (2)

53-56: API 문서화를 위한 @operation 애노테이션 추가 권장

다른 엔드포인트들과 일관성을 위해 @Operation 애노테이션을 추가하는 것이 좋겠습니다.

+@Operation(
+        summary = "내 피드 상단 정보 조회",
+        description = "인증된 사용자의 피드 상단에 표시될 정보를 조회합니다."
+)
 @GetMapping("/feeds/mine/info")
 public BaseResponse<FeedShowUserInfoResponse> showMyInfoInFeeds(@Parameter(hidden = true) @UserId final Long userId) {

58-63: API 문서화를 위한 @operation 애노테이션 추가 권장

위 엔드포인트와 마찬가지로 @Operation 애노테이션을 추가하여 API 문서화를 완성하는 것이 좋겠습니다.

+@Operation(
+        summary = "특정 유저의 피드 상단 정보 조회", 
+        description = "지정된 사용자의 피드 상단에 표시될 정보를 조회합니다."
+)
 @GetMapping("/feeds/users/{userId}/info")
 public BaseResponse<FeedShowUserInfoResponse> showAnotherUserInfoInFeeds(
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 42121df and c778d0b.

📒 Files selected for processing (33)
  • src/main/java/konkuk/thip/feed/adapter/in/web/FeedQueryController.java (3 hunks)
  • src/main/java/konkuk/thip/feed/adapter/in/web/response/FeedShowUserInfoResponse.java (1 hunks)
  • src/main/java/konkuk/thip/feed/adapter/out/persistence/FeedQueryPersistenceAdapter.java (1 hunks)
  • src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/FeedJpaRepository.java (1 hunks)
  • src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/FeedQueryRepositoryImpl.java (1 hunks)
  • src/main/java/konkuk/thip/feed/application/port/in/FeedShowUserInfoUseCase.java (1 hunks)
  • src/main/java/konkuk/thip/feed/application/port/out/FeedQueryPort.java (1 hunks)
  • src/main/java/konkuk/thip/feed/application/service/FeedShowUserInfoService.java (1 hunks)
  • src/main/java/konkuk/thip/record/adapter/out/persistence/repository/RecordQueryRepositoryImpl.java (1 hunks)
  • src/main/java/konkuk/thip/user/adapter/out/jpa/UserJpaEntity.java (1 hunks)
  • src/main/java/konkuk/thip/user/adapter/out/mapper/UserMapper.java (0 hunks)
  • src/main/java/konkuk/thip/user/adapter/out/persistence/FollowingQueryPersistenceAdapter.java (1 hunks)
  • src/main/java/konkuk/thip/user/adapter/out/persistence/repository/following/FollowingQueryRepository.java (1 hunks)
  • src/main/java/konkuk/thip/user/adapter/out/persistence/repository/following/FollowingQueryRepositoryImpl.java (1 hunks)
  • src/main/java/konkuk/thip/user/application/port/out/FollowingQueryPort.java (1 hunks)
  • src/test/java/konkuk/thip/book/adapter/in/web/BookChangeSavedControllerTest.java (0 hunks)
  • src/test/java/konkuk/thip/book/adapter/in/web/BookDetailSearchControllerTest.java (0 hunks)
  • src/test/java/konkuk/thip/book/adapter/in/web/BookMostSearchedBooksControllerTest.java (0 hunks)
  • src/test/java/konkuk/thip/book/adapter/in/web/BookQueryControllerTest.java (0 hunks)
  • src/test/java/konkuk/thip/common/util/TestEntityFactory.java (1 hunks)
  • src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowUserInfoApiTest.java (1 hunks)
  • src/test/java/konkuk/thip/record/adapter/in/web/RecordSearchApiTest.java (0 hunks)
  • src/test/java/konkuk/thip/room/adapter/in/web/RoomCloseJoinApiTest.java (0 hunks)
  • src/test/java/konkuk/thip/room/adapter/in/web/RoomCreateAPITest.java (0 hunks)
  • src/test/java/konkuk/thip/room/adapter/in/web/RoomGetMemberListApiTest.java (0 hunks)
  • src/test/java/konkuk/thip/room/adapter/in/web/RoomJoinApiTest.java (0 hunks)
  • src/test/java/konkuk/thip/room/adapter/in/web/RoomPlayingDetailViewApiTest.java (0 hunks)
  • src/test/java/konkuk/thip/room/adapter/in/web/RoomRecruitingDetailViewApiTest.java (0 hunks)
  • src/test/java/konkuk/thip/room/adapter/in/web/RoomSearchApiTest.java (0 hunks)
  • src/test/java/konkuk/thip/room/adapter/in/web/RoomVerifyPasswordAPITest.java (1 hunks)
  • src/test/java/konkuk/thip/user/adapter/in/web/UserFollowApiTest.java (0 hunks)
  • src/test/java/konkuk/thip/user/adapter/in/web/UserVerifyNicknameControllerTest.java (0 hunks)
  • src/test/java/konkuk/thip/vote/adapter/in/web/VoteCreateControllerTest.java (0 hunks)
💤 Files with no reviewable changes (16)
  • src/test/java/konkuk/thip/book/adapter/in/web/BookMostSearchedBooksControllerTest.java
  • src/test/java/konkuk/thip/book/adapter/in/web/BookDetailSearchControllerTest.java
  • src/test/java/konkuk/thip/room/adapter/in/web/RoomCreateAPITest.java
  • src/test/java/konkuk/thip/vote/adapter/in/web/VoteCreateControllerTest.java
  • src/main/java/konkuk/thip/user/adapter/out/mapper/UserMapper.java
  • src/test/java/konkuk/thip/room/adapter/in/web/RoomGetMemberListApiTest.java
  • src/test/java/konkuk/thip/book/adapter/in/web/BookQueryControllerTest.java
  • src/test/java/konkuk/thip/room/adapter/in/web/RoomSearchApiTest.java
  • src/test/java/konkuk/thip/room/adapter/in/web/RoomJoinApiTest.java
  • src/test/java/konkuk/thip/user/adapter/in/web/UserFollowApiTest.java
  • src/test/java/konkuk/thip/user/adapter/in/web/UserVerifyNicknameControllerTest.java
  • src/test/java/konkuk/thip/record/adapter/in/web/RecordSearchApiTest.java
  • src/test/java/konkuk/thip/book/adapter/in/web/BookChangeSavedControllerTest.java
  • src/test/java/konkuk/thip/room/adapter/in/web/RoomCloseJoinApiTest.java
  • src/test/java/konkuk/thip/room/adapter/in/web/RoomRecruitingDetailViewApiTest.java
  • src/test/java/konkuk/thip/room/adapter/in/web/RoomPlayingDetailViewApiTest.java
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#113
File: src/main/java/konkuk/thip/recentSearch/adapter/out/persistence/RecentSearchCommandPersistenceAdapter.java:38-44
Timestamp: 2025-07-30T14:05:04.945Z
Learning: seongjunnoh는 코드 최적화 제안에 대해 구체적인 기술적 근거와 효율성 차이를 이해하고 싶어하며, 성능 개선 방식에 대한 상세한 설명을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#93
File: src/main/java/konkuk/thip/room/adapter/out/persistence/RoomQueryPersistenceAdapter.java:49-114
Timestamp: 2025-07-28T16:44:31.224Z
Learning: seongjunnoh는 코드 중복 문제에 대한 리팩토링 제안을 적극적으로 수용하고 함수형 인터페이스를 활용한 해결책을 선호한다.
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#112
File: src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/FeedQueryRepositoryImpl.java:272-272
Timestamp: 2025-07-30T10:44:34.115Z
Learning: seongjunnoh는 피드 커서 페이지네이션에서 LocalDateTime 단일 커서 방식을 선호하며, 복합 키 기반 커서보다 구현 단순성과 성능을 우선시한다.
src/main/java/konkuk/thip/user/application/port/out/FollowingQueryPort.java (1)

Learnt from: seongjunnoh
PR: #43
File: src/main/java/konkuk/thip/book/application/port/out/BookCommandPort.java:0-0
Timestamp: 2025-07-03T03:05:05.031Z
Learning: THIP 프로젝트에서는 CQRS Port 분리 시 다음 컨벤션을 따름: CommandPort에는 findByXXX를 통해 도메인 엔티티를 찾아오는 메서드를 추가하고, QueryPort에는 조회 API의 response에 해당하는 데이터들을 DB로부터 조회하는 메서드를 추가함.

src/main/java/konkuk/thip/feed/application/port/out/FeedQueryPort.java (1)

Learnt from: seongjunnoh
PR: #43
File: src/main/java/konkuk/thip/book/application/port/out/BookCommandPort.java:0-0
Timestamp: 2025-07-03T03:05:05.031Z
Learning: THIP 프로젝트에서는 CQRS Port 분리 시 다음 컨벤션을 따름: CommandPort에는 findByXXX를 통해 도메인 엔티티를 찾아오는 메서드를 추가하고, QueryPort에는 조회 API의 response에 해당하는 데이터들을 DB로부터 조회하는 메서드를 추가함.

🧬 Code Graph Analysis (2)
src/test/java/konkuk/thip/room/adapter/in/web/RoomVerifyPasswordAPITest.java (1)
src/test/java/konkuk/thip/common/util/TestEntityFactory.java (1)
  • TestEntityFactory (29-291)
src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowUserInfoApiTest.java (1)
src/test/java/konkuk/thip/common/util/TestEntityFactory.java (1)
  • TestEntityFactory (29-291)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build
🔇 Additional comments (23)
src/test/java/konkuk/thip/common/util/TestEntityFactory.java (1)

189-189: LGTM - 코드 포맷팅 개선

파라미터 목록의 공백 추가로 코드 가독성이 향상되었습니다.

src/main/java/konkuk/thip/user/adapter/out/jpa/UserJpaEntity.java (1)

46-46: LGTM – 도메인 모델 일관성 개선

  • imageUrl 필드 제거 및 updateFrom 메서드 수정이 적절합니다.
  • 사용자 프로필 이미지를 alias 엔티티(aliasForUserJpaEntity)로 관리하는 설계가 더 일관성 있습니다.
  • 추가 확인 필요: 코드베이스 전반에서 여전히 user.imageUrl 직접 참조가 남아 있지 않은지 수동으로 점검해주세요.
src/test/java/konkuk/thip/room/adapter/in/web/RoomVerifyPasswordAPITest.java (1)

75-75: LGTM - 테스트 코드 개선

수동 엔티티 생성을 팩토리 메서드로 대체하여 테스트 코드의 일관성과 유지보수성이 향상되었습니다.

src/main/java/konkuk/thip/user/application/port/out/FollowingQueryPort.java (2)

6-7: LGTM - 필요한 임포트 추가

새로운 메서드의 반환 타입을 위한 List 임포트가 적절히 추가되었습니다.


12-12: LGTM - 피드 기능을 위한 적절한 메서드 추가

최신 팔로워의 프로필 이미지 URL을 조회하는 메서드가 적절히 추가되었습니다. size 파라미터를 통한 조회 수 제한으로 성능도 고려되었으며, CQRS 패턴에 맞게 QueryPort에 배치되었습니다.

src/main/java/konkuk/thip/record/adapter/out/persistence/repository/RecordQueryRepositoryImpl.java (1)

206-206: 변경사항 확인 완료 – 추가 수정 불필요

  • src/main/java/ 하위의 모든 Repository 구현체에서 user.imageUrl 직접 참조가 없음을 확인했습니다.

해당 변경으로 인한 추가 조치는 필요하지 않습니다.

src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/FeedQueryRepositoryImpl.java (1)

224-224: aliasForUserJpaEntity는 nullable=false로 NPE 발생 우려 없음

UserJpaEntity 에서

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_alias_id", nullable = false)
private AliasJpaEntity aliasForUserJpaEntity;

로 정의되어 있어 DB와 JPA 매핑 차원에서 null을 허용하지 않고, 테스트 코드에서도 항상 alias를 주입하고 있습니다.
따라서 getAliasForUserJpaEntity().getImageUrl() 호출 시 NPE 걱정 없이 그대로 사용하셔도 됩니다.

src/main/java/konkuk/thip/user/adapter/out/persistence/FollowingQueryPersistenceAdapter.java (1)

44-47: 구현이 적절합니다

단순한 위임 구조로 깔끔하게 구현되었습니다. 레이어 간 책임 분리가 잘 되어 있습니다.

src/main/java/konkuk/thip/feed/application/port/out/FeedQueryPort.java (1)

19-21: 메서드명 변경 관련 코드베이스 일관성 확인 완료

countFeedsByUserId 호출은 더 이상 존재하지 않으며,
모든 참조가 countAllFeedsByUserIdcountPublicFeedsByUserId로 올바르게 업데이트된 것을 확인했습니다.

추가 조치가 필요 없습니다.

src/main/java/konkuk/thip/feed/adapter/out/persistence/FeedQueryPersistenceAdapter.java (1)

72-75: long에서 int로의 형변환 시 오버플로우 위험성 검토 필요

개발자 코멘트에서도 확인되듯이 형변환에 대한 우려가 있습니다. Integer.MAX_VALUE(약 21억)를 초과하는 피드 수가 있을 경우 데이터 손실이 발생할 수 있습니다.

다음 중 하나의 방법을 고려해보세요:

  1. 포트 인터페이스와 서비스 레이어에서 long 타입 사용
  2. 오버플로우 체크 로직 추가
 public int countAllFeedsByUserId(Long userId) {
-    // int 로 강제 형변환 해도 괜찮죠??
-    return (int) feedJpaRepository.countAllFeedsByUserId(userId, StatusType.ACTIVE);
+    long count = feedJpaRepository.countAllFeedsByUserId(userId, StatusType.ACTIVE);
+    if (count > Integer.MAX_VALUE) {
+        throw new IllegalStateException("Feed count exceeds integer limit: " + count);
+    }
+    return (int) count;
 }
src/main/java/konkuk/thip/feed/adapter/in/web/response/FeedShowUserInfoResponse.java (1)

7-16: 응답 구조가 적절하게 설계되었습니다

피드 상단 화면에 필요한 모든 정보를 포함하고 있으며, @Builder 애노테이션을 통해 객체 생성이 용이하도록 구성되어 있습니다. 필드명도 명확하고 타입도 적절합니다.

src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/FeedJpaRepository.java (2)

11-12: 메서드명 변경과 쿼리 구현이 적절합니다

기존 countFeedsByUserId에서 countAllFeedsByUserId로 변경하여 의도를 명확히 했고, JPQL 쿼리도 올바르게 구현되었습니다.


14-15: 공개 피드만 조회하는 로직이 올바르게 구현되었습니다

isPublic = TRUE 조건을 추가하여 공개 피드만 카운트하도록 구현했습니다. 다른 사용자의 피드 정보 조회 시 개인정보 보호를 위한 중요한 구분입니다.

src/main/java/konkuk/thip/feed/application/service/FeedShowUserInfoService.java (5)

3-14: 적절한 import 구성

필요한 의존성들이 명확하게 import되어 있고, 불필요한 import가 없어 깔끔합니다.


19-22: 적절한 상수 정의와 의존성 구성

팔로워 프로필 이미지 표시 제한을 상수로 정의한 것이 좋고, 필요한 포트들이 명확하게 구성되어 있습니다.


24-37: 내 피드 정보 조회 로직이 적절함

읽기 전용 트랜잭션 설정이 적절하고, 사용자 조회 → 팔로워 프로필 이미지 조회 → 전체 피드 수 조회의 순서가 논리적입니다. 내 피드에서는 모든 피드(공개/비공개 포함)를 카운트하는 것이 올바른 비즈니스 로직입니다.


39-52: 다른 유저 피드 정보 조회 로직이 적절함

공개 피드만 카운트하는 것이 프라이버시 보호 측면에서 올바른 비즈니스 로직입니다. 전체적인 구조도 내 피드 조회와 일관성 있게 구성되어 있습니다.


54-64: 응답 생성 헬퍼 메서드가 잘 구성됨

private 메서드로 적절히 캡슐화되어 있고, alias 엔티티에서 이미지 URL을 가져오는 것이 도메인 모델 변경에 맞게 올바르게 구현되어 있습니다. 모든 필요한 필드가 명확하게 매핑되어 있습니다.

src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowUserInfoApiTest.java (5)

32-53: 테스트 설정과 정리가 적절함

통합 테스트 어노테이션 구성이 올바르고, tearDown에서 외래키 제약조건을 고려한 순서로 데이터를 정리하고 있습니다. JdbcTemplate 주입으로 타임스탬프 조작이 가능한 점도 좋습니다.


55-98: 포괄적인 내 피드 정보 조회 테스트

테스트 시나리오가 잘 구성되어 있고, 특히 팔로워 프로필 이미지의 최신순 정렬 검증이 중요한 비즈니스 로직을 적절히 테스트하고 있습니다. 전체 피드 수(공개+비공개) 카운트 검증도 올바릅니다.


100-162: 팔로워 제한 로직에 대한 경계값 테스트

7명의 팔로워 중 최신 5명만 반환되는 제한 로직을 정확히 검증하고 있습니다. 타임스탬프 조작을 통한 순서 제어도 적절하게 구현되어 있습니다.


164-207: 다른 유저 피드 정보 조회의 프라이버시 로직 검증

다른 유저의 피드에서 공개 피드만 카운트되는 중요한 프라이버시 로직을 정확히 검증하고 있습니다. 비공개글 2개가 제외되어 총 2개만 카운트되는 것을 확인하는 테스트가 적절합니다.


209-270: 다른 유저 컨텍스트에서의 팔로워 제한 검증

다른 유저의 피드에서도 팔로워 프로필 이미지 제한(최대 5개) 로직이 동일하게 작동하는지 검증하는 완성도 높은 테스트입니다. 전체적으로 테스트 커버리지가 우수합니다.

Copy link
Contributor

@buzz0331 buzz0331 left a comment

Choose a reason for hiding this comment

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

수고하셨습니다~ 리뷰 겸 간단한 제안 하나 남겼는데 확인 부탁드려요!

Comment on lines 54 to 64
private FeedShowUserInfoResponse buildResponse(User feedOwner, int totalFeedCount, List<String> latestFollowerProfileImageUrls) {
return FeedShowUserInfoResponse.builder()
.profileImageUrl(feedOwner.getAlias().getImageUrl())
.nickname(feedOwner.getNickname())
.aliasName(feedOwner.getAlias().getValue())
.aliasColor(feedOwner.getAlias().getColor())
.followerCount(feedOwner.getFollowerCount())
.totalFeedCount(totalFeedCount)
.latestFollowerProfileImageUrls(latestFollowerProfileImageUrls)
.build();
}
Copy link
Contributor

Choose a reason for hiding this comment

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

p2: 현재 저희가 MapStruct를 queryDto -> responseDto를 매핑하는 용도로만 사용 중인 것 같은데 단순히 dto를 파싱하는 부분도 MapStruct에게 책임을 위임하는 것이 어떨까요? 처음에 mapper 이름을 FollowDtoMapper 이런식으로 지은 것도 이러한 책임을 의도했기 때문인데, 이러한 의도를 전달하지 못했었네요!

이렇게 하면 서비스 클래스에서는 비즈니스 로직만 집중할 수 있고, dto 변환 로직이 분리되어 코드 가독성과 책임 분리가 더 명확해질 것 같아요!!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

좋은 것 같습니다!! 저희는 하나의 서비스 클래스가 많은 책임을 가지는 게 아니라, 하나의 유스케이스 동작만을 담당하는 경우가 많아서 response 구성은 서비스에서 해도 문제없다고 생각했었는데 매퍼로 분리해도 좋습니다!
다만 이러면 매퍼에 해당 도메인에 속하는 서비스의 response 를 생성하는 메서드가 모두 포함되게 되는데, 이 부분은 괜찮다고 생각하시나여??

Copy link
Contributor

Choose a reason for hiding this comment

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

엇 저도 처음엔 성준님 의견처럼 저희가 서비스 클래스 하나당 하나의 유즈케이스만 담당하니까 상관없다고 생각했는데, 점점 controller에 주입하는 usecase가 많아질수록 비슷한 usecase들끼리는 같은 서비스 클래스에서 담당하도록 하는 것이 좋을 것 같다는 생각이 들었습니다! 따라서 mapper가 매핑 책임을 담당하도록 해서 서비스 클래스의 책임을 분리하면 추후에 저희가 비슷한 usecase들을 하나의 클래스에 모을때 더 수월할 것 같습니다!

response를 생성하는 메서드라고 하면 getter들을 말씀하시는 것 맞을까요? 제가 생각한 코드는 다음과 같은 느낌이였습니다!

@Mapper(componentModel = "spring")
public interface FeedQueryMapper {

    @Mapping(target = "profileImageUrl", source = "feedOwner.alias.imageUrl")
    @Mapping(target = "nickname", source = "feedOwner.nickname")
    @Mapping(target = "aliasName", source = "feedOwner.alias.value")
    @Mapping(target = "aliasColor", source = "feedOwner.alias.color")
    @Mapping(target = "followerCount", source = "feedOwner.followerCount")
    @Mapping(target = "totalFeedCount", source = "totalFeedCount")
    @Mapping(target = "latestFollowerProfileImageUrls", source = "latestFollowerProfileImageUrls")
    FeedShowUserInfoResponse toFeedShowUserInfoResponse(User feedOwner,
                                        int totalFeedCount,
                                        List<String> latestFollowerProfileImageUrls);
}
return feedShowUserInfoMapper.toFeedShowUserInfoResponse(feedOwner, allFeedCount, latestFollowerProfileImageUrls);

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

유스케이스가 추가될때마다 현준님 예시코드에서의 toFeedShowUserInfoResponse 와 같은 dto -> response로의 매핑 메서드가 계속 늘어나는 걸 얘기한 거였습니다!
mapper 에 추가해보겟습니닷

Copy link
Contributor

Choose a reason for hiding this comment

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

아하 음 제 생각은 모든 api에서 dto를 파싱할때 사용하지 않고 현재 성준님이 구현하신 api처럼 반환 필드가 많아서 따로 메서드를 추출해야 하는 경우에만 사용하면 mapper에 너무 많은 메서드가 쌓이지 않을 것 같습니다!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

아하아하 의도를 이제야 이해했네요!! 좋습니다!!

Comment on lines -26 to -28
@Column(name = "image_url", columnDefinition = "TEXT", nullable = false)
private String imageUrl;

Copy link
Contributor

Choose a reason for hiding this comment

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

👍🏻 👍🏻

Comment on lines +72 to 80
public int countAllFeedsByUserId(Long userId) {
// int 로 강제 형변환 해도 괜찮죠??
return (int) feedJpaRepository.countFeedsByUserId(userId, StatusType.ACTIVE);
return (int) feedJpaRepository.countAllFeedsByUserId(userId, StatusType.ACTIVE);
}

@Override
public int countPublicFeedsByUserId(Long userId) {
return (int) feedJpaRepository.countPublicFeedsByUserId(userId, StatusType.ACTIVE);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

여기서는 파라미터로 statusType까지 전달해서 동적으로 필터링한 이유가 있나요?? (단순 궁금)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

그냥 jpa repository 내부에서 status = ACTIVE 구문을 추가할까 하다가, 영속성 adapter 에서 주입해주는게 "status가 ACTIVE인 것을 찾는다" 라는 것을 보여주고 싶어서 위와 같이 구현해보았습니다

Comment on lines +93 to +109
@Override
public List<String> findLatestFollowerImageUrls(Long userId, int size) {
QFollowingJpaEntity following = QFollowingJpaEntity.followingJpaEntity;
QUserJpaEntity follower = QUserJpaEntity.userJpaEntity; // userId 를 팔로우하는 사람들(= follower)
QAliasJpaEntity alias = QAliasJpaEntity.aliasJpaEntity;

return jpaQueryFactory
.select(alias.imageUrl)
.from(following)
.join(following.userJpaEntity, follower)
.join(follower.aliasForUserJpaEntity, alias)
.where(following.followingUserJpaEntity.userId.eq(userId)
.and(following.status.eq(StatusType.ACTIVE)))
.orderBy(following.createdAt.desc())
.limit(size)
.fetch();
}
Copy link
Contributor

Choose a reason for hiding this comment

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

LGTM

@seongjunnoh seongjunnoh merged commit 39888ea into develop Jul 31, 2025
2 checks passed
@seongjunnoh seongjunnoh deleted the feat/#115-feed-get-info-of-user branch July 31, 2025 16:22
buzz0331 added a commit that referenced this pull request Aug 12, 2025
buzz0331 added a commit that referenced this pull request Aug 12, 2025
buzz0331 added a commit that referenced this pull request Aug 12, 2025
buzz0331 added a commit that referenced this pull request Aug 12, 2025
buzz0331 added a commit that referenced this pull request Aug 12, 2025
- 유효성 검증
- CursorBasedList -> Response DTO 파싱
- Response DTO에 필요한 필드 추가 쿼리 요청
buzz0331 added a commit that referenced this pull request Aug 12, 2025
buzz0331 added a commit that referenced this pull request Aug 12, 2025
buzz0331 added a commit that referenced this pull request Aug 12, 2025
buzz0331 added a commit that referenced this pull request Aug 12, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[THIP2025-168] [feat] 피드 조회 상단 화면을 위한 api 개발

2 participants