-
Notifications
You must be signed in to change notification settings - Fork 2
perf: post 커서 기반 페이징 #255
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
perf: post 커서 기반 페이징 #255
Conversation
기존 '카테고리별 삭제 되지 않은 모든 게시물 조회'에서 mongoTemplate으로 커서 기반 페이징 로직 구현
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the 📝 WalkthroughWalkthrough카테고리별 게시글 조회에 커서 기반 페이지네이션을 도입. 컨트롤러에 신규 GET /posts/category/cursor 엔드포인트 추가, 서비스·리포지토리·DTO 신설. 기존 컨트롤러 생성자 시그니처가 PostCursorService 주입으로 변경됨. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Client
participant Controller as PostController
participant CursorSvc as PostCursorService
participant BlockSvc as BlockService
participant Repo as PostReadRepository
participant PostSvc as PostService
Client->>Controller: GET /posts/category/cursor?category&cursor&limit
Controller->>CursorSvc: getAllPostsByCursorIdOnly(category, cursor, limit)
CursorSvc->>BlockSvc: getBlockedUsers()
BlockSvc-->>CursorSvc: List<ObjectId>
CursorSvc->>CursorSvc: 검증/파싱(cursor → ObjectId?)
CursorSvc->>Repo: findByCategoryWithIdCursor(cat, blocked, lastId, limit)
Repo-->>CursorSvc: List<PostEntity>(limit+1까지)
CursorSvc->>CursorSvc: hasNext 계산 및 trim, nextCursor 산출
CursorSvc->>PostSvc: getPostListResponseDtos(batch)
PostSvc-->>CursorSvc: List<PostDetailResponseDTO>
CursorSvc-->>Controller: CursorPageResponse<PostDetailResponseDTO>
Controller-->>Client: 200 OK SingleResponse
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
✨ Finishing Touches🧪 Generate unit tests
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. Comment |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
🧹 Nitpick comments (5)
src/main/java/inu/codin/codin/domain/post/dto/response/CursorPageResponse.java (1)
8-14: 마지막 페이지에서 nextCursor를 숨겨 응답 단순화nextCursor가 null일 때 직렬화 제외하면 클라이언트 처리 비용이 줄어듭니다. 아래처럼 NON_NULL 적용을 권장합니다.
package inu.codin.codin.domain.post.dto.response; +import com.fasterxml.jackson.annotation.JsonInclude; import lombok.Builder; import lombok.Getter; import java.util.List; @Getter @Builder +@JsonInclude(JsonInclude.Include.NON_NULL) public class CursorPageResponse<T> { private final List<T> items; private final String nextCursor; // 다음 페이지 커서 값 private final boolean hasNext; }src/main/java/inu/codin/codin/domain/post/controller/PostController.java (1)
20-21: 검증 애노테이션 import 정리 제안위 변경을 적용한다면 아래 import가 필요합니다.
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Pattern;src/main/java/inu/codin/codin/domain/post/repository/PostReadRepository.java (3)
39-46: 주석과 실제 조건이 불일치합니다.
- Line 39: “차단 유저가 없을 때” → 조건은 blockedUsers가 존재할 때입니다.
- Line 44: “첫번째 페이지 조회” → cursorId가 있을 때는 첫 페이지가 아닙니다.
- // 차단 유저가 없을 때 + // 차단 유저가 있을 때 ... - // 첫번째 페이지 조회 + // 커서가 있을 때(후속 페이지)
48-53: 인덱스 설계 조정 제안(쿼리와 정렬에 정합성 맞추기)현재 쿼리는 _id 기준 내림차순 정렬만 사용합니다. 인덱스도 이에 맞추는 편이 효율적입니다.
- 제안 인덱스(부분 인덱스 포함, 필드명은 실제 스키마에 맞게 조정 필요):
// deleted_at 필드가 null 또는 미존재이고, postStatus가 ACTIVE인 문서만 대상 db.posts.createIndex( { postStatus: 1, postCategory: 1, _id: -1 }, { partialFilterExpression: { $and: [ { postStatus: { $eq: "ACTIVE" } }, { $or: [ { deleted_at: null }, { deleted_at: { $exists: false } } ] } ]}} )
- 만약 created_at 정렬을 실제로 사용할 계획이라면, 서비스/레포지토리 정렬과 범위 조건을 created_at + _id로 바꾸고, 인덱스도
{ postStatus:1, postCategory:1, created_at:-1, _id:-1 }로 일치시키세요.
21-53: Criteria 조합 간소화(선택)andOperator 중첩 대신 Query.addCriteria로 단계별 추가가 읽기 쉽습니다. 기능 동일합니다.
- Criteria base = new Criteria().andOperator( - new Criteria().orOperator( - Criteria.where("deletedAt").is(null), - Criteria.where("deletedAt").exists(false) - ), - Criteria.where("postStatus").is("ACTIVE"), - Criteria.where("postCategory").is(postCategory) - ); + Criteria base = new Criteria(); + base.orOperator( + Criteria.where("deletedAt").is(null), + Criteria.where("deletedAt").exists(false) + ); + base.and("postStatus").is("ACTIVE"); + base.and("postCategory").is(postCategory); ... - Query query = new Query(base) + Query query = new Query().addCriteria(base) .with(Sort.by(Sort.Direction.DESC, "_id")) .limit(limit + 1);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/main/java/inu/codin/codin/domain/post/controller/PostController.java(3 hunks)src/main/java/inu/codin/codin/domain/post/dto/response/CursorPageResponse.java(1 hunks)src/main/java/inu/codin/codin/domain/post/repository/PostReadRepository.java(1 hunks)src/main/java/inu/codin/codin/domain/post/service/PostCursorService.java(1 hunks)
🔇 Additional comments (2)
src/main/java/inu/codin/codin/domain/post/dto/response/CursorPageResponse.java (1)
8-14: DTO 구성 깔끔합니다.불변 필드 + Lombok 빌더 선택 적절합니다.
src/main/java/inu/codin/codin/domain/post/service/PostCursorService.java (1)
36-40: IllegalArgumentException 핸들러 확인 완료: 400 응답 반환
GlobalExceptionHandler.java에@ExceptionHandler(IllegalArgumentException.class)가 정의되어HttpStatus.BAD_REQUEST를 반환하도록 설정되어 있어 예외 매핑이 정상입니다.
src/main/java/inu/codin/codin/domain/post/controller/PostController.java
Show resolved
Hide resolved
src/main/java/inu/codin/codin/domain/post/repository/PostReadRepository.java
Show resolved
Hide resolved
src/main/java/inu/codin/codin/domain/post/repository/PostReadRepository.java
Show resolved
Hide resolved
src/main/java/inu/codin/codin/domain/post/service/PostCursorService.java
Show resolved
Hide resolved
|
기존 PR 빠르게 병합이후 위 PR 내용 병합진행하겠습니다. |
#️⃣ 연관된 이슈
#254 #242
📝 작업 내용
기존 '카테고리별 삭제 되지 않은 모든 게시물 조회'에서 mongoTemplate으로 커서 기반 페이징 로직 구현했습니다.
테스트 환경
'에브리 타임'과 같이 많은 게시물을 가지고 페이징을 해야하는 경우를 상정해서 테스트를 진행했습니다.
100만개 테스트 데이터 세트
로컬 m2 air 16gb 환경
10개 스레드 - Inifite
평균 latency : 3509ms -> 85ms 로 향상
초당 Throughput : 2.8/sec -> 115/sec 으로 향상
mongosh 복합 인덱스 추가 필요
스크린샷 (선택)
일반 limit, skip 기반 페이징 - page 0
cursor 기반 페이징 - page 0
cursor 기반 페이징 - page 1000
💬 리뷰 요구사항(선택)