Skip to content

refactor: 피드 조회 api dto 수정#79

Merged
lejuho merged 5 commits into
developfrom
feat/75-피드-dto-수정
Aug 24, 2025

Hidden character warning

The head ref may contain hidden characters: "feat/75-\ud53c\ub4dc-dto-\uc218\uc815"
Merged

refactor: 피드 조회 api dto 수정#79
lejuho merged 5 commits into
developfrom
feat/75-피드-dto-수정

Conversation

@xoruddl
Copy link
Copy Markdown
Member

@xoruddl xoruddl commented Aug 24, 2025

📝 개요
피드 조회 dto 수정

💻 작업 내용
이번 PR에서 작업한 내용을 상세히 설명해 주세요.

작업 내용 1
작업 내용 2
...

✅ PR 체크리스트
PR을 보내기 전에 아래 체크리스트를 확인해 주세요.

커밋 메시지는 포맷에 맞게 작성했나요?
스스로 코드를 다시 한번 검토했나요?
관련 이슈를 연결했나요?
빌드 및 테스트가 로컬에서 성공했나요?

🔗 관련 이슈
이번 PR과 관련된 이슈 번호를 기재해 주세요. 예: Closes #75

스크린샷 (선택)
UI 변경 사항이 있다면 스크린샷을 첨부해 주세요.

Summary by CodeRabbit

  • New Features
    • 피드 조회 API에 페이지네이션(page, size) 파라미터를 도입해 대용량 피드를 단계적으로 로드할 수 있습니다.
    • 피드 응답을 단일 형식으로 통합해 일기와 아바타 게시물을 한 목록에서 일관되게 확인할 수 있습니다.
    • 서버에서 소스들을 병합한 뒤 최신순으로 정렬하고 범위 기반 잘라내기(페이징)를 적용해 응답 예측성을 개선했습니다.
    • 요청한 페이지가 범위를 벗어날 경우 빈 목록을 반환합니다.

@xoruddl xoruddl linked an issue Aug 24, 2025 that may be closed by this pull request
6 tasks
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Aug 24, 2025

Walkthrough

피드 응답 DTO를 단일 형태(FeedResponse)로 추가하고 PostType enum을 신설했다. 컨트롤러는 반환 타입을 FeedResponse로 변경하고 page/size 파라미터를 받아 서비스로 전달한다. 서비스는 다이어리와 아바타 포스트를 후보로 조회해 FeedResponse로 매핑·병합·정렬한 뒤 메모리에서 슬라이싱하여 페이지네이션한다.

Changes

Cohort / File(s) Summary
Unified Feed DTOs
src/main/java/.../feed/dto/response/FeedResponse.java, src/main/java/.../feed/dto/response/PostType.java
새 레코드 FeedResponse(Long postId, PostType postType, String imageUrl, @JsonIgnore LocalDateTime createdAt) 추가. 도메인 매핑 팩토리 from(Diary), from(AvatarPost) 추가. PostType enum(DIARY, AVATAR_POST) 신설.
Controller Pagination & DTO Swap
src/main/java/.../feed/presentation/FeedController.java
getFeed 반환 타입을 List<FeedResponse>로 변경. page(default 0) 및 size(default 20) 요청 파라미터 추가. 서비스 호출을 feedService.getFeed(user.getUuid(), filter, page, size)로 갱신.
Service Merge & In-memory Paging
src/main/java/.../feed/service/FeedService.java
시그니처를 getFeed(UUID, String, int, int)로 변경하고 반환을 List<FeedResponse>로 통일. 다이어리/아바타 포스트를 후보(candidatePageable)로 조회해 FeedResponse로 매핑, 병합 후 createdAt 기준 내림차순 정렬 및 메모리 슬라이싱으로 페이지 결과 반환.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    actor Client
    participant Controller as FeedController
    participant Service as FeedService
    participant DiaryRepo as DiaryRepository
    participant AvatarRepo as AvatarPostRepository

    Client->>Controller: GET /feed?filter=...&page=...&size=...
    Controller->>Service: getFeed(userUuid, filter, page, size)
    alt following filter
        Service->>DiaryRepo: findDiariesOfFollowing(candidatePageable, excludeBlocked)
        DiaryRepo-->>Service: List<Diary>
        Service->>AvatarRepo: findAvatarPostsOfFollowing(candidatePageable, excludeBlocked)
        AvatarRepo-->>Service: List<AvatarPost>
    else all/non-following
        Service->>DiaryRepo: findDiaries(candidatePageable, excludeBlocked)
        DiaryRepo-->>Service: List<Diary>
        Service->>AvatarRepo: findAvatarPosts(candidatePageable, excludeBlocked)
        AvatarRepo-->>Service: List<AvatarPost>
    end
    Service->>Service: map to FeedResponse (from)
    Service->>Service: merge streams & sort by createdAt desc
    Service->>Service: slice (start=page*size, end=min(start+size,total))
    Service-->>Controller: List<FeedResponse>
    Controller-->>Client: 200 OK ApiResponse<List<FeedResponse>>
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Assessment against linked issues

