Skip to content

[feat] 단일 피드 조회 api 개발#135

Merged
seongjunnoh merged 13 commits into
developfrom
feat/#130-feed-show-single
Aug 5, 2025
Merged

[feat] 단일 피드 조회 api 개발#135
seongjunnoh merged 13 commits into
developfrom
feat/#130-feed-show-single

Conversation

@seongjunnoh

@seongjunnoh seongjunnoh commented Aug 3, 2025

Copy link
Copy Markdown
Collaborator

#️⃣ 연관된 이슈

closes #130

📝 작업 내용

단일 피드 조회 api 를 개발하였습니다

  • 주요 코드
    • service
      • 피드 도메인 조회 후, private 피드 일 경우 400 error 를 반환하도록 하였습니다
      • 피드 작성자 도메인, 피드와 연관된 책 도메인 을 조회합니다
      • 현재 유저(= access token 에 포함된 유저) 가 해당 피드를 저장했는지, 좋아요 하였는지를 조회합니다
      • 조회한 도메인, 데이터를 FeedQueryMapper 에서 response dto 로 매핑합니다

📸 스크린샷

image

💬 리뷰 요구사항

FeedQueryDto 를 활용하여 QueryDSL 로 여러 테이블을 join하여 response 를 구성할까 하다가, service 에서 필요한 도메인들을 조회한 후 queryMapper 에서 이를 엮어서 response dto를 반환하도록 하였습니다.

필터링, 페이징, 정렬이 필요한 조회로직이 아니므로 service 에서 도메인을 조회하도록 구현하였습니다!

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

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

Summary by CodeRabbit

  • 신규 기능

    • 단일 피드의 상세 정보를 조회할 수 있는 API 엔드포인트가 추가되었습니다. 피드 작성자, 책 정보, 본문, 이미지, 좋아요/저장 여부, 태그 등 다양한 정보를 확인할 수 있습니다.
    • 비공개 피드는 작성자 외 조회 불가하며, 이에 대한 권한 검증과 에러 메시지가 제공됩니다.
  • 버그 수정

    • 비공개 피드 접근 시 적절한 에러 코드와 메시지가 반환됩니다.
  • 테스트

    • 단일 피드 조회 성공, 비공개 피드 접근 실패, 작성자 비공개 피드 조회 성공에 대한 통합 테스트가 추가되었습니다.
    • 피드 도메인의 권한 검증 로직에 대한 단위 테스트가 추가되었습니다.
    • 기존 테스트 클래스명이 명확하게 변경되었습니다.

@seongjunnoh seongjunnoh linked an issue Aug 3, 2025 that may be closed by this pull request
2 tasks
@coderabbitai

coderabbitai Bot commented Aug 3, 2025

Copy link
Copy Markdown

Walkthrough

피드 상세 조회 API가 새롭게 추가되었습니다. 이를 위해 컨트롤러, 서비스, 포트, 매퍼, DTO, 에러코드, 스웨거 응답 설명, 그리고 통합 테스트가 생성 및 수정되었습니다. 기존 기능에는 영향이 없으며, 새로운 단일 피드 조회 엔드포인트와 관련 로직이 도입되었습니다.

Changes

