Skip to content

[feat] 반응 조회 api & 마이페이지 유저 정보 조회 api 구현#132

Merged
buzz0331 merged 12 commits into
developfrom
feat/#123-user-reactions
Aug 3, 2025
Merged

[feat] 반응 조회 api & 마이페이지 유저 정보 조회 api 구현#132
buzz0331 merged 12 commits into
developfrom
feat/#123-user-reactions

Conversation

@buzz0331
Copy link
Copy Markdown
Contributor

@buzz0331 buzz0331 commented Aug 2, 2025

#️⃣ 연관된 이슈

closes #123
closes #133

📝 작업 내용

반응 조회 api 흐름은 다음과 같습니다.

  • type : LIKE => 로그인한 사용자가 좋아요한 피드 또는 모임 게시글을 최신순 조회
  • type : COMMENT => 로그인한 사용자가 댓글을 남긴 피드 또는 모임 게시글을 최신순 조회
  • type : BOTH => 로그인한 사용자가 좋아요 또는 댓글을 남긴 피드 또는 모임 게시글을 최신순 조회

마이페이지 유저 정보 조회 api도 같이 구현했습니다. (너무 간단해서 굳이 테스트코드는 짜지 않았습니다.)

📸 스크린샷

스크린샷 2025-08-03 오전 2 34 34

💬 리뷰 요구사항

추가적으로 PostJpaEntity에 조회용 dtype 필드를 추가했습니다! insertable과 updatable을 모두 false로 둬서 오직 getter를 통해 불러오는 것만 가능한 필드입니다! @DiscriminatorValue에 설정해둔 FEED, VOTE, RECORD 이런식으로 반환됩니다.

추후에 상속 구조를 없애는 걸로 되어있었는데 찾아보니 상속 전략에 JOINED 전략과 SIGNLE_TABLE 전략이 있더라구요. 자동 join이 발생하는 것을 방지하고 싶으면 상속 구조를 아예 없애는 것보다 SINGLE_TABLE 전략을 고려해보는 것이 더 좋은 선택일 것 같다는 생각이 듭니다!

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

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

Summary by CodeRabbit

Summary by CodeRabbit

  • 신규 기능

    • 사용자의 좋아요 및 댓글 반응을 조회할 수 있는 API 엔드포인트가 추가되었습니다. (GET /users/reactions, 타입 및 커서 기반 페이지네이션 지원)
    • 사용자 마이페이지 프로필 정보를 조회하는 API가 추가되었습니다. (GET /users/my-page)
    • 반응 목록에 대한 커서 기반 페이지네이션과 마지막 페이지 여부(isLast) 정보가 제공됩니다.
    • 반응 조회 응답에 반응 상세 정보와 함께 다음 페이지 커서가 포함됩니다.
    • 반응 타입(좋아요, 댓글, 모두)에 따른 조회 기능이 확장되었습니다.
    • 사용자 반응 조회 시 다양한 반응 타입을 선택할 수 있는 기능이 추가되었습니다.
    • 사용자 반응 데이터를 효율적으로 매핑하고 표시하는 기능이 개선되었습니다.
  • 테스트

    • 사용자 반응 조회 API에 대한 통합 테스트가 추가되어, 좋아요/댓글/통합/페이지네이션 시나리오를 검증합니다.

@buzz0331 buzz0331 self-assigned this Aug 2, 2025
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Aug 2, 2025

Walkthrough

이번 변경에서는 사용자 반응(좋아요, 댓글) 조회 API가 새롭게 도입되었습니다. 이를 위해 컨트롤러, 서비스, 포트, 어댑터, 매퍼, DTO, 레포지토리 등 여러 계층에 걸쳐 관련 클래스와 메서드가 추가되었으며, 커서 기반 페이징 및 통합 응답 구조가 구현되었습니다. 통합 테스트도 추가되었습니다.

Changes

Cohort / File(s) Change Summary
커서 기반 페이징 및 유틸
src/main/java/konkuk/thip/common/util/CursorBasedList.java
isLast() 메서드 추가로 마지막 페이지 여부 반환 기능 도입
JPA 엔티티 확장
src/main/java/konkuk/thip/post/adapter/out/jpa/PostJpaEntity.java
dtype 필드 추가, 읽기 전용 JPA 컬럼 매핑
사용자 반응 API 컨트롤러
src/main/java/konkuk/thip/user/adapter/in/web/UserQueryController.java
/users/reactions GET API 및 showUserReaction 메서드 추가, /users/my-followings 경로명 변경
사용자 반응/프로필 응답 DTO
src/main/java/konkuk/thip/user/adapter/in/web/response/UserReactionResponse.java,
src/main/java/konkuk/thip/user/adapter/in/web/response/UserProfileResponse.java
사용자 반응 응답 및 프로필 응답 레코드 신규 도입, 중첩 DTO 및 팩토리 메서드 포함
사용자 반응 조회 퍼시스턴스 어댑터
src/main/java/konkuk/thip/user/adapter/out/persistence/UserQueryPersistenceAdapter.java,
src/main/java/konkuk/thip/user/adapter/out/persistence/function/ReactionQueryFunction.java
커서 기반 반응 조회 메서드 3종 및 함수형 인터페이스 도입, 공통 로직 추출
사용자 반응 레포지토리 및 구현
src/main/java/konkuk/thip/user/adapter/out/persistence/repository/UserQueryRepository.java,
src/main/java/konkuk/thip/user/adapter/out/persistence/repository/UserQueryRepositoryImpl.java
좋아요/댓글/전체 반응 조회 메서드 3종 추가, QueryDSL 기반 구현
반응 매핑 로직
src/main/java/konkuk/thip/user/application/mapper/ReactionQueryMapper.java
MapStruct 기반 ReactionQueryDto→Response 매핑 인터페이스 신규 도입, 타입별 커스텀 매핑 포함
사용자 마이페이지 유스케이스/서비스
src/main/java/konkuk/thip/user/application/port/in/UserMyPageUseCase.java,
src/main/java/konkuk/thip/user/application/service/UserMyPageService.java
반응/프로필 조회 유스케이스 및 서비스 구현, 비즈니스 로직 연결
반응 타입/DTO
src/main/java/konkuk/thip/user/application/port/in/dto/UserReactionType.java,
src/main/java/konkuk/thip/user/application/port/out/dto/ReactionQueryDto.java
반응 타입 enum 및 QueryDSL 지원 DTO 레코드 신규 도입
포트 인터페이스 확장
src/main/java/konkuk/thip/user/application/port/out/UserQueryPort.java
반응 조회용 커서 기반 메서드 3종 추가
테스트 코드
src/test/java/konkuk/thip/user/adapter/in/web/UserReactionApiTest.java
사용자 반응 조회 API 통합 테스트 클래스 및 주요 시나리오 4종 추가
불필요한 변경
src/main/java/konkuk/thip/user/application/mapper/UserQueryMapper.java
파일 끝에 불필요한 공백 2줄 추가 (기능 영향 없음)

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant UserQueryController
    participant UserMyPageService
    participant UserQueryPort
    participant UserQueryPersistenceAdapter
    participant UserQueryRepository

    Client->>UserQueryController: GET /users/reactions (userId, type, size, cursor)
    UserQueryController->>UserMyPageService: getUserReaction(userId, type, size, cursor)
    UserMyPageService->>UserQueryPort: find*ReactionsByUserId(userId, cursor)
    UserQueryPort->>UserQueryPersistenceAdapter: find*ReactionsByUserId(userId, cursor)
    UserQueryPersistenceAdapter->>UserQueryRepository: find*ByUserId(userId, cursorDateTime, size)
    UserQueryRepository-->>UserQueryPersistenceAdapter: List<ReactionQueryDto>
    UserQueryPersistenceAdapter-->>UserQueryPort: CursorBasedList<ReactionQueryDto>
    UserQueryPort-->>UserMyPageService: CursorBasedList<ReactionQueryDto>
    UserMyPageService->>UserQueryMapper: toReactionDtoList()
    UserMyPageService-->>UserQueryController: UserReactionResponse
    UserQueryController-->>Client: BaseResponse<UserReactionResponse>
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Assessment against linked issues

