Skip to content
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
b06d8f5
[BOOK-163] feat: infra - userbook에서 id와 userId로 도서 등록 여부 검증 메서드 구현 (#52)
minwoo1999 Jul 24, 2025
bf03a67
[BOOK-163] feat: infra - Jparepository 구현 (#52)
minwoo1999 Jul 24, 2025
48ab1d1
[BOOK-163] feat: infra - querydslrepository 구현 (#52)
minwoo1999 Jul 24, 2025
0a33bd5
[BOOK-163] feat: infra - 독서기록 Entity 구현 (#52)
minwoo1999 Jul 24, 2025
cc64171
[BOOK-163] feat: domain - 독서기록 Entity 구현 (#52)
minwoo1999 Jul 24, 2025
3881e6d
[BOOK-163] feat: domain - 독서기록 entity mapper 구현 (#52)
minwoo1999 Jul 24, 2025
5f665f8
[BOOK-163] feat: domain - 독서기록 도메인서비스 구현 (#52)
minwoo1999 Jul 24, 2025
6050cd2
[BOOK-163] feat: domain - 독서기록 domain VO 구현 (#52)
minwoo1999 Jul 24, 2025
6584426
[BOOK-163] feat: domain - 통합 interface 구현 (#52)
minwoo1999 Jul 24, 2025
c50f65c
[BOOK-163] feat: apis - 독서기록 usecase구현 (#52)
minwoo1999 Jul 24, 2025
c2cd5fe
[BOOK-163] feat: apis - 독서기록 service 구현 (#52)
minwoo1999 Jul 24, 2025
7d2172d
[BOOK-163] feat: apis - 독서기록 dto 구현 (#52)
minwoo1999 Jul 24, 2025
e947c77
[BOOK-163] feat: apis - 독서기록 controller 구현 (#52)
minwoo1999 Jul 24, 2025
dbb12b3
[BOOK-163] feat: apis - merge conflict 해결 (#52)
minwoo1999 Jul 24, 2025
c12c74a
[BOOK-163] feat: apis - userbook exception 처리 (#52)
minwoo1999 Jul 24, 2025
78e9e6c
[BOOK-163] refactor: apis - 코드컨벤션에 맞게 리팩토링 (#52)
minwoo1999 Jul 24, 2025
c4db4f4
Merge branch 'develop' into BOOK-163-feature/#52
minwoo1999 Jul 25, 2025
c6853af
[BOOK-163] refactor: apis - 1차 코드리뷰 반영(#52)
minwoo1999 Jul 26, 2025
6786eac
[BOOK-163] refactor: infra 1차 코드리뷰 반영(#52)
minwoo1999 Jul 26, 2025
49104b6
[BOOK-163] refactor: domain 1차 코드리뷰 반영(#52)
minwoo1999 Jul 26, 2025
d606fdc
[BOOK-163] refactor: domain bookId isbn -> UUID로 변경 (#52)
minwoo1999 Jul 26, 2025
93c3875
[BOOK-163] feat: apis 내서재 총 책개수 컬럼추가 (#52)
minwoo1999 Jul 26, 2025
fb6fd8d
[BOOK-163] feat: domain tag 중간테이블 기능개발 (#52)
minwoo1999 Jul 26, 2025
221d774
[BOOK-163] feat: infra tag 중간테이블 기능개발 (#52)
minwoo1999 Jul 26, 2025
413f117
[BOOK-163] feat: infra 외부API imagePage가져오도록 설정 (#52)
minwoo1999 Jul 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import org.yapp.apis.auth.dto.response.RefreshTokenResponse
name = "DeleteTokenRequest",
description = "Request DTO for deleting a refresh token"
)
data class DeleteTokenRequest(
data class DeleteTokenRequest private constructor(
@field:NotBlank(message = "Refresh token must not be blank.")
@Schema(description = "Refresh token to be deleted", example = "eyJhbGciOiJIUz...")
val refreshToken: String? = null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import java.util.*
name = "FindUserIdentityRequest",
description = "Request DTO to retrieve user identity information using userId"
)
data class FindUserIdentityRequest(
data class FindUserIdentityRequest private constructor(
@Schema(
description = "User ID (UUID format)",
example = "a1b2c3d4-e5f6-7890-1234-56789abcdef0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import java.util.*
name = "TokenGenerateRequest",
description = "DTO containing information required to save the generated refresh token"
)
data class TokenGenerateRequest(
data class TokenGenerateRequest private constructor(
@field:NotNull(message = "userId must not be null")
@Schema(description = "User ID", example = "f6b7d490-1b1a-4b9f-8e8e-27f8e3a5dafa")
val userId: UUID? = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import java.util.UUID
name = "UserBooksByIsbnsRequest",
description = "Request DTO for finding user books by user ID and a list of ISBNs"
)
data class UserBooksByIsbnsRequest(
data class UserBooksByIsbnsRequest private constructor(
@Schema(
description = "사용자 ID",
example = "1"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.yapp.apis.book.controller

import jakarta.validation.Valid
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.domain.Sort
import org.springframework.data.web.PageableDefault
Expand All @@ -23,6 +22,7 @@ import org.yapp.apis.book.dto.response.UserBookPageResponse
import org.yapp.apis.book.dto.response.UserBookResponse
import org.yapp.apis.book.usecase.BookUseCase
import org.yapp.domain.userbook.BookStatus
import org.yapp.domain.userbook.UserBookSortType
import java.util.UUID

@RestController
Expand Down Expand Up @@ -62,7 +62,7 @@ class BookController(
override fun getUserLibraryBooks(
@AuthenticationPrincipal userId: UUID,
@RequestParam(required = false) status: BookStatus?,
@RequestParam(required = false) sort: String?,
@RequestParam(required = false) sort: UserBookSortType?,
@PageableDefault(size = 10, sort = ["createdAt"], direction = Sort.Direction.DESC)
pageable: Pageable
): ResponseEntity<UserBookPageResponse> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import org.yapp.apis.book.dto.response.BookSearchResponse
import org.yapp.apis.book.dto.response.UserBookPageResponse
import org.yapp.apis.book.dto.response.UserBookResponse
import org.yapp.domain.userbook.BookStatus
import org.yapp.domain.userbook.UserBookSortType
import org.yapp.globalutils.exception.ErrorResponse
import java.util.UUID

Expand Down Expand Up @@ -140,7 +141,7 @@ interface BookControllerApi {
fun getUserLibraryBooks(
@AuthenticationPrincipal userId: UUID,
@RequestParam(required = false) status: BookStatus?,
@RequestParam(required = false) sort: String?,
@RequestParam(required = false) sort: UserBookSortType?,
@PageableDefault(size = 10, sort = ["createdAt"], direction = Sort.Direction.DESC)
pageable: Pageable
): ResponseEntity<UserBookPageResponse>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ data class BookDetailResponse private constructor(
val cover: String,
val categoryId: Int?,
val categoryName: String?,
val publisher: String
val publisher: String,
) {
companion object {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.yapp.apis.book.exception

import org.springframework.http.HttpStatus
import org.yapp.globalutils.exception.BaseErrorCode

enum class UserBookErrorCode(
private val status: HttpStatus,
private val code: String,
private val message: String
) : BaseErrorCode {
USER_BOOK_NOT_FOUND(HttpStatus.NOT_FOUND, "USER_BOOK_001", "사용자의 책을 찾을 수 없습니다.");

override fun getHttpStatus(): HttpStatus = status
override fun getCode(): String = code
override fun getMessage(): String = message
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.yapp.apis.book.exception

import org.yapp.globalutils.exception.CommonException

class UserBookNotFoundException(
errorCode: UserBookErrorCode,
message: String? = null
) : CommonException(errorCode, message)
23 changes: 14 additions & 9 deletions apis/src/main/kotlin/org/yapp/apis/book/service/UserBookService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ import org.yapp.apis.auth.dto.request.UserBooksByIsbnsRequest
import org.yapp.apis.book.dto.response.UserBookPageResponse
import org.yapp.apis.book.dto.response.UserBookResponse
import org.yapp.apis.book.dto.request.UpsertUserBookRequest
import org.yapp.apis.book.exception.UserBookErrorCode
import org.yapp.apis.book.exception.UserBookNotFoundException
import org.yapp.domain.userbook.BookStatus
import org.yapp.domain.userbook.UserBook
import org.yapp.domain.userbook.UserBookDomainService
import org.yapp.domain.userbook.vo.UserBookInfoVO
import org.yapp.domain.userbook.vo.UserBookStatusCountsVO
import org.yapp.domain.userbook.UserBookSortType
import java.util.UUID


Expand All @@ -21,6 +23,7 @@ class UserBookService(
fun upsertUserBook(upsertUserBookRequest: UpsertUserBookRequest): UserBookResponse {
val userBookInfoVO = userBookDomainService.upsertUserBook(
upsertUserBookRequest.validUserId(),
upsertUserBookRequest.validBookId(),
upsertUserBookRequest.validBookIsbn(),
upsertUserBookRequest.validBookTitle(),
upsertUserBookRequest.validBookAuthor(),
Expand All @@ -31,13 +34,15 @@ class UserBookService(
return UserBookResponse.from(userBookInfoVO)
}

fun findAllUserBooks(userId: UUID): List<UserBookResponse> {
val userBooks: List<UserBookInfoVO> = userBookDomainService.findAllUserBooks(userId)
return userBooks.map { userBook: UserBookInfoVO ->
UserBookResponse.from(userBook)
}
fun validateUserBookExists(userId: UUID, userBookId: UUID): UserBook {
return userBookDomainService.findByIdAndUserId(userBookId, userId)
?: throw UserBookNotFoundException(
UserBookErrorCode.USER_BOOK_NOT_FOUND,
"User book not found with id: $userBookId and userId: $userId"
)
}
Comment on lines +37 to 43
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

예외 메시지에서 민감한 정보 노출을 고려해주세요.

메서드 구현은 적절하지만 다음 사항들을 검토해보시기 바랍니다:

  1. 보안 고려사항: 예외 메시지에 userBookIduserId가 직접 노출되고 있습니다. 로그나 클라이언트 응답에서 이러한 내부 ID가 노출될 수 있습니다.
  2. 파라미터 순서: 도메인 서비스의 findByIdAndUserId(userBookId, userId)와 달리 현재 메서드는 (userId, userBookId) 순서를 사용합니다. 일관성을 위해 순서를 맞추는 것을 고려해보세요.

다음과 같이 수정을 제안합니다:

-    fun validateUserBookExists(userId: UUID, userBookId: UUID): UserBook {
-        return userBookDomainService.findByIdAndUserId(userBookId, userId)
+    fun validateUserBookExists(userBookId: UUID, userId: UUID): UserBook {
+        return userBookDomainService.findByIdAndUserId(userBookId, userId)
             ?: throw UserBookNotFoundException(
                 UserBookErrorCode.USER_BOOK_NOT_FOUND,
-                "User book not found with id: $userBookId and userId: $userId"
+                "User book not found"
             )
     }
📝 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
fun validateUserBookExists(userId: UUID, userBookId: UUID): UserBook {
return userBookDomainService.findByIdAndUserId(userBookId, userId)
?: throw UserBookNotFoundException(
UserBookErrorCode.USER_BOOK_NOT_FOUND,
"User book not found with id: $userBookId and userId: $userId"
)
}
fun validateUserBookExists(userBookId: UUID, userId: UUID): UserBook {
return userBookDomainService.findByIdAndUserId(userBookId, userId)
?: throw UserBookNotFoundException(
UserBookErrorCode.USER_BOOK_NOT_FOUND,
"User book not found"
)
}
🤖 Prompt for AI Agents
In apis/src/main/kotlin/org/yapp/apis/book/service/UserBookService.kt around
lines 35 to 41, the exception message exposes sensitive internal IDs (userBookId
and userId), which should be avoided for security reasons. Modify the exception
message to exclude these IDs or replace them with a generic message. Also,
adjust the parameter order in the validateUserBookExists method to match the
domain service's findByIdAndUserId method by passing userBookId first and then
userId for consistency.



fun findAllByUserIdAndBookIsbnIn(userBooksByIsbnsRequest: UserBooksByIsbnsRequest): List<UserBookResponse> {
val userBooks = userBookDomainService.findAllByUserIdAndBookIsbnIn(
userBooksByIsbnsRequest.validUserId(),
Expand All @@ -49,7 +54,7 @@ class UserBookService(
private fun findUserBooksByDynamicCondition(
userId: UUID,
status: BookStatus?,
sort: String?,
sort: UserBookSortType?,
pageable: Pageable
): Page<UserBookResponse> {
val page = userBookDomainService.findUserBooksByDynamicCondition(userId, status, sort, pageable)
Expand All @@ -59,7 +64,7 @@ class UserBookService(
fun findUserBooksByDynamicConditionWithStatusCounts(
userId: UUID,
status: BookStatus?,
sort: String?,
sort: UserBookSortType?,
pageable: Pageable
): UserBookPageResponse {
val userBookResponsePage = findUserBooksByDynamicCondition(userId, status, sort, pageable)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import org.yapp.apis.book.service.BookManagementService
import org.yapp.apis.book.service.BookQueryService
import org.yapp.apis.book.service.UserBookService
import org.yapp.domain.userbook.BookStatus
import org.yapp.domain.userbook.UserBookSortType
import org.yapp.globalutils.annotation.UseCase
import java.util.UUID

Expand Down Expand Up @@ -63,7 +64,7 @@ class BookUseCase(
val upsertUserBookRequest = UpsertUserBookRequest.of(
userId = userId,
bookCreateResponse,
status = request.bookStatus
status = request.bookStatus,
)
val userBookResponse = userBookService.upsertUserBook(upsertUserBookRequest)

Expand All @@ -73,7 +74,7 @@ class BookUseCase(
fun getUserLibraryBooks(
userId: UUID,
status: BookStatus?,
sort: String?,
sort: UserBookSortType?,
pageable: Pageable
): UserBookPageResponse {
userAuthService.validateUserExists(userId)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package org.yapp.apis.readingrecord.controller

import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.domain.Sort
import org.springframework.data.web.PageableDefault
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.security.core.annotation.AuthenticationPrincipal
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
import org.yapp.apis.readingrecord.dto.request.CreateReadingRecordRequest
import org.yapp.apis.readingrecord.dto.response.ReadingRecordResponse
import org.yapp.apis.readingrecord.usecase.ReadingRecordUseCase
import org.yapp.domain.readingrecord.ReadingRecordSortType
import java.util.UUID
import jakarta.validation.Valid

@RestController
Copy link
Member

Choose a reason for hiding this comment

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

여기에 RequestMapping 추가해주시면 감사하겠습니다! (가독성을 위해서)

@RequestMapping("/api/v1/reading-records")
class ReadingRecordController(
private val readingRecordUseCase: ReadingRecordUseCase
) : ReadingRecordControllerApi {

@PostMapping("/{userBookId}")
override fun createReadingRecord(
@AuthenticationPrincipal userId: UUID,
@PathVariable userBookId: UUID,
@Valid @RequestBody request: CreateReadingRecordRequest
): ResponseEntity<ReadingRecordResponse> {
val response = readingRecordUseCase.createReadingRecord(
userId = userId,
userBookId = userBookId,
request = request
)
return ResponseEntity.status(HttpStatus.CREATED).body(response)
}

@GetMapping("/{userBookId}")
override fun getReadingRecords(
@AuthenticationPrincipal userId: UUID,
@PathVariable userBookId: UUID,
@RequestParam(required = false) sort: ReadingRecordSortType?,
@PageableDefault(size = 10, sort = ["createdAt"], direction = Sort.Direction.DESC)
pageable: Pageable
): ResponseEntity<Page<ReadingRecordResponse>> {
val response = readingRecordUseCase.getReadingRecordsByUserBookId(
userId = userId,
userBookId = userBookId,
sort = sort,
pageable = pageable
)
return ResponseEntity.ok(response)
}
Comment on lines +25 to +59
Copy link
Member

Choose a reason for hiding this comment

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

그리고 getmapping이랑 postmapping도 추가해주시면 감사할 거 같아요 ㅎㅎ (이것도 가독성 때문에 ㅠㅠ)

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package org.yapp.apis.readingrecord.controller

import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.Parameter
import io.swagger.v3.oas.annotations.media.Content
import io.swagger.v3.oas.annotations.media.Schema
import io.swagger.v3.oas.annotations.responses.ApiResponse
import io.swagger.v3.oas.annotations.responses.ApiResponses
import io.swagger.v3.oas.annotations.tags.Tag
import jakarta.validation.Valid
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.domain.Sort
import org.springframework.data.web.PageableDefault
import org.springframework.http.ResponseEntity
import org.springframework.security.core.annotation.AuthenticationPrincipal
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.yapp.apis.readingrecord.dto.request.CreateReadingRecordRequest
import org.yapp.apis.readingrecord.dto.response.ReadingRecordResponse
import org.yapp.domain.readingrecord.ReadingRecordSortType
import org.yapp.globalutils.exception.ErrorResponse
import java.util.UUID

@Tag(name = "Reading Records", description = "독서 기록 관련 API")
@RequestMapping("/api/v1/reading-records")
interface ReadingRecordControllerApi {

@Operation(
summary = "독서 기록 생성",
description = "사용자의 책에 대한 독서 기록을 생성합니다."
)
@ApiResponses(
value = [
ApiResponse(
responseCode = "201",
description = "독서 기록 생성 성공",
content = [Content(schema = Schema(implementation = ReadingRecordResponse::class))]
),
ApiResponse(
responseCode = "400",
description = "잘못된 요청",
content = [Content(schema = Schema(implementation = ErrorResponse::class))]
),
ApiResponse(
responseCode = "404",
description = "사용자 또는 책을 찾을 수 없음",
content = [Content(schema = Schema(implementation = ErrorResponse::class))]
)
]
)
@PostMapping("/{userBookId}")
fun createReadingRecord(
@AuthenticationPrincipal @Parameter(description = "인증된 사용자 ID") userId: UUID,
@PathVariable @Parameter(description = "독서 기록을 생성할 사용자 책 ID") userBookId: UUID,
@Valid @RequestBody @Parameter(description = "독서 기록 생성 요청 객체") request: CreateReadingRecordRequest
): ResponseEntity<ReadingRecordResponse>

@Operation(
summary = "독서 기록 목록 조회",
description = "사용자의 책에 대한 독서 기록을 페이징하여 조회합니다. 정렬은 페이지 번호 또는 최신 등록순으로 가능합니다."
)
@ApiResponses(
value = [
ApiResponse(
responseCode = "200",
description = "독서 기록 목록 조회 성공",
content = [Content(schema = Schema(implementation = ReadingRecordResponse::class))]
),
ApiResponse(
responseCode = "404",
description = "사용자 또는 책을 찾을 수 없음",
content = [Content(schema = Schema(implementation = ErrorResponse::class))]
)
]
)
@GetMapping("/{userBookId}")
fun getReadingRecords(
@AuthenticationPrincipal @Parameter(description = "인증된 사용자 ID") userId: UUID,
@PathVariable @Parameter(description = "독서 기록을 조회할 사용자 책 ID") userBookId: UUID,
@RequestParam(required = false) @Parameter(description = "정렬 방식 (PAGE_NUMBER_ASC, PAGE_NUMBER_DESC, CREATED_DATE_ASC, CREATED_DATE_DESC)") sort: ReadingRecordSortType?,
@PageableDefault(size = 10, sort = ["createdAt"], direction = Sort.Direction.DESC)
@Parameter(description = "페이지네이션 정보 (페이지 번호, 페이지 크기, 정렬 방식)") pageable: Pageable
Comment on lines +85 to +87
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

정렬 매개변수 설계가 부분적으로 개선되었으나 여전히 모호함이 남아있습니다.

ReadingRecordSortType enum 사용으로 타입 안전성은 향상되었지만, 커스텀 sort 매개변수와 Pageable의 기본 정렬이 동시에 존재하여 어떤 정렬이 우선되는지 불분명합니다.

다음 중 하나의 방식으로 개선을 권장합니다:

옵션 1: Pageable만 사용 (권장)

- sort: ReadingRecordSortType?,
- @PageableDefault(size = 10, sort = ["createdAt"], direction = Sort.Direction.DESC)
+ @PageableDefault(size = 10, sort = ["createdAt"], direction = Sort.Direction.DESC)

옵션 2: 커스텀 sort가 Pageable 정렬을 오버라이드하도록 명시
API 문서에 정렬 우선순위를 명확히 기술하고, 구현에서 이를 일관되게 처리하도록 보장해주세요.

📝 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
@RequestParam(required = false) @Parameter(description = "정렬 방식 (PAGE_NUMBER_ASC, PAGE_NUMBER_DESC, CREATED_DATE_ASC, CREATED_DATE_DESC)") sort: ReadingRecordSortType?,
@PageableDefault(size = 10, sort = ["createdAt"], direction = Sort.Direction.DESC)
@Parameter(description = "페이지네이션 정보 (페이지 번호, 페이지 크기, 정렬 방식)") pageable: Pageable
@PageableDefault(size = 10, sort = ["createdAt"], direction = Sort.Direction.DESC)
@Parameter(description = "페이지네이션 정보 (페이지 번호, 페이지 크기, 정렬 방식)") pageable: Pageable
🤖 Prompt for AI Agents
In
apis/src/main/kotlin/org/yapp/apis/readingrecord/controller/ReadingRecordControllerApi.kt
around lines 85 to 87, the sorting parameters are ambiguous because both a
custom sort parameter and Pageable's default sorting coexist without clear
precedence. To fix this, choose either to rely solely on Pageable's sorting by
removing the custom sort parameter, or explicitly implement logic where the
custom sort parameter overrides Pageable's sorting. Additionally, update the API
documentation to clearly state the sorting precedence and ensure the code
consistently enforces this behavior.

): ResponseEntity<Page<ReadingRecordResponse>>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.yapp.apis.readingrecord.dto.request

import io.swagger.v3.oas.annotations.media.Schema
import jakarta.validation.constraints.Max
import jakarta.validation.constraints.Min
import jakarta.validation.constraints.NotBlank
import jakarta.validation.constraints.Size


@Schema(description = "독서 기록 생성 요청")
data class CreateReadingRecordRequest private constructor(
@field:Min(1, message = "페이지 번호는 1 이상이어야 합니다.")
@field:Max(9999, message = "페이지 번호는 9999 이하여야 합니다.")
@Schema(description = "현재 읽은 페이지 번호", example = "42", required = true)
val pageNumber: Int? = null,

@field:NotBlank(message = "기억에 남는 문장은 필수입니다.")
@field:Size(max = 1000, message = "기억에 남는 문장은 1000자를 초과할 수 없습니다.")
@Schema(description = "기억에 남는 문장", example = "이것은 기억에 남는 문장입니다.", required = true)
val quote: String? = null,

@field:NotBlank(message = "감상평은 필수입니다.")
@field:Size(max = 1000, message = "감상평은 1000자를 초과할 수 없습니다.")
@Schema(description = "감상평", example = "이 책은 매우 인상적이었습니다.", required = true)
val review: String? = null,

@field:Size(max = 3, message = "감정 태그는 최대 3개까지 가능합니다.")
@Schema(description = "감정 태그 목록 (최대 3개)", example = "[\"감동적\", \"슬픔\", \"희망\"]")
val emotionTags: List<@Size(max = 10, message = "감정 태그는 10자를 초과할 수 없습니다.") String> = emptyList()
) {
fun validPageNumber(): Int = pageNumber!!
fun validQuote(): String = quote!!
fun validReview(): String = review!!
fun validEmotionTags(): List<String> = emotionTags
}
Loading
Loading