-
Notifications
You must be signed in to change notification settings - Fork 1
feat: 홈 화면 도서 선별 로직 구현 #66
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
Conversation
|
Warning Rate limit exceeded@hoonyworld has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 13 minutes and 43 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (7)
📝 WalkthroughWalkthrough홈 화면 조회 GET API를 도입하는 신규 기능이 추가되었습니다. 이 과정에서 홈 화면 데이터 제공을 위한 컨트롤러, 서비스, 유스케이스, DTO, 도메인/인프라 계층의 쿼리 및 VO가 신설·확장되었습니다. 일부 파일에서는 불필요한 import 정리와 코드 포맷팅도 함께 이루어졌습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant HomeController
participant HomeUseCase
participant HomeService
participant UserBookDomainService
participant UserBookRepository
Client->>HomeController: GET /api/home?limit=n
HomeController->>HomeUseCase: getUserHomeData(userId, limit)
HomeUseCase->>HomeService: getUserHomeData(userId, limit)
HomeService->>UserBookDomainService: findBooksWithRecordsOrderByLatest(userId)
UserBookDomainService->>UserBookRepository: findRecordedBooksSortedByRecency(userId)
UserBookRepository-->>UserBookDomainService: List<Pair<UserBook, LocalDateTime>>
HomeService->>UserBookDomainService: findBooksWithoutRecordsByStatusPriority(userId, needed, excludeIds)
UserBookDomainService->>UserBookRepository: findUnrecordedBooksSortedByPriority(userId, needed, excludeIds)
UserBookRepository-->>UserBookDomainService: List<UserBook>
HomeService-->>HomeUseCase: UserHomeResponse
HomeUseCase-->>HomeController: UserHomeResponse
HomeController-->>Client: 200 OK + UserHomeResponse
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Assessment against linked issues
Assessment against linked issues: Out-of-scope changes
Possibly related PRs
Suggested labels
✨ 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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed 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)
Other keywords and placeholders
Documentation and Community
|
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: 9
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (13)
apis/src/main/kotlin/org/yapp/apis/book/service/UserBookService.kt(2 hunks)apis/src/main/kotlin/org/yapp/apis/home/controller/HomeController.kt(1 hunks)apis/src/main/kotlin/org/yapp/apis/home/controller/HomeControllerApi.kt(1 hunks)apis/src/main/kotlin/org/yapp/apis/home/dto/response/UserHomeResponse.kt(1 hunks)apis/src/main/kotlin/org/yapp/apis/home/usecase/HomeUseCase.kt(1 hunks)apis/src/main/kotlin/org/yapp/apis/readingrecord/service/ReadingRecordService.kt(1 hunks)domain/src/main/kotlin/org/yapp/domain/readingrecord/ReadingRecord.kt(0 hunks)domain/src/main/kotlin/org/yapp/domain/userbook/UserBookDomainService.kt(2 hunks)domain/src/main/kotlin/org/yapp/domain/userbook/UserBookRepository.kt(2 hunks)domain/src/main/kotlin/org/yapp/domain/userbook/vo/UserBookWithLastRecordVO.kt(1 hunks)infra/src/main/kotlin/org/yapp/infra/userbook/repository/JpaUserBookQuerydslRepository.kt(2 hunks)infra/src/main/kotlin/org/yapp/infra/userbook/repository/impl/JpaUserBookQuerydslRepositoryImpl.kt(4 hunks)infra/src/main/kotlin/org/yapp/infra/userbook/repository/impl/UserBookRepositoryImpl.kt(2 hunks)
💤 Files with no reviewable changes (1)
- domain/src/main/kotlin/org/yapp/domain/readingrecord/ReadingRecord.kt
👮 Files not reviewed due to content moderation or server errors (2)
- apis/src/main/kotlin/org/yapp/apis/book/service/UserBookService.kt
- apis/src/main/kotlin/org/yapp/apis/home/dto/response/UserHomeResponse.kt
⏰ 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-validation
🔇 Additional comments (27)
apis/src/main/kotlin/org/yapp/apis/readingrecord/service/ReadingRecordService.kt (1)
15-18: 의존성 제거가 적절합니다.
BookDomainService의존성 제거로 서비스 간 결합도가 감소했습니다. 현재 메서드들에서 해당 의존성을 사용하지 않으므로 적절한 리팩토링입니다.domain/src/main/kotlin/org/yapp/domain/userbook/UserBookRepository.kt (2)
5-5: 적절한 임포트 추가입니다.새로운 메서드의 반환 타입을 위한
LocalDateTime임포트가 적절히 추가되었습니다.
30-30: 새로운 리포지토리 메서드가 잘 설계되었습니다.
findUserBooksWithLastRecord메서드는 사용자 책과 마지막 기록 시간을 함께 조회하는 명확한 목적을 가지고 있습니다. 메서드명과 파라미터가 직관적입니다.infra/src/main/kotlin/org/yapp/infra/userbook/repository/JpaUserBookQuerydslRepository.kt (2)
3-3: QueryDSL Tuple 임포트가 적절합니다.인프라 계층에서 QueryDSL의
Tuple을 사용하는 것은 적절한 선택입니다.
24-27: QueryDSL 인터페이스 확장이 적절합니다.새로운 메서드가 QueryDSL 패턴을 잘 따르고 있으며,
Tuple반환 타입은 인프라 계층에서 적절한 선택입니다. 도메인 변환은 구현체에서 처리될 것으로 보입니다.apis/src/main/kotlin/org/yapp/apis/home/usecase/HomeUseCase.kt (1)
9-13: Use Case 구조가 적절합니다.
@UseCase어노테이션과@Transactional(readOnly = true)설정이 적절하고, 의존성 주입도 올바르게 구현되었습니다.domain/src/main/kotlin/org/yapp/domain/userbook/UserBookDomainService.kt (2)
9-9: 적절한 VO 임포트입니다.새로운 도메인 서비스 메서드에서 사용할
UserBookWithLastRecordVO임포트가 적절히 추가되었습니다.
74-83: 도메인 서비스 메서드가 잘 구현되었습니다.새로운
findRecentReadingBooksWithLastRecord메서드는 도메인 서비스 패턴을 잘 따르고 있습니다:
- 리포지토리에 위임하여 데이터를 조회
- 도메인 객체로 적절히 변환
- 명확한 메서드명과 책임 분리
구현이 깔끔하고 도메인 계층의 역할에 적합합니다.
apis/src/main/kotlin/org/yapp/apis/home/controller/HomeControllerApi.kt (1)
1-48: 잘 구현된 API 인터페이스입니다.OpenAPI 문서화가 잘 되어 있고, 적절한 HTTP 상태 코드와 보안 처리가 구현되어 있습니다. 인증된 사용자의 UUID를 사용하는 것도 적절합니다.
apis/src/main/kotlin/org/yapp/apis/home/controller/HomeController.kt (1)
1-26: 깔끔한 컨트롤러 구현입니다.HomeControllerApi 인터페이스를 올바르게 구현하고, UseCase 계층으로 적절히 위임하고 있습니다. 관심사의 분리가 잘 되어 있습니다.
infra/src/main/kotlin/org/yapp/infra/userbook/repository/impl/JpaUserBookQuerydslRepositoryImpl.kt (1)
3-3: 적절한 import 및 QueryDSL 엔티티 추가입니다.새로운 기능에 필요한 import와 QueryDSL 엔티티 참조가 올바르게 추가되었습니다.
Also applies to: 13-13, 25-25
domain/src/main/kotlin/org/yapp/domain/userbook/vo/UserBookWithLastRecordVO.kt (2)
21-29: 강력한 유효성 검증이 잘 구현되었습니다.init 블록의 유효성 검증이 포괄적이고 적절합니다. 특히 createdAt과 updatedAt의 시간 순서 검증이 좋습니다.
31-51: 팩토리 메서드가 잘 설계되었습니다.companion object의 newInstance 메서드가 적절하게 구현되어 있고, null 체크와 예외 처리도 명확합니다. 도메인 객체로부터 안전하게 VO를 생성하는 좋은 패턴입니다.
apis/src/main/kotlin/org/yapp/apis/book/service/UserBookService.kt (5)
7-7: 새로운 import 추가가 적절합니다.홈 화면 기능을 위한 새로운 DTO와 VO 클래스들이 올바르게 import되었습니다.
Also applies to: 12-12, 17-18
54-60: 홈 화면용 최근 독서 도서 조회 메서드 구현이 잘 되었습니다.메서드 로직이 명확하고 적절한 fetch limit 설정(최소 10개, 요청의 2배)과 우선순위 기반 선택이 잘 구현되었습니다.
7-7: 새로운 import 문들이 적절하게 추가되었습니다.새로운 기능에 필요한 DTO와 VO 클래스들이 올바르게 import되었습니다.
Also applies to: 12-12, 17-18
54-60: 홈 화면용 최근 읽은 책 조회 메서드의 구현이 좋습니다.fetchLimit 계산 로직(최소 10권, 요청량의 2배)과 우선순위 선택 로직이 적절하게 분리되어 있어 가독성이 좋습니다.
54-60: 홈 화면용 최근 읽은 책 조회 메서드 구현이 적절합니다.메서드 로직이 명확하고, fetchLimit을 요청된 limit의 2배로 설정하여 우선순위 필터링 후에도 충분한 데이터를 확보하는 전략이 합리적입니다.
apis/src/main/kotlin/org/yapp/apis/home/dto/response/UserHomeResponse.kt (9)
1-6: 패키지 구조와 import가 적절합니다.홈 화면 응답을 위한 적절한 패키지 위치와 필요한 의존성들이 올바르게 import되었습니다.
7-9: private constructor 사용이 좋은 설계입니다.팩토리 메서드를 통한 객체 생성 제어가 잘 구현되어 있습니다.
10-30: 중첩 클래스 구조와 팩토리 메서드가 잘 설계되었습니다.RecentBookResponse 중첩 클래스의 구조가 명확하고, 도메인 VO에서 DTO로의 변환 로직이 적절합니다.
32-39: 컴패니언 객체 팩토리 메서드가 깔끔합니다.리스트 변환 로직이 간결하고 읽기 쉽게 구현되어 있습니다.
1-6: 패키지 구조와 import 문이 적절합니다.홈 관련 DTO를 별도 패키지로 구성하고 필요한 도메인 클래스를 올바르게 import했습니다.
7-9: private 생성자를 사용한 DTO 설계가 좋습니다.팩토리 메서드를 통한 객체 생성을 강제하여 데이터 일관성을 보장하는 좋은 패턴입니다.
10-30: 중첩 데이터 클래스 구조가 잘 설계되었습니다.RecentBookResponse가 책의 핵심 정보와 마지막 기록 시간을 적절히 포함하고 있으며, 팩토리 메서드를 통한 도메인 객체 매핑도 올바르게 구현되었습니다.
32-39: 팩토리 메서드 구현이 간결하고 명확합니다.도메인 VO 리스트를 DTO 리스트로 변환하는 로직이 함수형 프로그래밍 스타일로 깔끔하게 구현되었습니다.
7-39: 잘 설계된 DTO 구조입니다.다음과 같은 좋은 설계 패턴을 따르고 있습니다:
- Private 생성자와 companion object 팩토리 메서드 사용
- 도메인 객체에서 DTO로의 명확한 매핑
- 불변 데이터 구조 사용
- 중첩된 RecentBookResponse 클래스를 통한 응집성 있는 구조
코드가 깔끔하고 유지보수하기 좋습니다.
apis/src/main/kotlin/org/yapp/apis/book/service/UserBookService.kt
Outdated
Show resolved
Hide resolved
apis/src/main/kotlin/org/yapp/apis/home/dto/response/UserHomeResponse.kt
Outdated
Show resolved
Hide resolved
apis/src/main/kotlin/org/yapp/apis/readingrecord/service/ReadingRecordService.kt
Show resolved
Hide resolved
...src/main/kotlin/org/yapp/infra/userbook/repository/impl/JpaUserBookQuerydslRepositoryImpl.kt
Outdated
Show resolved
Hide resolved
infra/src/main/kotlin/org/yapp/infra/userbook/repository/impl/UserBookRepositoryImpl.kt
Outdated
Show resolved
Hide resolved
infra/src/main/kotlin/org/yapp/infra/userbook/repository/impl/UserBookRepositoryImpl.kt
Outdated
Show resolved
Hide resolved
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: 7
♻️ Duplicate comments (3)
apis/src/main/kotlin/org/yapp/apis/home/usecase/HomeUseCase.kt (1)
14-14: 하드코딩된 기본값을 상수로 분리하세요.기본값
3이 하드코딩되어 있습니다. 유지보수성 향상을 위해 상수로 분리하는 것을 고려해보세요.+companion object { + private const val DEFAULT_RECENT_BOOKS_LIMIT = 3 +} + fun getUserHomeData(userId: UUID, limit: Int = 3): UserHomeResponse { - fun getUserHomeData(userId: UUID, limit: Int = 3): UserHomeResponse { + fun getUserHomeData(userId: UUID, limit: Int = DEFAULT_RECENT_BOOKS_LIMIT): UserHomeResponse {infra/src/main/kotlin/org/yapp/infra/userbook/repository/impl/UserBookRepositoryImpl.kt (1)
55-56: 불필요한 리팩토링입니다.중간 변수
page도입이 기능적 가치를 제공하지 않으며, 이전 코드가 더 간결했습니다.- val page = jpaUserBookRepository.findUserBooksByDynamicCondition(userId, status, sort, pageable) - return page.map { it.toDomain() } + return jpaUserBookRepository.findUserBooksByDynamicCondition(userId, status, sort, pageable) + .map { it.toDomain() }infra/src/main/kotlin/org/yapp/infra/userbook/repository/impl/JpaUserBookQuerydslRepositoryImpl.kt (1)
94-113: 입력 매개변수 검증을 추가하세요.
limit매개변수에 대한 검증이 없어 음수나 과도하게 큰 값이 전달될 수 있습니다.override fun findUnrecordedBooksSortedByPriority( userId: UUID, excludeIds: Set<UUID>, limit: Int ): List<UserBookEntity> { + require(limit > 0) { "limit은 0보다 커야 합니다." } return queryFactory
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (16)
apis/src/main/kotlin/org/yapp/apis/book/controller/BookControllerApi.kt(2 hunks)apis/src/main/kotlin/org/yapp/apis/book/service/UserBookService.kt(2 hunks)apis/src/main/kotlin/org/yapp/apis/home/controller/HomeController.kt(1 hunks)apis/src/main/kotlin/org/yapp/apis/home/controller/HomeControllerApi.kt(1 hunks)apis/src/main/kotlin/org/yapp/apis/home/dto/response/UserHomeResponse.kt(1 hunks)apis/src/main/kotlin/org/yapp/apis/home/service/HomeService.kt(1 hunks)apis/src/main/kotlin/org/yapp/apis/home/usecase/HomeUseCase.kt(1 hunks)apis/src/main/resources/static/kakao-login.html(1 hunks)domain/src/main/kotlin/org/yapp/domain/userbook/UserBookDomainService.kt(2 hunks)domain/src/main/kotlin/org/yapp/domain/userbook/UserBookRepository.kt(2 hunks)domain/src/main/kotlin/org/yapp/domain/userbook/vo/HomeBookVO.kt(1 hunks)infra/src/main/kotlin/org/yapp/infra/userbook/repository/JpaUserBookQuerydslRepository.kt(2 hunks)infra/src/main/kotlin/org/yapp/infra/userbook/repository/JpaUserBookRepository.kt(0 hunks)infra/src/main/kotlin/org/yapp/infra/userbook/repository/dto/UserBookLastRecordProjection.kt(1 hunks)infra/src/main/kotlin/org/yapp/infra/userbook/repository/impl/JpaUserBookQuerydslRepositoryImpl.kt(3 hunks)infra/src/main/kotlin/org/yapp/infra/userbook/repository/impl/UserBookRepositoryImpl.kt(2 hunks)
💤 Files with no reviewable changes (1)
- infra/src/main/kotlin/org/yapp/infra/userbook/repository/JpaUserBookRepository.kt
🔇 Additional comments (20)
apis/src/main/resources/static/kakao-login.html (1)
120-126:accessToken→oauthToken변경은 스펙 정합성 측면에서 OK, 단 전역 영향도 다시 한번 점검 권장
body에 넘기는 JSON 키를accessToken에서oauthToken으로 바꾸신 부분은 서버 단 스펙 변경을 반영한 것으로 보입니다. 변경 자체에는 문제 없어 보이지만 다음 항목을 확인해 두시면 좋겠습니다.
- 프런트의 동일 기능(모바일·웹 등) 및 테스트 코드에서도 동일한 키로 수정되었는지.
- Apple 로그인(156 라인)의
idToken키와 서버 기대값이 일관되는지 ‑ 서버가oauthToken만 받도록 단일화했다면 함께 맞춰야 합니다.- 백엔드 브랜치가 이미
oauthToken필드로 파싱하도록 배포되었는지(롤백 시 클라이언트가 깨질 수 있음).큰 문제는 없으므로 승인하지만, 위 영향도 체크 후 머지하시길 권장합니다.
apis/src/main/kotlin/org/yapp/apis/book/service/UserBookService.kt (2)
7-7: Import 구문 정리가 적절히 이루어졌습니다.UpsertUserBookRequest가 다른 request DTO들과 함께 그룹화되어 가독성이 향상되었습니다.
16-16: Wildcard import 사용이 적절합니다.UUID만 사용하는 상황에서 wildcard import로 변경한 것이 코드를 간결하게 만들었습니다.
infra/src/main/kotlin/org/yapp/infra/userbook/repository/dto/UserBookLastRecordProjection.kt (1)
1-11: QueryDSL projection DTO가 잘 설계되었습니다.
@QueryProjection어노테이션과 data class 구조가 적절하게 구성되어 있으며, 프로퍼티 명명도 명확합니다. 홈 화면 기능에서 최근 읽은 책 데이터를 효율적으로 조회하는 데 적합한 구조입니다.apis/src/main/kotlin/org/yapp/apis/home/usecase/HomeUseCase.kt (1)
15-16: 입력값 검증 로직이 적절합니다.
coerceIn(1, 10)을 사용한 limit 범위 제한이 올바르게 구현되었습니다.domain/src/main/kotlin/org/yapp/domain/userbook/UserBookRepository.kt (1)
30-36: 새로운 메서드들이 잘 설계되었습니다.홈 화면 기능을 위한 두 개의 새로운 메서드가 적절하게 추가되었습니다:
findRecordedBooksSortedByRecency: 읽기 기록이 있는 책들을 최근 순으로 조회findUnrecordedBooksSortedByPriority: 읽기 기록이 없는 책들을 우선순위별로 조회하며 제외할 ID 목록을 받아 중복을 방지메서드 시그니처가 명확하고 용도에 적합합니다.
apis/src/main/kotlin/org/yapp/apis/home/controller/HomeControllerApi.kt (1)
1-55: API 인터페이스가 잘 설계되었습니다.홈 화면 조회 API 인터페이스가 다음과 같이 우수하게 구현되었습니다:
- Swagger/OpenAPI 문서화가 포괄적이고 한국어로 명확하게 작성됨
- RESTful 설계 원칙을 준수하며 적절한 HTTP 응답 코드 정의
@AuthenticationPrincipal을 통한 사용자 인증 처리가 적절함- limit 파라미터의 기본값과 설명이 명확함
- 에러 응답에 대한 스키마 정의가 포함됨
apis/src/main/kotlin/org/yapp/apis/home/controller/HomeController.kt (1)
24-25: 글로벌 예외 처리 핸들러가 이미 구현되어 있습니다
global-utils/src/main/kotlin/org/yapp/globalutils/exception/GlobalExceptionHandler.kt에@RestControllerAdvice기반의 전역 예외 처리기가 정의되어 있어, 컨트롤러 레이어에서 발생하는 BindException, CommonException, IllegalArgumentException, MethodArgumentNotValidException 등은 물론 모든 예외(Exception)까지 적절히 처리됩니다.
따라서 HomeController에 별도의 try–catch를 추가하지 않아도 됩니다.apis/src/main/kotlin/org/yapp/apis/home/service/HomeService.kt (1)
18-35: 로직 구현이 잘 되어 있습니다홈 화면용 도서 선택 로직이 명확하고 효율적으로 구현되어 있습니다:
- 독서 기록이 있는 도서를 우선 선택
- 부족한 경우 기록이 없는 도서로 보완
- 중복 제거를 위한 제외 ID 처리
infra/src/main/kotlin/org/yapp/infra/userbook/repository/JpaUserBookQuerydslRepository.kt (2)
8-9: 새로운 import 추가 승인
UserBookLastRecordProjectionimport가 새로운 메서드의 반환 타입을 지원하기 위해 적절히 추가되었습니다.
24-30: 저장소 메서드 설계가 적절합니다새로 추가된 메서드들이 다음과 같이 잘 설계되어 있습니다:
findRecordedBooksSortedByRecency: 기록이 있는 도서를 최신순으로 조회findUnrecordedBooksSortedByPriority: 기록이 없는 도서를 우선순위별로 조회하며 제외 ID와 제한 수를 지원메서드명이 명확하고 매개변수가 적절합니다.
domain/src/main/kotlin/org/yapp/domain/userbook/vo/HomeBookVO.kt (2)
7-29: 잘 설계된 도메인 값 객체
HomeBookVO가 다음과 같이 우수하게 설계되어 있습니다:
- private 생성자로 직접 인스턴스화 방지
- 포괄적인 유효성 검증 (빈 문자열, 날짜 일관성)
- 도메인 타입 사용으로 타입 안전성 확보
- 불변 설계로 데이터 무결성 보장
- 명확한 한국어 오류 메시지
32-51: 팩토리 메서드 구현이 우수합니다
newInstance팩토리 메서드가 적절히 구현되어 있습니다:
- null 체크를 통한 방어적 프로그래밍
- 명확한 오류 메시지
- UserBook 도메인 객체에서 안전한 변환
domain/src/main/kotlin/org/yapp/domain/userbook/UserBookDomainService.kt (3)
5-9: 적절한 import 추가
HomeBookVO와java.util.*import가 새로운 메서드를 지원하기 위해 적절히 추가되었습니다.
74-83: 독서 기록이 있는 도서 조회 로직이 우수합니다메서드 구현이 다음과 같이 잘 되어 있습니다:
- 저장소 메서드 호출이 적절함
- 구조 분해를 활용한 깔끔한 매핑
- HomeBookVO 팩토리 메서드 활용
85-102: 도메인 서비스 메서드 설계가 적절합니다기록이 없는 도서 조회 메서드가 다음과 같이 잘 구현되어 있습니다:
- 매개변수 전달이 정확함
updatedAt을lastRecordedAt으로 사용하는 것이 합리적인 대안- 도메인 VO로의 변환이 적절함
infra/src/main/kotlin/org/yapp/infra/userbook/repository/impl/UserBookRepositoryImpl.kt (1)
63-78: 새로운 메서드들의 구현이 올바릅니다.
findRecordedBooksSortedByRecency와findUnrecordedBooksSortedByPriority메서드들이 JPA 레포지토리에 올바르게 위임하고 도메인 객체로 적절히 매핑하고 있습니다.apis/src/main/kotlin/org/yapp/apis/home/dto/response/UserHomeResponse.kt (1)
7-39: 깔끔한 DTO 구현입니다.
- private 생성자와 companion object 팩토리 메서드를 사용한 제어된 객체 생성 방식이 적절합니다
lastRecordedAt필드가 non-nullable로 설계되어 일관성 있는 API 응답을 보장합니다- 도메인 객체에서 DTO로의 매핑 로직이 명확하고 올바릅니다
infra/src/main/kotlin/org/yapp/infra/userbook/repository/impl/JpaUserBookQuerydslRepositoryImpl.kt (2)
75-92: 기록이 있는 책을 조회하는 쿼리 로직이 올바릅니다.
findRecordedBooksSortedByRecency메서드에서 INNER JOIN 사용이 메서드명과 일치하며, 읽기 기록이 있는 책만을 조회하는 의도가 명확합니다.
115-145: 유틸리티 확장 함수들이 잘 구조화되어 있습니다.
- 각 확장 함수가 단일 책임을 가지고 있어 재사용성과 가독성이 좋습니다
statusPriorityOrder()메서드의 비즈니스 로직(읽는 중 > 완료 > 읽기 전 > 기타)이 명확합니다idNotIn함수에서 빈 컬렉션 처리가 적절합니다
domain/src/main/kotlin/org/yapp/domain/userbook/UserBookDomainService.kt
Show resolved
Hide resolved
infra/src/main/kotlin/org/yapp/infra/userbook/repository/JpaUserBookQuerydslRepository.kt
Show resolved
Hide resolved
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: 1
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (2)
apis/src/main/kotlin/org/yapp/apis/home/controller/HomeController.kt(1 hunks)apis/src/main/kotlin/org/yapp/apis/home/controller/HomeControllerApi.kt(1 hunks)
🔇 Additional comments (5)
apis/src/main/kotlin/org/yapp/apis/home/controller/HomeControllerApi.kt (4)
1-20: 패키지 구조와 import 구성이 적절합니다.필요한 Spring, OpenAPI, validation 관련 import들이 모두 포함되어 있고, 패키지 구조도 적절합니다.
21-23: API 태그와 매핑 경로가 명확합니다.
@Tag를 통한 API 그룹화와/api/home경로 매핑이 적절하게 구성되었습니다.
25-47: OpenAPI 문서화가 상세하고 완성도가 높습니다.API 요약, 설명, 그리고 각 응답 코드(200, 400, 404)에 대한 문서화가 잘 작성되어 있습니다. 각 응답에 적절한 스키마도 연결되어 있습니다.
48-56: 메서드 시그니처와 매개변수 검증이 적절합니다.
@AuthenticationPrincipal을 통한 사용자 인증 처리가 올바름limit매개변수에@Min(1) @Max(10)검증 어노테이션이 적절히 적용됨- 기본값 설정과 매개변수 문서화도 잘 구성됨
apis/src/main/kotlin/org/yapp/apis/home/controller/HomeController.kt (1)
14-19: 클래스 어노테이션과 의존성 주입이 적절합니다.
@Validated,@RestController,@RequestMapping어노테이션이 올바르게 설정되어 있고,HomeUseCase의 의존성 주입도 적절합니다.
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: 1
♻️ Duplicate comments (1)
domain/src/main/kotlin/org/yapp/domain/userbook/UserBookDomainService.kt (1)
85-102: Null 안전성이 개선되었습니다과거 리뷰에서 지적된
updatedAt!!사용 문제가 elvis operator와 예외 처리로 개선되었습니다. 현재 구현이 안전하지만, 추가 개선을 고려해볼 수 있습니다.더 방어적인 처리를 원한다면 다음과 같이
createdAt을 fallback으로 사용하는 것도 고려해보세요:lastRecordedAt = userBook.updatedAt ?: throw IllegalStateException("UserBook의 updatedAt이 null입니다: ${userBook.id}") +lastRecordedAt = userBook.updatedAt ?: userBook.createdAt + ?: throw IllegalStateException("UserBook의 updatedAt과 createdAt이 모두 null입니다: ${userbook.id}")
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (1)
domain/src/main/kotlin/org/yapp/domain/userbook/UserBookDomainService.kt(2 hunks)
🔇 Additional comments (2)
domain/src/main/kotlin/org/yapp/domain/userbook/UserBookDomainService.kt (2)
74-83: 잘 구현된 메서드입니다기록이 있는 책들을 조회하는 로직이 깔끔하게 구현되었습니다. repository 계층과의 분리도 적절하고, destructuring을 활용한 코드도 가독성이 좋습니다.
74-102: 잘 설계된 도메인 서비스 확장홈 화면 기능을 위한 두 메서드가 도메인 서비스 계층에 적절히 추가되었습니다. Repository 패턴과 VO 패턴을 일관성 있게 활용하고 있으며, 메서드명도 명확하여 코드의 가독성과 유지보수성이 우수합니다.
domain/src/main/kotlin/org/yapp/domain/userbook/UserBookDomainService.kt
Show resolved
Hide resolved
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: 6
♻️ Duplicate comments (4)
apis/src/main/kotlin/org/yapp/apis/book/controller/BookControllerApi.kt (2)
16-16: 와일드카드 임포트 대신 명시적 임포트 사용을 권장합니다.이전 리뷰에서도 동일한 지적이 있었습니다.
org.springframework.web.bind.annotation.*와일드카드 임포트는 코드 가독성을 떨어뜨리고 Kotlin 코딩 컨벤션에 위배됩니다.
27-27:java.util.*와일드카드 임포트는 불필요합니다.이전 리뷰에서 지적된 동일한 문제입니다. 현재 파일에서는
UUID만 사용되므로 명시적 임포트가 더 적절합니다.infra/src/main/kotlin/org/yapp/infra/userbook/repository/JpaUserBookQuerydslRepository.kt (1)
25-25: 페이지네이션 고려사항이 여전히 유효합니다.이전 리뷰에서 언급된 것처럼, 사용자가 많은 도서를 가지고 있는 경우 성능 이슈가 발생할 수 있습니다. 향후 최적화가 필요할 수 있습니다.
domain/src/main/kotlin/org/yapp/domain/userbook/UserBookDomainService.kt (1)
9-9: 구체적인 클래스 임포트를 권장합니다.이전 리뷰에서도 지적된 동일한 문제입니다.
-import java.util.* +import java.util.UUID
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (8)
apis/src/main/kotlin/org/yapp/apis/book/controller/BookControllerApi.kt(2 hunks)apis/src/main/kotlin/org/yapp/apis/book/service/UserBookService.kt(2 hunks)apis/src/main/kotlin/org/yapp/apis/home/controller/HomeController.kt(1 hunks)domain/src/main/kotlin/org/yapp/domain/userbook/UserBookDomainService.kt(2 hunks)domain/src/main/kotlin/org/yapp/domain/userbook/UserBookRepository.kt(2 hunks)infra/src/main/kotlin/org/yapp/infra/userbook/repository/JpaUserBookQuerydslRepository.kt(2 hunks)infra/src/main/kotlin/org/yapp/infra/userbook/repository/impl/JpaUserBookQuerydslRepositoryImpl.kt(3 hunks)infra/src/main/kotlin/org/yapp/infra/userbook/repository/impl/UserBookRepositoryImpl.kt(2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: intellij idea에서 와일드카드 임포트를 방지하는 설정 방법: file > settings > editor > code style > java > imports 탭에서 "c...
Learnt from: hoonyworld
PR: YAPP-Github/Reed-Server#31
File: apis/src/main/kotlin/org/yapp/apis/book/controller/BookControllerApi.kt:13-13
Timestamp: 2025-07-08T14:24:44.340Z
Learning: IntelliJ IDEA에서 와일드카드 임포트를 방지하는 설정 방법: File > Settings > Editor > Code Style > Java > Imports 탭에서 "Class count to use import with '*'" 값을 높게 설정(예: 500)하면 해당 임계값 이하에서는 와일드카드 임포트가 자동으로 사용되지 않음. 와일드카드 임포트 관련 리뷰 시 이 IDE 설정 방법도 함께 안내하면 좋음.
Applied to files:
apis/src/main/kotlin/org/yapp/apis/book/controller/BookControllerApi.kt
⏰ 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-validation
🔇 Additional comments (11)
apis/src/main/kotlin/org/yapp/apis/book/service/UserBookService.kt (2)
7-7: 명시적 import 추가 👍
UpsertUserBookRequest를 명시적으로 import한 것은 가독성을 높여주며 IDE-최적화에도 도움이 됩니다. 추가 조치 필요 없습니다.
52-53: 불필요한 공백 제거 👍잔여 공백을 정리해 코드가 더욱 깔끔해졌습니다.
domain/src/main/kotlin/org/yapp/domain/userbook/UserBookRepository.kt (2)
32-33: 새로운 메서드가 잘 설계되었습니다.홈 화면을 위한 도서 선별 로직에 필요한 메서드가 적절하게 정의되었습니다. 반환 타입도 명확하고 비즈니스 요구사항을 잘 반영합니다.
34-38: 메서드 설계가 비즈니스 로직을 잘 지원합니다.제외할 ID 목록과 제한 개수를 매개변수로 받는 설계가 홈 화면의 도서 선별 요구사항을 효과적으로 지원합니다.
infra/src/main/kotlin/org/yapp/infra/userbook/repository/JpaUserBookQuerydslRepository.kt (1)
27-31: QueryDSL 메서드 설계가 적절합니다.
excludeIds와limit매개변수를 통해 효율적인 쿼리 실행이 가능하도록 설계되었습니다.apis/src/main/kotlin/org/yapp/apis/home/controller/HomeController.kt (1)
23-30: 컨트롤러 구현이 잘 되어 있습니다.매개변수 검증 어노테이션이 적절히 적용되었고, HomeControllerApi 인터페이스를 올바르게 구현했습니다. HomeUseCase에 대한 위임도 적절합니다.
domain/src/main/kotlin/org/yapp/domain/userbook/UserBookDomainService.kt (2)
75-84: 도메인 로직이 잘 구현되었습니다.기록이 있는 도서를 최신순으로 조회하는 비즈니스 로직이 명확하게 구현되어 있습니다. HomeBookVO 변환도 적절합니다.
86-103: null 안전성 처리가 개선되었습니다.Line 100에서 Elvis 연산자와 적절한 예외 처리를 통해 null 안전성이 향상되었습니다. 이전 리뷰 피드백이 잘 반영되었습니다.
infra/src/main/kotlin/org/yapp/infra/userbook/repository/impl/UserBookRepositoryImpl.kt (1)
68-74: 새로운 메서드 구현이 적절합니다.
findRecordedBooksSortedByRecency메서드는 읽기 기록이 있는 도서를 최신순으로 조회하는 로직이 명확하게 구현되어 있습니다. JPA 레포지토리에서 반환된 projection을 도메인 객체로 적절히 매핑하고 있습니다.infra/src/main/kotlin/org/yapp/infra/userbook/repository/impl/JpaUserBookQuerydslRepositoryImpl.kt (2)
78-95: 쿼리 로직이 비즈니스 요구사항과 일치합니다.
findRecordedBooksSortedByRecency메서드는 읽기 기록이 있는 도서만을 대상으로 하므로 INNER JOIN 사용이 적절합니다. 그룹핑과 정렬 로직도 올바르게 구현되어 있습니다.
118-148: 확장 함수들이 코드 가독성을 향상시킵니다.QueryDSL 쿼리를 위한 확장 함수들이 잘 구조화되어 있습니다:
userIdEq,isNotDeleted: 기본적인 필터링 조건hasNoRecords: 서브쿼리를 사용한 읽기 기록 부재 확인idNotIn: 조건부 제외 로직statusPriorityOrder: 비즈니스 우선순위에 따른 정렬특히
statusPriorityOrder의 우선순위 설정(READING → COMPLETED → BEFORE_READING)이 PR 목표와 일치합니다.
| import org.yapp.domain.userbook.UserBookSortType | ||
| import java.util.UUID | ||
|
|
||
| import java.util.* |
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.
🧹 Nitpick (assertive)
와일드카드 import 대신 구체적 import 권장
java.util.* 와일드카드 사용은 필요 이상의 타입 노출로 컴파일 성능과 가독성 모두에 불리합니다. UUID 하나만 사용하므로 구체적 import로 교체해주세요.
-import java.util.*
+import java.util.UUID🤖 Prompt for AI Agents
In apis/src/main/kotlin/org/yapp/apis/book/service/UserBookService.kt at line
16, replace the wildcard import 'import java.util.*' with a specific import for
'java.util.UUID' since only UUID is used. This improves compile performance and
code clarity by limiting the imported types.
| import org.springframework.web.bind.annotation.RestController | ||
| import org.yapp.apis.home.dto.response.UserHomeResponse | ||
| import org.yapp.apis.home.usecase.HomeUseCase | ||
| import java.util.* |
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.
🧹 Nitpick (assertive)
구체적인 클래스 임포트를 권장합니다.
-import java.util.*
+import java.util.UUID📝 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.
| import java.util.* | |
| -import java.util.* | |
| +import java.util.UUID |
🤖 Prompt for AI Agents
In apis/src/main/kotlin/org/yapp/apis/home/controller/HomeController.kt at line
14, replace the wildcard import 'import java.util.*' with explicit imports of
only the specific classes from java.util that are actually used in this file.
This improves code clarity and avoids unnecessary imports.
| import org.springframework.data.domain.Pageable | ||
| import java.util.UUID | ||
| import java.time.LocalDateTime | ||
| import java.util.* |
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.
🧹 Nitpick (assertive)
구체적인 클래스 임포트를 권장합니다.
import java.util.* 대신 필요한 클래스만 명시적으로 임포트하는 것이 좋습니다.
-import java.util.*
+import java.util.UUID📝 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.
| import java.util.* | |
| -import java.util.* | |
| +import java.util.UUID |
🤖 Prompt for AI Agents
In domain/src/main/kotlin/org/yapp/domain/userbook/UserBookRepository.kt at line
6, replace the wildcard import 'import java.util.*' with explicit imports of
only the specific classes from java.util that are actually used in the file.
This improves code clarity and avoids unnecessary imports.
| override fun findUnrecordedBooksSortedByPriority( | ||
| userId: UUID, | ||
| excludeIds: Set<UUID>, | ||
| limit: Int | ||
| ): List<UserBookEntity> { | ||
| return queryFactory | ||
| .selectFrom(userBook) | ||
| .where( | ||
| userBook.userIdEq(userId), | ||
| userBook.isNotDeleted(), | ||
| userBook.hasNoRecords(), | ||
| userBook.idNotIn(excludeIds) | ||
| ) | ||
| .orderBy( | ||
| statusPriorityOrder(), | ||
| userBook.updatedAt.desc() | ||
| ) | ||
| .limit(limit.toLong()) | ||
| .fetch() | ||
| } |
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.
🛠️ Refactor suggestion
매개변수 검증을 추가하고 성능을 고려하세요.
쿼리 로직은 올바르지만 몇 가지 개선사항이 필요합니다:
limit매개변수에 대한 검증이 없습니다- 대용량 데이터에서 성능 이슈가 발생할 수 있습니다
override fun findUnrecordedBooksSortedByPriority(
userId: UUID,
excludeIds: Set<UUID>,
limit: Int
): List<UserBookEntity> {
+ require(limit > 0) { "limit은 0보다 커야 합니다." }
+ require(limit <= 1000) { "limit은 1000 이하여야 합니다." }
+
return queryFactory
.selectFrom(userBook)
.where(
userBook.userIdEq(userId),
userBook.isNotDeleted(),
userBook.hasNoRecords(),
userBook.idNotIn(excludeIds)
)
.orderBy(
statusPriorityOrder(),
userBook.updatedAt.desc()
)
.limit(limit.toLong())
.fetch()
}또한 다음 인덱스 생성을 고려해보세요:
user_book(user_id, deleted_at, status, updated_at)reading_record(user_book_id)
📝 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.
| override fun findUnrecordedBooksSortedByPriority( | |
| userId: UUID, | |
| excludeIds: Set<UUID>, | |
| limit: Int | |
| ): List<UserBookEntity> { | |
| return queryFactory | |
| .selectFrom(userBook) | |
| .where( | |
| userBook.userIdEq(userId), | |
| userBook.isNotDeleted(), | |
| userBook.hasNoRecords(), | |
| userBook.idNotIn(excludeIds) | |
| ) | |
| .orderBy( | |
| statusPriorityOrder(), | |
| userBook.updatedAt.desc() | |
| ) | |
| .limit(limit.toLong()) | |
| .fetch() | |
| } | |
| override fun findUnrecordedBooksSortedByPriority( | |
| userId: UUID, | |
| excludeIds: Set<UUID>, | |
| limit: Int | |
| ): List<UserBookEntity> { | |
| require(limit > 0) { "limit은 0보다 커야 합니다." } | |
| require(limit <= 1000) { "limit은 1000 이하여야 합니다." } | |
| return queryFactory | |
| .selectFrom(userBook) | |
| .where( | |
| userBook.userIdEq(userId), | |
| userBook.isNotDeleted(), | |
| userBook.hasNoRecords(), | |
| userBook.idNotIn(excludeIds) | |
| ) | |
| .orderBy( | |
| statusPriorityOrder(), | |
| userBook.updatedAt.desc() | |
| ) | |
| .limit(limit.toLong()) | |
| .fetch() | |
| } |
🤖 Prompt for AI Agents
In
infra/src/main/kotlin/org/yapp/infra/userbook/repository/impl/JpaUserBookQuerydslRepositoryImpl.kt
around lines 97 to 116, add validation to ensure the 'limit' parameter is
positive and within a reasonable range to prevent misuse. To improve performance
on large datasets, consider optimizing the query or adding pagination if
applicable. Also, recommend creating database indexes on (user_id, deleted_at,
status, updated_at) in the user_book table and on user_book_id in the
reading_record table to speed up query execution.
infra/src/main/kotlin/org/yapp/infra/userbook/repository/impl/UserBookRepositoryImpl.kt
Show resolved
Hide resolved
infra/src/main/kotlin/org/yapp/infra/userbook/repository/JpaUserBookQuerydslRepository.kt
Show resolved
Hide resolved
|


🔗 관련 이슈
📘 작업 유형
📙 작업 내역
1. 홈 화면 도서 선별 로직 구현
2. 데이터베이스 쿼리 최적화
🧪 테스트 내역
🎨 스크린샷 또는 시연 영상 (선택)
✅ PR 체크리스트
💬 추가 설명 or 리뷰 포인트 (선택)
Summary by CodeRabbit
Summary by CodeRabbit
신규 기능
문서화