Cohort / File(s) Change Summary
피드 상세조회 API 엔드포인트 및 서비스 구현
src/main/java/konkuk/thip/feed/adapter/in/web/FeedQueryController.java, src/main/java/konkuk/thip/feed/application/port/in/FeedShowSingleUseCase.java, src/main/java/konkuk/thip/feed/application/service/FeedShowSingleService.java, src/main/java/konkuk/thip/feed/application/mapper/FeedQueryMapper.java, src/main/java/konkuk/thip/feed/adapter/in/web/response/FeedShowSingleResponse.java
피드 상세조회 API 엔드포인트, 서비스, 포트, 매퍼, DTO가 신설되어 단일 피드 상세정보를 제공하는 기능이 추가됨
에러 및 스웨거 응답 코드 추가
src/main/java/konkuk/thip/common/exception/code/ErrorCode.java, src/main/java/konkuk/thip/common/swagger/SwaggerResponseDescription.java
비공개 피드 조회 불가 에러코드 및 상세조회 API용 스웨거 응답 설명이 추가됨
도메인 로직 및 검증 추가
src/main/java/konkuk/thip/feed/domain/Feed.java
피드 조회 권한 검증 메서드 validateViewPermission 추가, 비공개 피드 접근 제한 로직 구현
통합 테스트 추가 및 클래스명 변경
src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowSingleApiTest.java, src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowSpecificUserApiTest.java, src/test/java/konkuk/thip/feed/domain/FeedTest.java
상세조회 API 통합 테스트 및 도메인 단위 테스트 신설, 특정 유저 피드 조회 테스트 클래스명 일부 변경

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant FeedQueryController
    participant FeedShowSingleService
    participant FeedRepository
    participant UserRepository
    participant BookRepository
    participant Mapper

    Client->>FeedQueryController: GET /feeds/{feedId} (userId)
    FeedQueryController->>FeedShowSingleService: showSingleFeed(feedId, userId)
    FeedShowSingleService->>FeedRepository: findFeedById(feedId)
    FeedShowSingleService->>Feed: validateViewPermission(userId)
    FeedShowSingleService->>UserRepository: findUserById(creatorId)
    FeedShowSingleService->>BookRepository: findBookByIsbn(feed.isbn)
    FeedShowSingleService->>FeedShowSingleService: checkSavedAndLiked(feedId, userId)
    FeedShowSingleService->>Mapper: toFeedShowSingleResponse(...)
    FeedShowSingleService-->>FeedQueryController: FeedShowSingleResponse
    FeedQueryController-->>Client: BaseResponse<FeedShowSingleResponse>
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Assessment against linked issues