Objective Addressed Explanation
피드 DTO 수정 및 단일화 (#75)
컨트롤러에서 새 DTO 적용 및 API 변경 (#75)
서비스 레이어에서 새 DTO 매핑 및 반환 적용 (#75)

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
페이지네이션 파라미터 추가 (src/main/java/.../feed/presentation/FeedController.java) 이슈 #75는 DTO 수정이 주된 목적으로 보이며 API 파라미터 확장은 명시적 요구가 아님.
후보 페치 전략 및 인메모리 페이지네이션 구현 (src/main/java/.../feed/service/FeedService.java) DTO 변경 이외에 데이터 수집 방식(후보Pageable 산정, 병합 후 메모리 슬라이싱)이 설계 변경으로 보이며 이슈의 명시적 범위를 벗어남.

Suggested labels

Refactor

Suggested reviewers

  • lejuho

Poem

귀가 쫑긋한 토끼가 말하네 🐇
두 포스트를 한 그릇에 담아
시간 순으로 탕탕 정렬했지요
페이지 뚝딱, 응답은 바삭바삭
당근 들고 머지하러 깡충!


📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 06d8379 and 0fd2d3d.

📒 Files selected for processing (1)
  • src/main/java/com/example/cp_main_be/domain/social/feed/presentation/FeedController.java (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/com/example/cp_main_be/domain/social/feed/presentation/FeedController.java
✨ 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/75-피드-dto-수정

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.
    • 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.
  • 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 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/Issue comments)

Type @coderabbitai help to get the list of available commands.

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

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • 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: 2

🧹 Nitpick comments (14)
src/main/java/com/example/cp_main_be/domain/social/feed/dto/response/PostType.java (1)

3-6: 네이밍/위치 소폭 제안: AVATAR_POST vs AVATAR, 패키지 레이어링

  • 향후 아바타 관련 포스트 외 타입이 늘 경우, AVATAR_POST 보다 AVATAR 가 더 일반적일 수 있습니다.
  • 응답 DTO 패키지(dto.response)에 enum 이 위치하면 재사용 범위가 DTO 레이어로 한정됩니다. 도메인 전역에서 참조될 가능성이 있으면 공용 패키지(예: domain.social.feed.model 또는 domain.common)로 이동을 고려해도 좋습니다.
src/main/java/com/example/cp_main_be/domain/social/feed/dto/response/FeedResponse.java (2)

12-14: createdAt@JsonIgnore 적용 — 페이징/정렬 확장성 관점 점검 필요

현재는 서버 측에서만 정렬/슬라이싱하므로 문제는 없지만, 추후 클라이언트 측 무한스크롤(커서 기반) 또는 정합성 검증(“다음 페이지 유효성”)이 필요하면 createdAt 또는 별도 cursor 값이 응답에 포함되어야 합니다. 최소한 스펙에 "클라이언트로는 createdAt 미노출"임을 명시하거나, cursor 필드 도입을 고려해주세요.


15-18: 이미지 매핑 NPE 방어는 OK — 다중 이미지/썸네일 정책 확인 권장

diary.getDiaryImage() 단건 기준 매핑은 깔끔합니다. 다만 다중 이미지가 허용되는 모델이라면 대표 이미지(썸네일) 선택 규칙이 필요할 수 있습니다. 현 모델이 단건 보장인지 확인 부탁드립니다.

src/main/java/com/example/cp_main_be/domain/social/feed/presentation/FeedController.java (4)

28-33: 페이지 파라미터 검증 추가 제안(DoS/메모리 급증 방지)

size 무제한/대형 값 요청을 차단하기 위해 Bean Validation을 붙이는 것을 권장합니다(예: size <= 100).
아래처럼 컨트롤러 파라미터에 직접 부착하면 간단합니다.

-  public ResponseEntity<ApiResponse<List<FeedResponse>>> getFeed(
+  public ResponseEntity<ApiResponse<List<FeedResponse>>> getFeed(
       @AuthenticationPrincipal User user, // 인증된 사용자 정보
-      @RequestParam(required = false) String filter,
-      @RequestParam(defaultValue = "0") int page,
-      @RequestParam(defaultValue = "20") int size) {
+      @RequestParam(required = false) String filter,
+      @RequestParam(defaultValue = "0") @jakarta.validation.constraints.PositiveOrZero int page,
+      @RequestParam(defaultValue = "20") @jakarta.validation.constraints.Positive @jakarta.validation.constraints.Max(100) int size) {

추가로, 아래 import가 필요합니다(파일 상단):

+import jakarta.validation.constraints.Max;
+import jakarta.validation.constraints.Positive;
+import jakarta.validation.constraints.PositiveOrZero;

35-35: 인증 컨텍스트 가정 확인

@AuthenticationPrincipal User user 가 null 일 수 있는 경로(익명 접근)가 완전히 차단되어 있는지 확인 부탁드립니다. 미보장 시 user.getUuid() 에서 NPE 발생합니다. 시큐리티 설정으로 엔드포인트 보호 또는 널 가드/401 응답 처리를 권장합니다.


37-37: 주석 타입 명시 오탈자

주석의 FeedItemResponse 는 현재 타입과 불일치합니다. FeedResponse 로 교정해주세요.

-    // List<Object>가 아닌 List<FeedItemResponse>로 응답 타입을 명확히 합니다.
+    // List<Object>가 아닌 List<FeedResponse>로 응답 타입을 명확히 합니다.

26-27: API 문서화에 필터 값/기본값 명시 권장

filter"following" 만 특별 취급됨을 스펙에 명시하고, 가능한 값(예: following | all)을 OpenAPI @Operation/@Parameter 로 표시하면 클라이언트 통합이 수월합니다.

src/main/java/com/example/cp_main_be/domain/social/feed/service/FeedService.java (7)

35-41: (성능) 소스별 (page+1)*size 후보 수집은 안전하지만 상한선 필요

딥 페이지네이션 시 두 소스에서 2배 데이터를 메모리에 적재합니다. 컨트롤러 단 size 상한(+ 서버 프로퍼티로 튜너블) 적용을 권장합니다. 추후 트래픽 증가 시 커서 기반(Keyset)으로의 전환을 고려해 주세요.


49-50: 차단 정책 보강 제안: ‘내가 차단한 유저’뿐 아니라 ‘나를 차단한 유저’도 배제

현재는 blockedUserIdsByBlocker(currentUser)만 제외합니다. 일반적인 블록 의미론(양방향 차단)이라면 “나를 차단한 유저”도 제외해야 합니다. 리포지토리 메서드가 있다면 병합해 주세요.


54-57: 팔로잉 필터에서 ‘자기 자신’ 포함 여부 확인

일반적으로 팔로잉 피드에는 자신의 포스트도 함께 보이는 경우가 많습니다. 현재 구현은 followingUsers 만 포함하므로 자기 자신 게시물이 제외됩니다. 요구사항에 맞는지 확인 바랍니다.


83-87: 동일 시각 게시물의 결정적 정렬 보장

createdAt 동일 시 타이브레이커가 없어 페이지 경계에서 불안정할 수 있습니다. postId(또는 소스+ID)로 2차 정렬을 추가해 결정성을 높이세요.

-        .sorted(Comparator.comparing(FeedResponse::createdAt).reversed())
+        .sorted(
+            Comparator.<FeedResponse, java.time.LocalDateTime>comparing(FeedResponse::createdAt)
+                .reversed()
+                .thenComparing(Comparator.comparing(FeedResponse::postId).reversed()))

35-97: 테스트 커버리지 제안

다음 케이스 단위/통합 테스트를 추가하면 회귀 방지에 유효합니다.

  • 팔로잉/전체 모드 각각에서 차단된 유저의 포스트 미포함 검증
  • page/size 경계값(0, 1, 최대치) 및 범위 초과 시 빈 리스트 반환
  • createdAt 동일한 아이템의 결정적 정렬(타이브레이커 적용 후)
  • Diary 이미지 null/존재 케이스 매핑 결과
  • filter 미지정/대소문자 혼용(following, Following) 동작

35-41: 추가 아키텍처 제안: 커서(키셋) 기반 통합 정렬

현재는 소스별 페이지블 + 메모리 병합 정렬 구조입니다. 트래픽 규모가 커지면 DB 레벨에서 “소스 통합 뷰(UNION ALL) + 인덱스(created_at, user_id, is_public)” 또는 커서 기반(최신 createdAt, id) 페이징이 유리합니다. 중장기 로드맵에 반영을 권장합니다.


58-69: 파생 쿼리 메서드 존재 확인 및 JDK 설정

  • 아래 4가지 파생 메서드는 DiaryRepository·AvatarPostRepository에 모두 정상으로 선언되어 있습니다.
  • Gradle toolchain에서 Java 17(languageVersion = JavaLanguageVersion.of(17))을 사용 중임을 확인했습니다.

다만, JPA의 NotIn 연산에 빈 컬렉션을 넘겼을 때 DB 벤더(MySQL, PostgreSQL 등) 및 JPA 구현체(Hibernate 등)마다

  • 전체 반환
  • 예외 발생
    로 동작이 달라질 수 있으므로,
    blockedUserIds가 빈 리스트일 경우에는
  1. 별도 분기 처리해 쿼리를 생략하거나
  2. NotIn 대신 다른 쿼리(예: In + 전체 제외 등)
    방안을 검토하실 것을 권장드립니다.

적용 위치:

  • src/main/java/com/example/cp_main_be/domain/social/feed/service/FeedService.java (58–69, 74–81)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 6570c49 and 1dda5cd.

📒 Files selected for processing (4)
  • src/main/java/com/example/cp_main_be/domain/social/feed/dto/response/FeedResponse.java (1 hunks)
  • src/main/java/com/example/cp_main_be/domain/social/feed/dto/response/PostType.java (1 hunks)
  • src/main/java/com/example/cp_main_be/domain/social/feed/presentation/FeedController.java (2 hunks)
  • src/main/java/com/example/cp_main_be/domain/social/feed/service/FeedService.java (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/main/java/com/example/cp_main_be/domain/social/feed/service/FeedService.java (1)
src/main/java/com/example/cp_main_be/global/exception/UserNotFoundException.java (1)
  • UserNotFoundException (3-7)
🔇 Additional comments (2)
src/main/java/com/example/cp_main_be/domain/social/feed/dto/response/PostType.java (1)

3-6: Enum 추가 적절 — 단일 피드 타입 식별자 도입은 👍

피드 통합에 필요한 최소한의 타입만 노출되어 단순하고 명확합니다.

src/main/java/com/example/cp_main_be/domain/social/feed/service/FeedService.java (1)

90-97: JDK 버전 호환성 확인: Stream#toList() + subList

Stream.toList() 는 JDK 16+ 필요합니다. 현재 빌드 타깃이 JDK 17(스프링 부트 3.x 권장)이라면 OK입니다. 또한 toList() 결과는 불변 리스트이지만 subList는 읽기 전용 뷰를 반환하므로 사용 자체는 문제 없습니다. 빌드 JDK 버전만 확인해 주세요.

Comment on lines +20 to +27
public static FeedResponse from(AvatarPost avatarPost) {
// AvatarPost에 imageUrl 필드가 있다고 가정합니다. 필드명은 실제 코드에 맞게 수정해주세요.
return new FeedResponse(
avatarPost.getId(),
PostType.AVATAR_POST,
avatarPost.getImageUrl(),
avatarPost.getCreatedAt());
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

컴파일 리스크: AvatarPost#getImageUrl() 존재 가정 주석

주석대로 실제 필드/메서드명이 다르면 컴파일 실패합니다. 해당 엔터티의 정확한 접근자에 맞춰 즉시 수정이 필요합니다. 주석은 제거하시고, 불확실하면 Mapper 계층으로 위임하는 것도 방법입니다.

가능하면 AvatarPost 엔티티 시그니처 공유해 주시면 정확한 매핑 코드로 패치 드리겠습니다.

🤖 Prompt for AI Agents
In
src/main/java/com/example/cp_main_be/domain/social/feed/dto/response/FeedResponse.java
around lines 20-27, remove the placeholder comment and replace the assumed
avatarPost.getImageUrl() call with the actual accessor for the AvatarPost entity
(e.g., getImage(), getImagePath(), getThumbnailUrl, etc.) or call a dedicated
mapper method (e.g., AvatarPostMapper.toFeedResponse(avatarPost)) if the field
name is uncertain; update the constructor arguments accordingly so the code
compiles, and if you cannot confirm the entity signature, add a TODO to delegate
mapping to a mapper and create that mapper to read the correct field.

import com.example.cp_main_be.domain.social.diary.domain.Diary;
import com.example.cp_main_be.domain.social.diary.domain.Repository.DiaryRepository;
import com.example.cp_main_be.domain.social.diary.dto.response.DiaryFeedItemResponse;
import com.example.cp_main_be.domain.social.feed.dto.response.FeedResponse;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

컴파일 오류 가능: DiaryRepository 패키지 경로 오타 의심

다른 리포지토리와 달리 domain.social.diary.domain.Repository.DiaryRepository 처럼 대문자 Repository 를 사용하고 있습니다. 일반적으로는 ...domain.social.diary.domain.repository.DiaryRepository 입니다. 아래처럼 수정해주세요.

-import com.example.cp_main_be.domain.social.diary.domain.Repository.DiaryRepository;
+import com.example.cp_main_be.domain.social.diary.domain.repository.DiaryRepository;
🤖 Prompt for AI Agents
In
src/main/java/com/example/cp_main_be/domain/social/feed/service/FeedService.java
around line 8, the import for DiaryRepository likely uses an incorrect package
segment with capital "Repository"; change the import to use the lowercase
package path
(com.example.cp_main_be.domain.social.diary.domain.repository.DiaryRepository),
verify the target package and class name exist, and update any other references
if necessary so the project compiles.

@lejuho lejuho merged commit 9d4234e into develop Aug 24, 2025
6 checks passed
Copy link
Copy Markdown
Member

@c5ln c5ln left a comment

Choose a reason for hiding this comment

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

okay

c5ln pushed a commit that referenced this pull request Aug 24, 2025
* ddd

* refactor: 피드 조회 api dto 수정 (#79)

* refactor: 피드 조회 api dto 수정

* spotless apply

* spotless apply

* spotless apply

* spotless apply

* ddd

---------

Co-authored-by: 이태경 <161710171+xoruddl@users.noreply.github.com>
@coderabbitai coderabbitai Bot mentioned this pull request Aug 24, 2025
Merged
@lejuho lejuho deleted the feat/75-피드-dto-수정 branch August 25, 2025 08:13
@coderabbitai coderabbitai Bot mentioned this pull request Aug 26, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

피드 dto 수정

3 participants