Objective Addressed Explanation
사용자 반응(좋아요/댓글/통합) 조회 API 개발 (#123)
커서 기반 페이징 및 응답 구조 구현 (#123)
통합 테스트로 API 동작 검증 (#123)

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
파일 끝 공백 추가 (src/main/java/konkuk/thip/user/application/mapper/UserQueryMapper.java) 기능과 무관한 단순 스타일 변경으로, 본 이슈 목적과 직접 관련 없음.

Possibly related PRs

Suggested reviewers

  • seongjunnoh
  • hd0rable

Poem

🐇
반응을 찾아 hop hop hop,
좋아요, 댓글, 한 번에 척척!
커서 따라 페이지를 넘기며,
토끼는 API를 뚝딱 완성해요.
테스트도 통과, 기분은 up!
오늘도 개발밭에 당근 한 입! 🥕


📜 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 08fda9d and 54d2435.

📒 Files selected for processing (2)
  • src/main/java/konkuk/thip/record/application/service/RecordCreateService.java (1 hunks)
  • src/test/java/konkuk/thip/record/adapter/in/web/RecordCreateControllerTest.java (6 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/test/java/konkuk/thip/record/adapter/in/web/RecordCreateControllerTest.java
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: hd0rable
PR: THIP-TextHip/THIP-Server#101
File: src/test/java/konkuk/thip/comment/adapter/in/web/CommentControllerTest.java:118-265
Timestamp: 2025-07-23T17:41:55.507Z
Learning: CommentControllerTest는 댓글 생성 API의 검증 로직과 예외 상황만을 테스트하는 단위 테스트이며, 성공 케이스는 별도의 통합 테스트(CommentCreateAPITest)에서 다룬다.
📚 Learning: thip 프로젝트에서 record와 vote는 room에 속하지만 feed는 room에 속하지 않는 구조이며, 댓글 작성 시 record/vote에 대해서만 사용자가 해당 room...
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#101
File: src/main/java/konkuk/thip/comment/application/service/CommentCreateService.java:36-39
Timestamp: 2025-07-26T06:09:00.850Z
Learning: THIP 프로젝트에서 Record와 Vote는 Room에 속하지만 Feed는 Room에 속하지 않는 구조이며, 댓글 작성 시 Record/Vote에 대해서만 사용자가 해당 Room의 참가자인지 검증이 필요하다.

Applied to files:

  • src/main/java/konkuk/thip/record/application/service/RecordCreateService.java
⏰ 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 (1)
src/main/java/konkuk/thip/record/application/service/RecordCreateService.java (1)

39-39: 매개변수 순서 수정이 올바르게 적용되었습니다.

roomParticipantValidator.validateUserIsRoomMember 호출 시 매개변수 순서를 (command.roomId(), command.userId())로 수정한 것이 검증자 메서드의 예상 매개변수 순서와 일치합니다. 이는 룸 멤버십 검증 로직이 올바르게 작동하도록 보장합니다.

✨ 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/#123-user-reactions

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

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: 5

🧹 Nitpick comments (5)
src/main/java/konkuk/thip/user/application/mapper/UserQueryMapper.java (1)

15-16: 파일 끝 불필요한 공백 라인 제거 권장

파일 종료 전에 연속된 두 줄의 공백이 추가되었습니다. 팀 코딩 컨벤션에서 EOF에 단일 개행만 유지하도록 하고 있다면, 중복 공백은 제거하는 편이 좋습니다.

-
src/main/java/konkuk/thip/post/adapter/out/jpa/PostJpaEntity.java (1)

30-32: 필드명과 주석 개선 제안

구현은 올바르지만 다음 개선사항을 제안합니다:

  1. dtype 필드명을 discriminatorType 또는 postType으로 변경하면 더 명확할 것 같습니다.
  2. 주석을 영어로 통일하는 것을 고려해보세요 (프로젝트 컨벤션에 따라).
-    // type 구분을 위한 조회용 컬럼
-    @Column(name = "dtype", insertable = false, updatable = false)
-    private String dtype;
+    // Read-only column for post type discrimination
+    @Column(name = "dtype", insertable = false, updatable = false)
+    private String discriminatorType;
src/main/java/konkuk/thip/user/application/port/in/dto/UserReactionType.java (1)

19-27: 성능 개선 및 예외 메시지 개선 제안

현재 구현은 올바르지만 다음 개선사항을 제안합니다:

  1. 성능 개선: 매번 배열을 순회하는 대신 정적 Map을 사용하면 더 효율적입니다.
  2. 예외 메시지: 국제화를 고려하여 영어 메시지 사용을 검토해보세요.
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.Arrays;
+
 @Getter
 public enum UserReactionType {
     LIKE("LIKE"),
     COMMENT("COMMENT"),
     BOTH("BOTH");

     private final String type;
+    
+    private static final Map<String, UserReactionType> TYPE_MAP = 
+        Arrays.stream(values())
+            .collect(Collectors.toMap(
+                reactionType -> reactionType.getType().toUpperCase(), 
+                reactionType -> reactionType
+            ));

     UserReactionType(String type) {
         this.type = type;
     }

     public static UserReactionType from(String type) {
-        for (UserReactionType reactionType : UserReactionType.values()) {
-            if (reactionType.getType().equalsIgnoreCase(type)) {
-                return reactionType;
-            }
-        }
+        UserReactionType reactionType = TYPE_MAP.get(type.toUpperCase());
+        if (reactionType != null) {
+            return reactionType;
+        }
         throw new InvalidStateException(ErrorCode.API_INVALID_PARAM,
-            new IllegalArgumentException("유효하지 않은 사용자 반응 타입: " + type));
+            new IllegalArgumentException("Invalid user reaction type: " + type));
     }
src/main/java/konkuk/thip/user/application/port/out/dto/ReactionQueryDto.java (1)

8-27: 레코드 구현이 잘 되었습니다.

QueryDSL 통합을 위한 @QueryProjection 사용과 모든 필드에 대한 null 체크가 적절히 구현되었습니다. id 필드가 feedId 또는 postId를 나타낸다는 주석도 명확합니다.

필요시 타입 안전성을 더 높이기 위해 id 필드를 feedIdpostId로 분리하는 것을 고려해볼 수 있지만, 현재 구현도 충분히 명확합니다.

src/main/java/konkuk/thip/user/adapter/in/web/response/UserReactionResponse.java (1)

5-29: 응답 DTO 구조가 잘 설계되었습니다.

커서 기반 페이징을 위한 필드들과 중첩된 ReactionDto 구조가 적절합니다. 정적 팩토리 메서드 사용도 좋은 패턴입니다.

postDate 필드가 String 타입인데, 일관성을 위해 다른 곳에서 사용하는 날짜 형식과 동일한지 확인해보세요.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between f375203 and c7a7e7b.

📒 Files selected for processing (17)
  • src/main/java/konkuk/thip/common/util/CursorBasedList.java (1 hunks)
  • src/main/java/konkuk/thip/post/adapter/out/jpa/PostJpaEntity.java (1 hunks)
  • src/main/java/konkuk/thip/user/adapter/in/web/UserQueryController.java (4 hunks)
  • src/main/java/konkuk/thip/user/adapter/in/web/response/UserProfileResponse.java (1 hunks)
  • src/main/java/konkuk/thip/user/adapter/in/web/response/UserReactionResponse.java (1 hunks)
  • src/main/java/konkuk/thip/user/adapter/out/persistence/UserQueryPersistenceAdapter.java (3 hunks)
  • src/main/java/konkuk/thip/user/adapter/out/persistence/function/ReactionQueryFunction.java (1 hunks)
  • src/main/java/konkuk/thip/user/adapter/out/persistence/repository/UserQueryRepository.java (2 hunks)
  • src/main/java/konkuk/thip/user/adapter/out/persistence/repository/UserQueryRepositoryImpl.java (2 hunks)
  • src/main/java/konkuk/thip/user/application/mapper/ReactionQueryMapper.java (1 hunks)
  • src/main/java/konkuk/thip/user/application/mapper/UserQueryMapper.java (1 hunks)
  • src/main/java/konkuk/thip/user/application/port/in/UserMyPageUseCase.java (1 hunks)
  • src/main/java/konkuk/thip/user/application/port/in/dto/UserReactionType.java (1 hunks)
  • src/main/java/konkuk/thip/user/application/port/out/UserQueryPort.java (2 hunks)
  • src/main/java/konkuk/thip/user/application/port/out/dto/ReactionQueryDto.java (1 hunks)
  • src/main/java/konkuk/thip/user/application/service/UserMyPageService.java (1 hunks)
  • src/test/java/konkuk/thip/user/adapter/in/web/UserReactionApiTest.java (1 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: buzz0331
PR: THIP-TextHip/THIP-Server#78
File: src/main/java/konkuk/thip/user/application/port/out/FollowingQueryPort.java:3-3
Timestamp: 2025-07-14T18:22:56.538Z
Learning: THIP 프로젝트에서는 Query API(조회 API)에 한해서는 application 계층에서 adapter.in.web.response 패키지의 response DTO를 직접 참조하는 것을 허용함. 이는 CQRS 아키텍처에서 읽기 전용 작업의 효율성을 위한 팀 컨벤션임.
Learnt from: hd0rable
PR: THIP-TextHip/THIP-Server#101
File: src/test/java/konkuk/thip/comment/adapter/in/web/CommentControllerTest.java:118-265
Timestamp: 2025-07-23T17:41:55.507Z
Learning: CommentControllerTest는 댓글 생성 API의 검증 로직과 예외 상황만을 테스트하는 단위 테스트이며, 성공 케이스는 별도의 통합 테스트(CommentCreateAPITest)에서 다룬다.
📚 Learning: thip 프로젝트에서는 query api(조회 api)에 한해서는 application 계층에서 adapter.in.web.response 패키지의 response dto를 직접 ...
Learnt from: buzz0331
PR: THIP-TextHip/THIP-Server#78
File: src/main/java/konkuk/thip/user/application/port/out/FollowingQueryPort.java:3-3
Timestamp: 2025-07-14T18:22:56.538Z
Learning: THIP 프로젝트에서는 Query API(조회 API)에 한해서는 application 계층에서 adapter.in.web.response 패키지의 response DTO를 직접 참조하는 것을 허용함. 이는 CQRS 아키텍처에서 읽기 전용 작업의 효율성을 위한 팀 컨벤션임.

Applied to files:

  • src/main/java/konkuk/thip/user/adapter/in/web/response/UserProfileResponse.java
  • src/main/java/konkuk/thip/user/application/port/out/dto/ReactionQueryDto.java
  • src/main/java/konkuk/thip/user/application/port/out/UserQueryPort.java
  • src/main/java/konkuk/thip/user/adapter/out/persistence/repository/UserQueryRepository.java
  • src/main/java/konkuk/thip/user/adapter/out/persistence/UserQueryPersistenceAdapter.java
📚 Learning: thip 프로젝트에서는 cqrs port 분리 시 다음 컨벤션을 따름: commandport에는 findbyxxx를 통해 도메인 엔티티를 찾아오는 메서드를 추가하고, querypo...
Learnt from: seongjunnoh
PR: THIP-TextHip/THIP-Server#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로부터 조회하는 메서드를 추가함.

Applied to files:

  • src/main/java/konkuk/thip/user/application/port/out/UserQueryPort.java
  • src/main/java/konkuk/thip/user/adapter/out/persistence/repository/UserQueryRepositoryImpl.java
  • src/main/java/konkuk/thip/user/adapter/out/persistence/repository/UserQueryRepository.java
📚 Learning: commentcontrollertest는 댓글 생성 api의 검증 로직과 예외 상황만을 테스트하는 단위 테스트이며, 성공 케이스는 별도의 통합 테스트(commentcreateapi...
Learnt from: hd0rable
PR: THIP-TextHip/THIP-Server#101
File: src/test/java/konkuk/thip/comment/adapter/in/web/CommentControllerTest.java:118-265
Timestamp: 2025-07-23T17:41:55.507Z
Learning: CommentControllerTest는 댓글 생성 API의 검증 로직과 예외 상황만을 테스트하는 단위 테스트이며, 성공 케이스는 별도의 통합 테스트(CommentCreateAPITest)에서 다룬다.

Applied to files:

  • src/test/java/konkuk/thip/user/adapter/in/web/UserReactionApiTest.java
🧬 Code Graph Analysis (2)
src/main/java/konkuk/thip/user/application/mapper/ReactionQueryMapper.java (1)
src/main/java/konkuk/thip/common/util/DateUtil.java (1)
  • DateUtil (12-62)
src/test/java/konkuk/thip/user/adapter/in/web/UserReactionApiTest.java (1)
src/test/java/konkuk/thip/common/util/TestEntityFactory.java (1)
  • TestEntityFactory (33-304)
⏰ 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 (20)
src/main/java/konkuk/thip/common/util/CursorBasedList.java (1)

17-19: LGTM!

isLast() 메서드의 구현이 논리적으로 올바르며, 커서 기반 페이징에서 마지막 페이지 여부를 확인하는 용도로 적절합니다.

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

8-11: LGTM!

함수형 인터페이스 설계가 적절하며, 커서 기반 페이징을 위한 메서드 시그니처가 명확합니다. 다양한 반응 쿼리 유형을 추상화하기에 좋은 설계입니다.

src/main/java/konkuk/thip/user/adapter/in/web/UserQueryController.java (2)

12-12: 새로운 import 및 의존성 추가가 적절합니다.

UserReactionType enum과 UserMyPageUseCase 인터페이스의 import가 올바르게 추가되었습니다.

Also applies to: 27-27


50-50: 의존성 주입 필드 추가가 적절합니다.

UserMyPageUseCase 의존성이 올바르게 추가되었습니다.

src/main/java/konkuk/thip/user/application/port/in/UserMyPageUseCase.java (1)

1-19: CQRS 패턴에 맞는 인터페이스 설계가 잘 되었습니다.

Query API에서 application 계층이 adapter.in.web.response 패키지의 response DTO를 직접 참조하는 것은 팀 컨벤션에 따라 허용됩니다. 메서드 시그니처가 명확하고 책임이 잘 분리되어 있습니다.

src/main/java/konkuk/thip/user/application/service/UserMyPageService.java (1)

25-42: 사용자 반응 조회 로직이 잘 구현되었습니다.

Switch 표현식을 사용한 반응 타입별 분기 처리, 커서 기반 페이징, 그리고 매퍼를 통한 DTO 변환이 깔끔하게 구현되었습니다.

src/main/java/konkuk/thip/user/application/port/out/UserQueryPort.java (1)

23-27: 새로운 반응 조회 메서드들이 잘 설계되었습니다.

커서 기반 페이징을 지원하는 세 가지 반응 조회 메서드가 적절히 추가되었습니다. 메서드명이 명확하고 CQRS QueryPort 패턴을 잘 따르고 있습니다.

src/test/java/konkuk/thip/user/adapter/in/web/UserReactionApiTest.java (4)

45-49: 통합 테스트 설정이 적절합니다.

Spring Boot 통합 테스트를 위한 어노테이션들과 프로파일 설정이 올바르게 구성되어 있습니다.


100-113: 테스트 데이터 준비가 체계적입니다.

필요한 모든 엔티티들(alias, user, category, book, room, feed, record, vote)을 TestEntityFactory를 통해 생성하고 있어 테스트 환경이 잘 구성되었습니다.


115-139: 좋아요 반응 조회 테스트가 철저합니다.

API 응답 구조와 데이터 검증이 상세하게 이루어지고 있습니다. JSON 경로별 검증이 적절합니다.


180-211: 커서 기반 페이징 테스트가 우수합니다.

15개 댓글을 생성하여 첫 10개 조회 후 nextCursor로 나머지 5개를 조회하는 시나리오가 실제 사용 패턴을 잘 반영합니다. isLast 플래그 검증도 포함되어 있어 완전한 페이징 동작을 확인합니다.

src/main/java/konkuk/thip/user/application/mapper/ReactionQueryMapper.java (3)

14-23: MapStruct 매퍼 구성이 우수합니다.

Spring 컴포넌트 모델 사용과 named 매핑 메서드를 통한 복잡한 매핑 로직 분리가 잘 되어 있습니다.


25-33: 포스트 타입별 ID 매핑이 적절합니다.

FEED 타입은 feedId로, VOTE/RECORD 타입은 postId로 조건부 매핑하는 로직이 올바릅니다. 타입에 따른 적절한 null 처리도 포함되어 있습니다.


35-38: 시간 포맷팅이 일관성 있게 처리됩니다.

기존 DateUtil.formatBeforeTime을 활용하여 상대적 시간 표시를 일관되게 처리하고 있습니다.

src/main/java/konkuk/thip/user/adapter/out/persistence/UserQueryPersistenceAdapter.java (2)

51-64: 포트 인터페이스 구현이 깔끔합니다.

세 가지 반응 조회 메서드가 getReactions 헬퍼 메서드를 통해 일관되게 구현되어 중복 코드가 제거되었습니다.


66-75: 커서 기반 페이징 로직이 잘 구현되었습니다.

커서를 LocalDateTime으로 변환하고, 함수형 인터페이스를 활용한 추상화, CursorBasedList 생성 로직이 모두 적절합니다. createdAt을 다음 커서로 사용하는 것도 시간순 정렬에 적합합니다.

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

15-19: 레포지토리 인터페이스 확장이 적절합니다.

커서 기반 페이징을 위한 세 가지 반응 조회 메서드가 일관된 파라미터 구조로 잘 정의되었습니다. LocalDateTime 커서 파라미터 사용이 시간 기반 페이징에 적합합니다.

src/main/java/konkuk/thip/user/adapter/out/persistence/repository/UserQueryRepositoryImpl.java (3)

3-18: 새로운 imports 추가 확인됨

반응 조회 API 구현을 위해 필요한 QueryDSL Q클래스들과 DTO가 적절히 추가되었습니다.


23-28: 새로운 imports 적절히 추가됨

Stream API와 시간 관련 클래스들이 새로운 기능에 맞게 적절히 추가되었습니다.


151-160: 병합 로직 성능 및 페이징 정확성 검증 필요

현재 findLikeAndCommentByUserId 메서드는 두 개의 별도 쿼리 결과를 메모리에서 병합한 뒤 페이징 처리합니다. 대량의 데이터 환경에서 성능 및 정확성 문제가 발생할 수 있으며, 해당 로직에 대한 테스트 커버리지도 확인되지 않았습니다.

확인 및 조치가 필요합니다:

  • src/main/java/konkuk/thip/user/adapter/out/persistence/repository/UserQueryRepositoryImpl.java
    • findLikeAndCommentByUserId 메서드 성능 최적화 검토
    • 각 쿼리에서 size + 1 만큼만 가져올 경우 병합 후 실제 페이지 크기보다 부족한 데이터가 반환될 여지
  • 테스트 커버리지
    • src/test/java/konkuk/thip/user/adapter/in/web/UserReactionApiTest.java 등에서 해당 메서드 또는 API 경로에 대한 경계 조건(좋아요만, 댓글만, 혼합 시) 테스트가 존재하는지 확인
    • 없는 경우, 병합 로직에 대한 단위·통합 테스트 추가

권장 개선 방안:

public List<ReactionQueryDto> findLikeAndCommentByUserId(Long userId, LocalDateTime cursor, Integer size) {
-    List<ReactionQueryDto> likes = findLikeByUserId(userId, cursor, size);
-    List<ReactionQueryDto> comments = findCommentByUserId(userId, cursor, size);
-    return Stream.concat(likes.stream(), comments.stream())
-            .sorted(Comparator.comparing(ReactionQueryDto::createdAt).reversed())
-            .limit(size + 1)
-            .toList();
+    // UNION 쿼리로 단일 호출하여 정렬 및 페이징 처리
+    // 또는 각 쿼리에서 충분한 여유분(size + 1 이상) 데이터를 조회한 뒤 정확한 병합 로직 적용
}

Comment thread src/main/java/konkuk/thip/user/adapter/in/web/response/UserProfileResponse.java Outdated
Comment on lines +127 to +141
@Operation(
summary = "사용자 반응 조회",
description = "사용자가 남긴 반응(좋아요, 댓글)을 조회합니다."
)
@GetMapping("/users/reactions")
public BaseResponse<UserReactionResponse> showUserReaction(
@Parameter(hidden = true) @UserId final Long userId,
@Parameter(description = "반응 타입 (LIKE, COMMENT) / default : 둘다", example = "LIKE")
@RequestParam(required = false, defaultValue = "BOTH") final String type,
@Parameter(description = "단일 요청 페이지 크기 (1~10) / default : 10", example = "10")
@RequestParam(defaultValue = "10") @Max(value = 10) @Min(value = 1) final int size,
@Parameter(description = "커서 (첫번째 요청시 : null, 다음 요청시 : 이전 요청에서 반환받은 nextCursor 값)")
@RequestParam(required = false) final String cursor) {
return BaseResponse.ok(userMyPageUseCase.getUserReaction(userId, UserReactionType.from(type), size, cursor));
}
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

타입 파라미터 검증 로직을 추가하세요.

API 엔드포인트 구현이 대체로 잘 되었지만, UserReactionType.from(type) 호출 시 잘못된 타입이 전달되면 예외가 발생할 수 있습니다. 컨트롤러 레벨에서 명시적인 검증을 추가하는 것을 고려해보세요.

다음과 같이 @Pattern 어노테이션을 추가하여 검증을 강화할 수 있습니다:

@Parameter(description = "반응 타입 (LIKE, COMMENT) / default : 둘다", example = "LIKE")
-@RequestParam(required = false, defaultValue = "BOTH") final String type,
+@RequestParam(required = false, defaultValue = "BOTH") 
+@Pattern(regexp = "LIKE|COMMENT|BOTH", message = "타입은 LIKE, COMMENT, BOTH 중 하나여야 합니다") final String type,
📝 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
@Operation(
summary = "사용자 반응 조회",
description = "사용자가 남긴 반응(좋아요, 댓글)을 조회합니다."
)
@GetMapping("/users/reactions")
public BaseResponse<UserReactionResponse> showUserReaction(
@Parameter(hidden = true) @UserId final Long userId,
@Parameter(description = "반응 타입 (LIKE, COMMENT) / default : 둘다", example = "LIKE")
@RequestParam(required = false, defaultValue = "BOTH") final String type,
@Parameter(description = "단일 요청 페이지 크기 (1~10) / default : 10", example = "10")
@RequestParam(defaultValue = "10") @Max(value = 10) @Min(value = 1) final int size,
@Parameter(description = "커서 (첫번째 요청시 : null, 다음 요청시 : 이전 요청에서 반환받은 nextCursor 값)")
@RequestParam(required = false) final String cursor) {
return BaseResponse.ok(userMyPageUseCase.getUserReaction(userId, UserReactionType.from(type), size, cursor));
}
@Operation(
summary = "사용자 반응 조회",
description = "사용자가 남긴 반응(좋아요, 댓글)을 조회합니다."
)
@GetMapping("/users/reactions")
public BaseResponse<UserReactionResponse> showUserReaction(
@Parameter(hidden = true) @UserId final Long userId,
@Parameter(description = "반응 타입 (LIKE, COMMENT) / default : 둘다", example = "LIKE")
@RequestParam(required = false, defaultValue = "BOTH")
@Pattern(regexp = "LIKE|COMMENT|BOTH", message = "타입은 LIKE, COMMENT, BOTH 중 하나여야 합니다") final String type,
@Parameter(description = "단일 요청 페이지 크기 (1~10) / default : 10", example = "10")
@RequestParam(defaultValue = "10") @Max(value = 10) @Min(value = 1) final int size,
@Parameter(description = "커서 (첫번째 요청시 : null, 다음 요청시 : 이전 요청에서 반환받은 nextCursor 값)")
@RequestParam(required = false) final String cursor) {
return BaseResponse.ok(userMyPageUseCase.getUserReaction(userId, UserReactionType.from(type), size, cursor));
}
🤖 Prompt for AI Agents
In src/main/java/konkuk/thip/user/adapter/in/web/UserQueryController.java around
lines 127 to 141, the type parameter passed to UserReactionType.from(type) can
cause exceptions if invalid values are provided. To fix this, add validation on
the 'type' request parameter by using the @Pattern annotation with a regex that
only allows valid values like "LIKE", "COMMENT", or "BOTH". This will ensure
invalid inputs are rejected before reaching the service layer, preventing
runtime exceptions.

Comment on lines +85 to +116
@Override
public List<ReactionQueryDto> findLikeByUserId(Long userId, LocalDateTime cursorLocalDateTime, Integer size) {
QUserJpaEntity user = QUserJpaEntity.userJpaEntity;
QPostLikeJpaEntity postLike = QPostLikeJpaEntity.postLikeJpaEntity;
QPostJpaEntity post = QPostJpaEntity.postJpaEntity;

BooleanBuilder where = new BooleanBuilder();
where.and(user.userId.eq(userId))
.and(post.status.eq(StatusType.ACTIVE))
.and(postLike.status.eq(StatusType.ACTIVE));
if (cursorLocalDateTime != null) {
where.and(postLike.createdAt.lt(cursorLocalDateTime));
}

return queryFactory
.select(new QReactionQueryDto(
Expressions.constant("좋아요"),
post.postId,
post.userJpaEntity.nickname,
post.userJpaEntity.userId,
post.dtype,
post.content,
postLike.createdAt
))
.from(postLike)
.join(postLike.postJpaEntity, post)
.join(postLike.userJpaEntity, user)
.where(where)
.orderBy(postLike.createdAt.desc())
.limit(size + 1)
.fetch();
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

좋아요 조회 메서드 구현이 적절함

커서 기반 페이징과 적절한 조인을 통해 사용자의 좋아요 반응을 조회하는 로직이 잘 구현되었습니다. 다만 다음 사항들을 확인해주세요:

  1. limit(size + 1) 사용으로 다음 페이지 존재 여부를 확인할 수 있어 좋습니다.
  2. 활성 상태 필터링이 적절히 적용되었습니다.

성능 최적화를 위해 다음 인덱스가 존재하는지 확인해주세요:


🏁 Script executed:

#!/bin/bash
# Description: 좋아요 조회 성능을 위한 인덱스 존재 여부 확인
# Expected: post_like 테이블에 (user_id, created_at, status) 복합 인덱스가 존재해야 함

echo "좋아요 조회 성능을 위한 인덱스 확인이 필요합니다:"
echo "1. PostLikeJpaEntity에서 (userJpaEntity.userId, createdAt, status) 복합 인덱스"
echo "2. PostJpaEntity에서 status 인덱스"

# 데이터베이스 스키마 파일에서 인덱스 정의 확인
fd -e sql -e ddl | xargs grep -l "post_like\|PostLike" | head -5

Length of output: 355


🏁 Script executed:

#!/bin/bash
echo "JPA 엔티티의 인덱스 정의 확인:"

# PostLikeJpaEntity에서 @Table 및 @Index 어노테이션 검색
fd -e java -g "PostLikeJpaEntity.java" | xargs grep -n "@Table\|@Index"

# PostJpaEntity에서 @Table 및 @Index 어노테이션 검색
fd -e java -g "PostJpaEntity.java" | xargs grep -n "@Table\|@Index"

Length of output: 244


인덱스 정의가 누락됨 – 성능 최적화용 인덱스 추가 필요

PostLikeJpaEntity 및 PostJpaEntity에서 조회 쿼드의 성능을 보장하기 위한 인덱스(@Index 또는 DB 마이그레이션)가 정의되어 있지 않습니다. 아래 인덱스를 추가해주세요:

  • PostLikeJpaEntity.java
    @Table(
      name = "post_likes",
      indexes = {
        @Index(
          name = "idx_post_likes_user_created_status",
          columnList = "user_id, created_at, status"
        )
      }
    )
  • PostJpaEntity.java
    @Table(
      name = "posts",
      indexes = {
        @Index(
          name = "idx_posts_status",
          columnList = "status"
        )
      }
    )

또는 Flyway/Liquibase 마이그레이션 스크립트에 아래 SQL을 추가할 수도 있습니다:

CREATE INDEX idx_post_likes_user_created_status
  ON post_likes (user_id, created_at, status);

CREATE INDEX idx_posts_status
  ON posts (status);

위 인덱스가 반영된 후, 실제 쿼리 성능이 개선되었는지 확인 부탁드립니다.

🤖 Prompt for AI Agents
In
src/main/java/konkuk/thip/user/adapter/out/persistence/repository/UserQueryRepositoryImpl.java
lines 85 to 116, the query performance can be improved by adding database
indexes. Add the specified @Index annotations to the PostLikeJpaEntity and
PostJpaEntity classes on the columns user_id, created_at, status for post_likes
and status for posts respectively. Alternatively, create corresponding Flyway or
Liquibase migration scripts to add these indexes at the database level. After
applying the indexes, verify that the query performance has improved.

Comment on lines +118 to +149
@Override
public List<ReactionQueryDto> findCommentByUserId(Long userId, LocalDateTime cursorLocalDateTime, Integer size) {
QUserJpaEntity user = QUserJpaEntity.userJpaEntity;
QPostJpaEntity post = QPostJpaEntity.postJpaEntity;
QCommentJpaEntity comment = QCommentJpaEntity.commentJpaEntity;

BooleanBuilder where = new BooleanBuilder();
where.and(user.userId.eq(userId))
.and(post.status.eq(StatusType.ACTIVE))
.and(comment.status.eq(StatusType.ACTIVE));
if (cursorLocalDateTime != null) {
where.and(comment.createdAt.lt(cursorLocalDateTime));
}

return queryFactory
.select(new QReactionQueryDto(
Expressions.constant("댓글"),
post.postId,
post.userJpaEntity.nickname,
post.userJpaEntity.userId,
post.dtype,
post.content, // 일단은 post의 content를 사용 (추후에 댓글 content로 수정 가능)
comment.createdAt
))
.from(comment)
.join(comment.userJpaEntity, user)
.join(comment.postJpaEntity, post)
.where(where)
.orderBy(comment.createdAt.desc())
.limit(size + 1)
.fetch();
}
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

댓글 조회 메서드에 개선점 존재

전반적인 구현은 좋으나 다음 사항들을 개선해주세요:

  1. Line 139: post.content 대신 comment.content를 사용해야 합니다. 주석에서 언급했듯이 추후 수정이 필요한 부분입니다.
  2. 조인 순서를 최적화할 수 있습니다.

다음과 같이 수정을 권장합니다:

                .select(new QReactionQueryDto(
                        Expressions.constant("댓글"),
                        post.postId,
                        post.userJpaEntity.nickname,
                        post.userJpaEntity.userId,
                        post.dtype,
-                       post.content, // 일단은 post의 content를 사용 (추후에 댓글 content로 수정 가능)
+                       comment.content,
                        comment.createdAt
                ))
📝 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 List<ReactionQueryDto> findCommentByUserId(Long userId, LocalDateTime cursorLocalDateTime, Integer size) {
QUserJpaEntity user = QUserJpaEntity.userJpaEntity;
QPostJpaEntity post = QPostJpaEntity.postJpaEntity;
QCommentJpaEntity comment = QCommentJpaEntity.commentJpaEntity;
BooleanBuilder where = new BooleanBuilder();
where.and(user.userId.eq(userId))
.and(post.status.eq(StatusType.ACTIVE))
.and(comment.status.eq(StatusType.ACTIVE));
if (cursorLocalDateTime != null) {
where.and(comment.createdAt.lt(cursorLocalDateTime));
}
return queryFactory
.select(new QReactionQueryDto(
Expressions.constant("댓글"),
post.postId,
post.userJpaEntity.nickname,
post.userJpaEntity.userId,
post.dtype,
post.content, // 일단은 post의 content를 사용 (추후에 댓글 content로 수정 가능)
comment.createdAt
))
.from(comment)
.join(comment.userJpaEntity, user)
.join(comment.postJpaEntity, post)
.where(where)
.orderBy(comment.createdAt.desc())
.limit(size + 1)
.fetch();
}
@Override
public List<ReactionQueryDto> findCommentByUserId(Long userId, LocalDateTime cursorLocalDateTime, Integer size) {
QUserJpaEntity user = QUserJpaEntity.userJpaEntity;
QPostJpaEntity post = QPostJpaEntity.postJpaEntity;
QCommentJpaEntity comment = QCommentJpaEntity.commentJpaEntity;
BooleanBuilder where = new BooleanBuilder();
where.and(user.userId.eq(userId))
.and(post.status.eq(StatusType.ACTIVE))
.and(comment.status.eq(StatusType.ACTIVE));
if (cursorLocalDateTime != null) {
where.and(comment.createdAt.lt(cursorLocalDateTime));
}
return queryFactory
.select(new QReactionQueryDto(
Expressions.constant("댓글"),
post.postId,
post.userJpaEntity.nickname,
post.userJpaEntity.userId,
post.dtype,
comment.content,
comment.createdAt
))
.from(comment)
.join(comment.userJpaEntity, user)
.join(comment.postJpaEntity, post)
.where(where)
.orderBy(comment.createdAt.desc())
.limit(size + 1)
.fetch();
}
🤖 Prompt for AI Agents
In
src/main/java/konkuk/thip/user/adapter/out/persistence/repository/UserQueryRepositoryImpl.java
between lines 118 and 149, the method findCommentByUserId incorrectly selects
post.content instead of comment.content as noted in the comment; update the
select clause to use comment.content. Additionally, optimize the join order by
starting from the post entity, then joining comment and user entities to improve
query performance.

@buzz0331 buzz0331 changed the title [feat] 반응 조회 api 구현 [feat] 반응 조회 api & 마이페이지 유저 정보 조회 api 구현 Aug 2, 2025
Copy link
Copy Markdown
Member

@hd0rable hd0rable left a comment

Choose a reason for hiding this comment

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

수고하셨습니닷!! 간단한 리뷰 남겼는데 확인 부탁드립니당

)
@ExceptionDescription(GET_USER_FOLLOW)
@GetMapping("/users/my/following")
@GetMapping("/users/my-followings")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM


return queryFactory
.select(new QReactionQueryDto(
Expressions.constant("좋아요"),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

p3: 여기에 하드코딩 대신 UserReactionType에 좋아요,댓글도 추가해서 UserReactionType.getValue() 이런식으로 쓰는 것은 어떨까요?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

UserReactionType은 파라미터로 받는 상수라고 생각해서 컨트롤러 쪽에서만 사용하려규 일부러 주입을 안 시킨거긴 한데, 가져와서 사용하는게 나을까요? 근데 BOTH에는 그러면 무의미한 문자열이 들어갈 것 같아서,, 아니면 ReactionLabel이라는 enum을 하나 persistence패키지에 둘까요??

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

아하 근데 그 결국 파라미터의 값이 반환값이기도 하니 의존받아도될거같 긴한데 그러면 Both에은 무의미한 문자열이 들어갈것같기도 하네여

ReactionLabel이라는 enum을 하나 persistence패키지에 둘까요??

이렇게하면 그 enum을 파라미터에서도 쓰신다는건가요?? 아니면 따로 또 생성하신다는건지..? 따로 쓸거면 그냥 파라미터 값이 결국 반환값이니 의존받아도될것같다는 생각입니닷

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.

전 어차피 request 로 들어오는 String label 값을 내부에서 enum 으로 매핑 (+ 유효성 검증) 하고, enum type인 reactionType 을 다시 response로 응답하는 거니, UserQueryRepositoryImpl 에서 현재 구현된 UserReactionType enum의 의존성이 있어도 괜찮다고 생각합니다!
동일한 value를 가지는 enum을 adatper/in, adapter/out 에 중복해서 유지한다면, 해당 코드가 수정될 경우 2군데를 건드려야하는 번거로움이 있을것 같습니다

adapter/out 에서 enum 의존성이 있는게 별로라면,

  1. service에서 reactionType에 따라 분기처리하여 queryPort의 메서드를 호출할때 String 타입의 enum value를 넘기고
  2. 이걸 UserQueryRepositoryImpl 까지 넘기기
  3. UserQueryRepositoryImpl 에서는 전달받은 String type의 reactionType 을 이용해서 dto 구성하여 반환
    -> both 의 경우에는 현재 코드 구조상 service에서 "like", "comment" 2개를 모두 전달하고, UserQueryRepositoryImpl 에서는 이를 받아 "like" 를 활용해 메서드 호출, "comment" 를 활용해 메서드 호출, 반환받은 결과를 ordering 해야할 것 같네요

위와 같은 방법도 있을 것 같습니다!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

오호 좋은 것 같습니다!! 성준님 말대로 enum에서 매핑해두고 서비스 로직에서 PersistenceAdapter쪽으로 넘겨줄때 enum에서 label을 문자열로 꺼내서 넘겨주는 쪽으로 수정해보겠습니다!

post.userJpaEntity.nickname,
post.userJpaEntity.userId,
post.dtype,
post.content, // 일단은 post의 content를 사용 (추후에 댓글 content로 수정 가능)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

엇 이거 왜 post의 content를 사용하신건가요??

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

현재 댓글에 대한 반응을 누르면 해당하는 게시물로 이동이 되는 흐름으로 이해해서 commentId가 아닌 postId를 넘기고 있습니다. 따라서 보여주는 content도 댓글이 아닌 게시글을 보여주는게 더 자연스럽지 않을까 생각했습니다!(기획적인 부분이라 한번 물어봐야될 것 같네여)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

아아 그렇네여 이따 회의시간에 물어보겠습니다!~~

@ActiveProfiles("test")
@AutoConfigureMockMvc(addFilters = false)
@Transactional
@DisplayName("[통합] 사용자 반응 조회 API 통합 테스트")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM

seongjunnoh
seongjunnoh previously approved these changes Aug 3, 2025
Copy link
Copy Markdown
Collaborator

@seongjunnoh seongjunnoh left a comment

Choose a reason for hiding this comment

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

고생하셨습니다! enum 과 관련해서 코멘트 추가하였습니다!!

@Override
public UserReactionResponse getUserReaction(Long userId, UserReactionType userReactionType, int size, String cursorStr) {

Cursor cursor = Cursor.from(cursorStr, size);
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.

네이밍 굳 입니다

Comment on lines +66 to +75
private CursorBasedList<ReactionQueryDto> getReactions(Long userId, Cursor cursor, ReactionQueryFunction reactionQueryFunction) {
LocalDateTime cursorLocalDateTime = cursor.isFirstRequest() ? null : cursor.getLocalDateTime(0);

List<ReactionQueryDto> reactionQueryDtos = reactionQueryFunction.fetch(userId, cursorLocalDateTime, cursor.getPageSize());

return CursorBasedList.of(reactionQueryDtos, cursor.getPageSize(), reactionQueryDto -> {
Cursor nextCursor = new Cursor(List.of(reactionQueryDto.createdAt().toString()));
return nextCursor.toEncodedString();
});
}
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.

LGTM

BooleanBuilder where = new BooleanBuilder();
where.and(user.userId.eq(userId))
.and(post.status.eq(StatusType.ACTIVE))
.and(postLike.status.eq(StatusType.ACTIVE));
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.

p3 : PostLike 는 hard delete 이므로 해당 조건문은 없어도 문제없을 것 같습니다!


return queryFactory
.select(new QReactionQueryDto(
Expressions.constant("좋아요"),
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.

전 어차피 request 로 들어오는 String label 값을 내부에서 enum 으로 매핑 (+ 유효성 검증) 하고, enum type인 reactionType 을 다시 response로 응답하는 거니, UserQueryRepositoryImpl 에서 현재 구현된 UserReactionType enum의 의존성이 있어도 괜찮다고 생각합니다!
동일한 value를 가지는 enum을 adatper/in, adapter/out 에 중복해서 유지한다면, 해당 코드가 수정될 경우 2군데를 건드려야하는 번거로움이 있을것 같습니다

adapter/out 에서 enum 의존성이 있는게 별로라면,

  1. service에서 reactionType에 따라 분기처리하여 queryPort의 메서드를 호출할때 String 타입의 enum value를 넘기고
  2. 이걸 UserQueryRepositoryImpl 까지 넘기기
  3. UserQueryRepositoryImpl 에서는 전달받은 String type의 reactionType 을 이용해서 dto 구성하여 반환
    -> both 의 경우에는 현재 코드 구조상 service에서 "like", "comment" 2개를 모두 전달하고, UserQueryRepositoryImpl 에서는 이를 받아 "like" 를 활용해 메서드 호출, "comment" 를 활용해 메서드 호출, 반환받은 결과를 ordering 해야할 것 같네요

위와 같은 방법도 있을 것 같습니다!

public UserProfileResponse getUserProfile(Long userId) {
User user = userCommandPort.findById(userId);

return UserProfileResponse.of(user.getAlias().getImageUrl(), user.getNickname(), user.getAlias().getValue());
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.

굳굳

Comment on lines +180 to +181
@Test
@DisplayName("커서 기반 페이징 정상 동작 확인")
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.

@buzz0331
Copy link
Copy Markdown
Contributor Author

buzz0331 commented Aug 3, 2025

@hd0rable @seongjunnoh 리뷰 반영했습니다! 확인 부탁드릴게여~

  1. 댓글 조회시에 comment.content가 조회되도록 수정
  2. label에 대한 하드코딩을 enum에서 꺼내서 사용하도록 수정

Copy link
Copy Markdown
Collaborator

@seongjunnoh seongjunnoh 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 +34 to +36
case LIKE -> userQueryPort.findLikeReactionsByUserId(userId, cursor, UserReactionType.LIKE.getLabel());
case COMMENT -> userQueryPort.findCommentReactionsByUserId(userId, cursor, UserReactionType.COMMENT.getLabel());
case BOTH -> userQueryPort.findBothReactionsByUserId(userId, cursor, UserReactionType.LIKE.getLabel(), UserReactionType.COMMENT.getLabel());
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.

굳굳

post.userJpaEntity.nickname,
post.userJpaEntity.userId,
post.dtype,
comment.content,
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.

확인햇슴다

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-180] [feat] 마이페이지 유저 정보 조회 api [THIP2025-173] [feat] 반응 조회 api 개발

3 participants