<aside> 🔑

요약

QueryDSL로 N:M 관계인 약초-태그 데이터를 페이징할 때, 페이징 범위에 따라 한 약초의 일부 태그만 조회되는 문제가 발생했다.

이는 .offset().limit()이 조인된 태그 테이블까지 영향을 주면서 태그 데이터가 누락된 것이 원인이었다.

이를 해결하기 위해 먼저 약초 ID에만 페이징을 적용한 뒤, 해당 ID 목록을 바탕으로 태그와 기타 데이터를 별도로 조회하였다. GroupByProjections를 활용하여 약초 하나에 여러 태그가 묶이도록 하여 데이터 일관성을 확보했다.

이 방식으로 정확한 페이징 결과와 함께 약초에 연결된 모든 태그가 누락 없이 반환되도록 개선되었다. 다대다 관계에서 페이징 시에는 ID 선조회 후 상세 조회 방식이 가장 안정적이라는 점을 배웠다.

</aside>

문제 배경

개선

개선 과정1 | herb.id 기준으로 먼저 페이징 적용

List<Long> herbIds = jpaQueryFactory
    .select(herb.id)
    .from(herb)
    .where(booleanBuilder)
    .orderBy(orderSpecifier)
    .offset(pageable.getOffset())
    .limit(pageable.getPageSize())
    .fetch();

개선 과정2 | 조회된 herb id 리스트를 바탕으로 상세 데이터 조회

List<HerbAdminDTO> herbList = jpaQueryFactory
    .selectFrom(herb)
    .leftJoin(herbTag).on(herbTag.herb.eq(herb))
    .leftJoin(tag).on(tag.eq(herbTag.tag))
    .leftJoin(herbViews).on(herbViews.herb.eq(herb))
    .where(herb.id.in(herbIds))
    .transform(
        GroupBy.groupBy(herb.id).list(
            Projections.constructor(HerbAdminDTO.class,
                herb.id, herb.bneNm, herb.cntntsSj, herb.hbdcNm,
                herb.imgUrl1, herb.imgUrl2, herb.imgUrl3, herb.imgUrl4, herb.imgUrl5, herb.imgUrl6,
                herb.prvateTherpy, herb.useeRegn, herb.growthForm, herb.flowering,
                herb.habitat, herb.harvest,
                herbViews.viewCount,
                GroupBy.list(
                    Projections.constructor(TagDTO.class,
                        tag.id, tag.name, tag.tagType
                    )
                )
            )
        )
    );

성과 및 배운 점