Objective Addressed Explanation
피드 상세조회 API 개발 (#130)
Task1, Task2 (#130) 구체적인 Task1, Task2의 내용이 명시되어 있지 않아 확인 불가

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
클래스명 변경 (FeedShowSpecificUserTestFeedShowSpecificUserApiTest) (src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowSpecificUserApiTest.java) 본 PR의 목적(피드 상세조회 API 개발)과 직접적 연관이 없는 테스트 클래스명 변경입니다.

Suggested reviewers

  • buzz0331

Poem

🐰
새로 열린 피드의 문,
한눈에 담는 상세한 숨,
공개와 비공개 구분도 똑똑히,
오류 코드도 빠짐없이,
테스트까지 꼼꼼하게 챙긴 오늘,
토끼는 기쁜 마음으로 깡총!

(피드 상세조회, 이제 걱정 끝!)

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 c7c7959 and bf1c2d4.

📒 Files selected for processing (5)
  • src/main/java/konkuk/thip/common/exception/code/ErrorCode.java (1 hunks)
  • src/main/java/konkuk/thip/common/swagger/SwaggerResponseDescription.java (1 hunks)
  • src/main/java/konkuk/thip/feed/application/mapper/FeedQueryMapper.java (2 hunks)
  • src/main/java/konkuk/thip/feed/domain/Feed.java (2 hunks)
  • src/test/java/konkuk/thip/feed/domain/FeedTest.java (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/main/java/konkuk/thip/feed/application/mapper/FeedQueryMapper.java
  • src/test/java/konkuk/thip/feed/domain/FeedTest.java
  • src/main/java/konkuk/thip/common/swagger/SwaggerResponseDescription.java
  • src/main/java/konkuk/thip/feed/domain/Feed.java
  • src/main/java/konkuk/thip/common/exception/code/ErrorCode.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
✨ 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/#130-feed-show-single

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

Support

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

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/main/java/konkuk/thip/common/exception/code/ErrorCode.java (1)

156-156: HTTP 상태 코드 검토가 필요합니다.

비공개 피드 접근 제한은 접근 권한 문제이므로 BAD_REQUEST 대신 FORBIDDEN이 더 적합할 수 있습니다. BAD_REQUEST는 일반적으로 잘못된 요청 형식을 나타내는 반면, FORBIDDEN은 서버가 요청을 이해했지만 승인을 거부하는 경우에 사용됩니다.

-    FEED_CAN_NOT_SHOW_PRIVATE_ONE(HttpStatus.BAD_REQUEST, 160008, "비공개 피드는 조회할 수 없습니다."),
+    FEED_CAN_NOT_SHOW_PRIVATE_ONE(HttpStatus.FORBIDDEN, 160008, "비공개 피드는 조회할 수 없습니다."),
src/main/java/konkuk/thip/feed/application/service/FeedShowSingleService.java (1)

34-34: 입력 파라미터 검증 추가를 고려해보세요.

feedIduserId에 대한 null 체크가 없습니다. 잘못된 입력으로 인한 NPE를 방지하기 위해 검증 로직을 추가하는 것이 좋겠습니다.

 @Override
 public FeedShowSingleResponse showSingleFeed(Long feedId, Long userId) {
+    if (feedId == null || userId == null) {
+        throw new BusinessException(API_BAD_REQUEST);
+    }
+    
     // 1. 단일 피드 조회
📜 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 f00c2c7.

📒 Files selected for processing (9)
  • src/main/java/konkuk/thip/common/exception/code/ErrorCode.java (1 hunks)
  • src/main/java/konkuk/thip/common/swagger/SwaggerResponseDescription.java (1 hunks)
  • src/main/java/konkuk/thip/feed/adapter/in/web/FeedQueryController.java (3 hunks)
  • src/main/java/konkuk/thip/feed/adapter/in/web/response/FeedShowSingleResponse.java (1 hunks)
  • src/main/java/konkuk/thip/feed/application/mapper/FeedQueryMapper.java (2 hunks)
  • src/main/java/konkuk/thip/feed/application/port/in/FeedShowSingleUseCase.java (1 hunks)
  • src/main/java/konkuk/thip/feed/application/service/FeedShowSingleService.java (1 hunks)
  • src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowSingleApiTest.java (1 hunks)
  • src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowSpecificUserApiTest.java (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 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 단일 커서 방식을 선호하며, 복합 키 기반 커서보다 구현 단순성과 성능을 우선시한다.
📚 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/feed/adapter/in/web/FeedShowSingleApiTest.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 (19)
src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowSpecificUserApiTest.java (1)

42-42: 네이밍 컨벤션 개선을 위한 좋은 변경사항입니다.

API 통합 테스트 클래스명에 ApiTest 접미사를 추가하여 테스트 유형을 명확히 구분할 수 있게 되었습니다.

src/main/java/konkuk/thip/common/swagger/SwaggerResponseDescription.java (1)

172-177: 단일 피드 조회 API에 대한 적절한 에러 코드 그룹핑입니다.

관련된 모든 에러 케이스를 포괄적으로 포함하고 있어 API 문서화에 도움이 됩니다.

src/main/java/konkuk/thip/feed/application/port/in/FeedShowSingleUseCase.java (2)

7-7: 메서드 시그니처가 명확하고 적절합니다.

파라미터명과 반환 타입이 메서드의 목적을 잘 표현하고 있습니다.


3-3: 아키텍처 의존성 방향 검토가 필요합니다.

application.port.in 패키지가 adapter.in.web.response 패키지의 클래스를 import하고 있습니다. 헥사고날 아키텍처에서 application 레이어는 adapter 레이어에 의존하지 않아야 합니다.

Response DTO를 application 레이어 내부로 이동하거나, 도메인 객체를 반환하도록 수정하는 것을 고려해보세요.

⛔ Skipped due to 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: 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로부터 조회하는 메서드를 추가함.
src/main/java/konkuk/thip/feed/application/service/FeedShowSingleService.java (4)

36-39: 비공개 피드 접근 제어 로직이 적절합니다.

피드 조회 후 즉시 공개 여부를 확인하여 접근을 제어하는 로직이 올바르게 구현되었습니다.


41-45: 도메인 객체 조회 로직이 명확합니다.

피드 작성자와 연관 도서 정보를 적절한 포트를 통해 조회하고 있습니다.


47-51: 사용자 상호작용 상태 확인 로직이 효율적입니다.

Set을 사용하여 저장 및 좋아요 상태를 한 번에 조회하고 contains()로 확인하는 방식이 효율적입니다.


53-53: 매퍼를 통한 응답 변환이 적절합니다.

도메인 객체들과 상태 플래그를 매퍼를 통해 응답 DTO로 변환하는 방식이 관심사 분리 원칙을 잘 따르고 있습니다.

src/main/java/konkuk/thip/feed/adapter/in/web/response/FeedShowSingleResponse.java (1)

3-21: 레코드 클래스 구조가 잘 설계되었습니다.

단일 피드 조회에 필요한 모든 필드가 적절히 포함되어 있고, 불변 객체인 레코드를 사용하여 DTO의 목적에 부합합니다.

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

8-8: Swagger 예외 문서화 어노테이션 추가가 좋습니다.

새로운 API의 예외 상황에 대한 문서화가 추가되어 API 사용성이 개선됩니다.


12-12: 새로운 UseCase 인터페이스 import가 적절합니다.

헥사고날 아키텍처 패턴에 따라 포트 인터페이스를 통한 의존성 주입이 올바르게 구현되었습니다.


30-30: 의존성 주입이 올바르게 구현되었습니다.

final 키워드를 사용하여 불변성을 보장하고 기존 패턴과 일관성을 유지합니다.


89-100: 단일 피드 조회 엔드포인트가 잘 구현되었습니다.

  • REST API 설계 원칙에 따른 적절한 경로 구조 (/feeds/{feedId})
  • Swagger 문서화가 완전함
  • 예외 처리 문서화 포함
  • 기존 컨트롤러 패턴과 일관성 유지
src/main/java/konkuk/thip/feed/application/mapper/FeedQueryMapper.java (3)

3-9: 필요한 도메인 객체 import가 적절히 추가되었습니다.

새로운 매핑 기능에 필요한 도메인 클래스들이 정확히 import되었습니다.


63-79: 단일 피드 응답 매핑이 완전하고 정확합니다.

  • 모든 필드가 명시적으로 매핑되어 명확함
  • DateUtil을 사용한 일관된 날짜 포맷팅
  • 도메인 객체에서 DTO로의 적절한 변환
  • MapStruct 어노테이션 사용이 올바름

82-99: 헬퍼 메서드들이 방어적으로 잘 구현되었습니다.

  • null 체크로 NullPointerException 방지
  • Stream API를 활용한 효율적인 변환
  • 빈 배열 반환으로 일관된 동작 보장
  • 메서드명이 목적을 명확히 표현
src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowSingleApiTest.java (3)

77-112: 성공 케이스 테스트가 포괄적으로 잘 구현되었습니다.

  • 필요한 모든 테스트 데이터 설정이 완전함
  • 피드 저장/좋아요 상태 검증 포함
  • JSON 응답 필드들의 정확한 검증
  • 태그와 컨텐츠 URL 배열 검증까지 포함

116-137: 비공개 피드 접근 제한 테스트가 적절합니다.

비공개 피드에 대한 접근 제한과 적절한 에러 메시지 반환을 정확히 검증합니다. 보안 요구사항이 올바르게 테스트되었습니다.


62-73: 데이터 정리 순서 검증 완료

  • postLike, savedFeed, content, feedTag 등 Feed를 참조하는 모든 엔티티가 feedJpaRepository.deleteAllInBatch() 호출 이전에 삭제되고 있습니다.
  • 따라서 외래키 제약 조건 충돌 없이 안전하게 삭제됩니다.

@hd0rable hd0rable left a comment

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.

수고하셨습니다!! 다만 저는 현재 개발하신 단일 피드 조회가 피드 상세조회로 모든 피드가 조회되는 화면에서 해당 피드를 선택했을시에 피드의 정보가 상세조회되는 api로 이해해서 비공개 게시글일 경우, 작성자는 조회할수 있다 라고생각해서 리뷰달았습니다!! 보시고 확인 부탁드립니닷

@Override
public FeedShowSingleResponse showSingleFeed(Long feedId, Long userId) {
// 1. 단일 피드 조회
Feed feed = feedCommandPort.getByIdOrThrow(feedId);

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.

p2: 근데 혹시 피드 상세조회시에 피드를 작성한 게시자라면 비공개 글이라도 조회할수 있지않나요 ?? feed.validateCreateComment 처럼 피드의 도메인 내의 함수를 사용하여 로직 수정하시면 될 것같습니다! 만약 아니더라도 서비스에서 예외처리 대신 비공개 피드 조회에 대한 예외는 도메인 내에서 처리하는게 더 자연스러울 것 같습니다!

Copy link
Copy Markdown
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 +48 to +49
Set<Long> savedFeedIds = savedQueryPort.findSavedFeedIdsByUserIdAndFeedIds(Set.of(feedId), userId);
Set<Long> likedFeedIds = postLikeQueryPort.findPostIdsLikedByUser(Set.of(feedId), userId);

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

DUPLICATED_FEEDS_IN_COLLECTION(HttpStatus.INTERNAL_SERVER_ERROR, 160005, "중복된 피드가 존재합니다."),
FEED_ALREADY_SAVED(HttpStatus.BAD_REQUEST, 160006, "사용자가 이미 저장한 피드입니다."),
FEED_NOT_SAVED_CANNOT_DELETE(HttpStatus.BAD_REQUEST, 160007, "사용자가 저장하지 않은 피드는 저장삭제 할 수 없습니다."),
FEED_CAN_NOT_SHOW_PRIVATE_ONE(HttpStatus.BAD_REQUEST, 160008, "비공개 피드는 조회할 수 없습니다."),

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.

이것도 비공개 게시글일 시 작성사가 조회하는 것이라면 필요없는 코드일 것같습니다!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/main/java/konkuk/thip/feed/domain/Feed.java (1)

165-172: 피드 조회 권한 검증 로직이 올바르게 구현되었습니다.

공개 피드는 모든 사용자가 조회 가능하고, 비공개 피드는 작성자만 조회 가능하도록 하는 비즈니스 로직이 정확히 구현되었습니다. JavaDoc 문서화도 적절합니다.

다만 null 안전성을 고려해볼 수 있습니다:

userId가 null일 경우를 고려한 방어적 코딩을 추가할 수 있습니다:

 public void validateViewPermission(Long userId) {
+    if (userId == null) {
+        throw new BusinessException(FEED_CAN_NOT_SHOW_PRIVATE_ONE);
+    }
     if (!isPublic && !creatorId.equals(userId)) {
         throw new BusinessException(FEED_CAN_NOT_SHOW_PRIVATE_ONE);
     }
 }
src/test/java/konkuk/thip/feed/domain/FeedTest.java (1)

1-62: 포괄적이고 잘 구조화된 단위 테스트입니다.

validateViewPermission 메서드의 모든 시나리오를 적절히 테스트하고 있습니다:

  • 공개 피드 조회 (성공)
  • 비공개 피드 작성자 조회 (성공)
  • 비공개 피드 타인 조회 (실패)

테스트 메서드명이 한국어로 명확하게 작성되어 있고, 헬퍼 메서드를 통한 테스트 데이터 생성도 적절합니다.

null userId에 대한 엣지 케이스 테스트를 추가할 수 있습니다:

@Test
@DisplayName("userId가 null인 경우 비공개 피드 조회 시 예외가 발생한다.")
void validateViewPermission_exception_when_null_userId() throws Exception {
    //given
    Feed privateFeed = makeFeedWithPublicStatus(false);

    //when //then
    assertThatThrownBy(() -> privateFeed.validateViewPermission(null))
            .isInstanceOf(BusinessException.class)
            .hasMessage(FEED_CAN_NOT_SHOW_PRIVATE_ONE.getMessage());
}
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between f00c2c7 and c7c7959.

📒 Files selected for processing (5)
  • src/main/java/konkuk/thip/common/exception/code/ErrorCode.java (1 hunks)
  • src/main/java/konkuk/thip/feed/application/service/FeedShowSingleService.java (1 hunks)
  • src/main/java/konkuk/thip/feed/domain/Feed.java (2 hunks)
  • src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowSingleApiTest.java (1 hunks)
  • src/test/java/konkuk/thip/feed/domain/FeedTest.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main/java/konkuk/thip/common/exception/code/ErrorCode.java
  • src/main/java/konkuk/thip/feed/application/service/FeedShowSingleService.java
🧰 Additional context used
🧠 Learnings (2)
📓 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 단일 커서 방식을 선호하며, 복합 키 기반 커서보다 구현 단순성과 성능을 우선시한다.
📚 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/feed/domain/Feed.java
🧬 Code Graph Analysis (1)
src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowSingleApiTest.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 (5)
src/main/java/konkuk/thip/feed/domain/Feed.java (1)

4-4: 새로운 import 추가가 적절합니다.

BusinessException import가 새로운 validateViewPermission 메서드에서 사용되기 위해 적절히 추가되었습니다.

src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowSingleApiTest.java (4)

40-73: 통합 테스트 설정이 적절하고 포괄적입니다.

Spring Boot 테스트 설정과 MockMvc 사용이 적절하며, @AfterEach에서 모든 관련 테이블을 정리하여 테스트 격리를 보장하고 있습니다. 다수의 Repository 의존성은 복잡해 보이지만 피드 도메인의 연관관계를 고려할 때 필요한 설정입니다.


75-112: 공개 피드 조회 테스트가 상세하고 정확합니다.

테스트 데이터 설정이 현실적이며, 응답 검증이 매우 상세합니다:

  • 피드 기본 정보 (ID, 작성자, ISBN)
  • 컨텐츠 URL 목록
  • 저장/좋아요 상태
  • 태그 목록

TestEntityFactory를 활용한 테스트 데이터 생성도 적절합니다.


114-137: 비공개 피드 접근 제한 테스트가 정확합니다.

비공개 피드에 대한 타인의 접근을 적절히 차단하고, 올바른 에러 코드와 메시지를 반환하는지 검증하고 있습니다. 도메인 레벨의 권한 검증 로직이 API 레벨까지 올바르게 전파되는 것을 확인할 수 있습니다.


139-161: 피드 작성자의 비공개 피드 조회 테스트가 적절합니다.

작성자가 자신의 비공개 피드를 조회할 수 있는 시나리오를 간결하게 테스트하고 있습니다. 성공 케이스에 대한 핵심 검증(피드 ID 일치)이 포함되어 있습니다.

hd0rable
hd0rable previously approved these changes Aug 3, 2025

@hd0rable hd0rable left a comment

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.

수고하셨습니닷

}

@Test
@DisplayName("피드 작성자는 비공개 피드를 단일 조회할 수 있다.")

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

buzz0331
buzz0331 previously approved these changes Aug 3, 2025

@buzz0331 buzz0331 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

굿굿 로직 깔끔하네여~

Comment on lines +81 to +99
// List<Content> → String[] 변환
default String[] mapContentList(List<Content> contentList) {
if (contentList == null) {
return new String[0];
}
return contentList.stream()
.map(Content::getContentUrl)
.toArray(String[]::new);
}

// List<Tag> → String[] 변환
default String[] mapTagList(List<Tag> tagList) {
if (tagList == null) {
return new String[0];
}
return tagList.stream()
.map(Tag::getValue)
.toArray(String[]::new);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

p3: 사용되지 않는 메서드 같은데 지우는게 좋을 것 같습니다!

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

엇 이게 있어야 FeedQueryMapperImpl 에서 해당 메서드 참고해서 dto -> 피드 단일 상세조회 response 로의 매핑을 잘 시켜주는 것으로 확인했습니다!

image

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

아하 명시하지 않아도 MapStruct가 내부적으로 사용하나 보네요. 찾아보니 메서드 이름이 map[SourceType] 형식이라 명시적으로 매핑하지 않아도 찾을 수 있다고 합니다!

다만, 어떤 매핑 함수가 사용되는지 명시되어 있으면 더 좋을 것 같아서 제가 ReactionQueryMapper에서 했던 것처럼 다음과 같이 수정해보면 어떨지 제안드립니다!!

  • 변환 메서드에 @Named 추가
import org.mapstruct.Named;

@Named("mapContentList")
default String[] mapContentList(List<Content> contentList) {
    if (contentList == null) return new String[0];
    return contentList.stream().map(Content::getContentUrl).toArray(String[]::new);
}

@Named("mapTagList")
default String[] mapTagList(List<Tag> tagList) {
    if (tagList == null) return new String[0];
    return tagList.stream().map(Tag::getValue).toArray(String[]::new);
}
  • @Mapping에서 qualifiedByName으로 명시
@Mapping(target = "contentUrls", source = "feed.contentList", qualifiedByName = "mapContentList")
@Mapping(target = "tagList", source = "feed.tagList", qualifiedByName = "mapTagList")
FeedShowSingleResponse toFeedShowSingleResponse(
    Feed feed,
    User feedCreator,
    Book book,
    boolean isSaved,
    boolean isLiked
);

Comment on lines +48 to +49
Set<Long> savedFeedIds = savedQueryPort.findSavedFeedIdsByUserIdAndFeedIds(Set.of(feedId), userId);
Set<Long> likedFeedIds = postLikeQueryPort.findPostIdsLikedByUser(Set.of(feedId), userId);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

@seongjunnoh seongjunnoh dismissed stale reviews from buzz0331 and hd0rable via bf1c2d4 August 5, 2025 12:19
@seongjunnoh seongjunnoh merged commit 7b7bea3 into develop Aug 5, 2025
2 checks passed
@seongjunnoh seongjunnoh deleted the feat/#130-feed-show-single branch August 5, 2025 12:23
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-178] [feat] 피드 상세조회 api 개발

3 participants