최근 프로젝트에서 Kotlin JDSL을 도입하며, 특히 페이지네이션된 데이터를 반환할 때 몇 가지 문제에 직면했습니다. 자료가 부족하거나 오래되어 직접 구현하기 쉽지 않았지만, 공식 문서와 GitHub 테스트 코드를 참고하여 문제를 해결할 수 있었습니다. 이번 글에서는 해결 과정을 코드와 함께 공유하고자 합니다.

문제 해결 과정: Page 타입 응답 구현

페이지네이션된 데이터를 반환하기 위해 KotlinJdslJpqlExecutor의 findPage 메서드와 selectNew를 사용했습니다. selectNew 메서드는 엔티티가 아닌 사용자 정의 DTO로 데이터를 매핑할 수 있어 매우 유용합니다. 아래는 구현한 코드입니다

@Repository
class CustomCircleRepositoryImpl(
    private val kotlinJdslJpqlExecutor: KotlinJdslJpqlExecutor
) : CustomCircleRepository {

    override fun findCirclesByPagination(pageable: Pageable): Page<CirclePageResponse?> {
        return kotlinJdslJpqlExecutor.findPage(pageable) {
            selectNew<CirclePageResponse>(
                path(Circle::id),
                path(Circle::thumbnailUrl),
                path(Circle::title),
                path(Circle::introduction),
                path(Member::profile).`as`(expression("leaderProfile")),
                path(Member::nickname).`as`(expression("leaderName")),
                path(Circle::englishLevel),
                path(Circle::city),
                path(Circle::capacity),
                path(Circle::totalView),
                path(Circle::totalLike)
            ).from(
                entity(Circle::class),
                join(Circle::leader)
            )
        }
    }
}

코드 설명

  1. CustomCircleRepositoryImpl 클래스: KotlinJdslJpqlExecutor를 주입받아 findCirclesByPagination 메서드를 구현한 커스텀 리포지토리입니다.
  2. findCirclesByPagination 메서드: Pageable 객체를 인자로 받아 Page<CirclePageResponse?> 타입의 응답을 반환합니다. 이 메서드는 JPA의 Page 기능을 활용해 페이지네이션된 데이터를 반환하도록 설계되었습니다.
  3. selectNew 메서드: Kotlin JDSL의 핵심 기능 중 하나로, CirclePageResponse라는 사용자 정의 DTO에 엔티티 데이터를 매핑합니다. path 메서드를 통해 각 필드를 선택하며, DTO 필드와 일치하도록 매핑합니다. Member 엔티티에서 리더의 프로필과 닉네임을 가져오기 위해 join을 사용했습니다.

DTO 설계와 발생한 이슈

페이지네이션을 위해 사용하는 CirclePageResponse는 다음과 같은 구조를 가지고 있습니다:

@Schema(description = "서클 페이지 조회 응답")
data class CirclePageResponse(
    val id: UUID,
    val thumbnailUrl: String? = null,
    val title: String,
    val introduction: String = "",
    val leaderProfile: String? = null,
    val leaderName: String,
    val englishLevel: EnglishLevel,
    val city: City,
    val capacity: Int,
    val totalView: Int,
    val totalLike: Int,
    val likedByMe: Boolean = false
)

이슈: selectNew 메서드의 제한 사항

selectNew를 사용할 때, DTO의 생성자 파라미터에 정확히 일치하는 개수와 순서대로 값을 할당해야 합니다. likedByMe 필드는 기본값이 false로 설정되어 있었지만, selectNew는 기본값을 무시하고 모든 파라미터에 값을 전달하도록 강제합니다. 이로 인해 초기 코드에서 오류가 발생했습니다.

해결 방법

이 문제를 해결하기 위해 likedByMe 필드를 생성자 밖으로 이동하여 기본값을 설정하도록 변경했습니다. 수정된 DTO는 다음과 같습니다.

@Schema(description = "서클 페이지 조회 응답")
data class CirclePageResponse(
    val id: UUID,
    val thumbnailUrl: String? = null,
    val title: String,
    val introduction: String = "",
    val leaderProfile: String? = null,
    val leaderName: String,
    val englishLevel: EnglishLevel,
    val city: City,
    val capacity: Int,
    val totalView: Int,
    val totalLike: Int
) {
    val likedByMe: Boolean = false
}

이제 selectNew는 DTO의 모든 생성자 파라미터에 값을 할당할 필요가 없어졌습니다. likedByMe 필드는 클래스 바디에 정의되어 기본값 false로 설정됩니다. 이로써 selectNew의 제한 사항을 우회하면서 원하는 데이터를 매핑할 수 있었습니다.

'Kotlin-Jdsl' 카테고리의 다른 글

Kotlin JDSL에서 Null 값을 가진 조건 무시하기  (1) 2024.11.07

+ Recent posts