해결된 질문
작성
·
942
0
개인 프로젝트를 진행하며 querydsl을 사용하여 동적 쿼리문을 작성했습니다. 무한스크롤 페이징 처리를 하였고, 코드의 일부분만 보여드리면 아래와 같습니다.
public ArticlePagingResponse<Article> searchDynamicQueryAndPaging(Long lastArticleId,
ArticleSearchCond cond,
String orderBy,
int size) {
JPAQuery<Article> query = new JPAQuery<>(em);
query.from(article)
.join(article.member,member)//article.member는 Article테이블에 있는 member_id, member는 Member테이블에 있는 id라고 생각
.join(article.restaurant, restaurant)//article.restaurant는 Article테이블에 있는 restaurant_id, restaurant는 Restaurant테이블에 있는 id
.fetchJoin()
.where(
// no-offset 페이징 처리
ltStoreId(lastArticleId),
// 검색조건들
생략...
);
//정렬 동적 처리
switch(orderBy){
case OrderConst.CREATED_DATE_DESC://최신 순으로 정렬
query.orderBy(article.createdDate.desc());
break;
case OrderConst.CREATED_DATE_ASC://오래된 순으로 정렬
query.orderBy(article.createdDate.asc());
break;
case OrderConst.VIEWS_DESC://조회수 순으로 정렬
query.orderBy(article.views.desc(),article.createdDate.desc());
break;
case OrderConst.LIKE_COUNT_DESC://좋아요 갯수 순으로 정렬
query.leftJoin(likeArticle)
.on(article.id.eq(likeArticle.article.id))
.groupBy(article.id)
.orderBy(likeArticle.count().desc(),article.createdDate.desc());
break;
case OrderConst.COMMENT_COUNT_DESC://댓글 갯수 순으로 정렬
query.leftJoin(comment)
.on(article.id.eq(comment.article.id))
.groupBy(article.id)
.orderBy(comment.count().desc(),article.createdDate.desc());
break;
default:
throw new IllegalStateException("OrderConst에 정의되어있는 orderBy값 외의 다른 값이 들어왔습니다.");
}
List<Article> results = query
.limit(size + 1)
.fetch();//size를 DB에서 받는 것보다 프론트에서 받는게 더 유연할 것같음.fetch();
boolean hasNext = false;
if (results.size() > size) {//결과가 6개이면 size(5)보다 크므로 다음 페이지가 있다는 의미
hasNext = true;
results.remove(size - 1);//다음 페이지 확인을 위하 게시글을 하나더 가져왔으므로 확인 후 삭제
}
return new ArticlePagingResponse<>(results,hasNext);
}
코드를 보면 no-offset방식으로 구현을 하였습니다.
whrer문의 lastArticleId 값을 받고 그것보다 작은 값중에 5개씩 받도록 처리하였습니다.
예) 10, 9, 8, 7, 6 의 게시글을 받고 그다음 스크롤 이벤트가 발생하면 6보다 작은 값 중에서 5개인 5, 4, 3, 2, 1 을 가져 오는 것입니다.
문제는 최신순으로 정렬하여 값을 가져오면 최신 게시글의 id값이 가장 크므로 잘 작동하는데 다른 정렬 조건(오래된 순, 조회수순, 좋아요 갯수순, 댓글 갯수순) 으로 가져올 때는 id값의 순서를 예상하지 못하니때문에 정렬이 되지않는 문제가 발생하였습니다.
오래된 순은 id값을 lt 대신에 gt쓰고 어떻게 구현할 수 있을 것같은데 다른 정렬 조건(조회수순, 좋아요순, 댓글순)은 어떻게 구현할 좋은 방법이 생각 나질 않네요 방법을 아시는 분 계시면 알려주시면 감사하겠습니다.
답변 3
2
안녕하세요. 백엔드 개발자 취준생님
결론부터 말씀드리면 NO OFFSET 방식은 반드시 정렬 기준이 되는 고유한 키를 가져야 합니다. 이 키는 고유하고 변경되지 않아야 합니다. 그렇지 않으면 정확한 페이징을 보장할 수 없습니다.
보통 특정 시간을 기준으로 ID가 생성되기 때문에 이런 경우에는 NO OFFSET이 유요하지만 임의의 정렬 기준을 다시 새워야 한다면 NO OFFSET 방식을 사용하기는 어렵습니다. 이때는 일반적인 페이징 방식을 고려해야 합니다.
관련해서 NO OFFSET 단점으로 검색해보시면 도움이 되실거에요.
감사합니다.
1
해결했습니다!!!
조회수를 구현할 때는 조건식을 where절에 넣어야했지만 좋아요순과, 댓글순은 group by를 사용했으니 having절에 넣어야했는데 조회수했을 때 처럼 where절에 조건식을 넣어버려서 생긴 문제였습니다.ㅠㅠㅠ
테스트 코드의 일부분만 보여드리면 아래와 같습니다.
List<Article> findArticles = query.select(article)
.from(article)
.leftJoin(article.likes, likeArticle)
.groupBy(article.id)
.having(
likeArticle.count().loe(likeCount),
article.id.loe(lastArticleId)
)
.orderBy(
likeArticle.count().desc(),
article.id.desc()
)
.fetch();
답변 감사합니다!
조회수순으로 정렬은 해결을 했는데 좋아요순, 댓글순 정렬에서 막힙니다ㅠㅠㅠㅠ
최신순은 id값이 높을 수록 높을 수록 최신순이므로 마지막 id값을 기준으로 해당 id 값보다 작은 값중 5개를 가져오는 것처럼 조회수순으로 정렬할 때도 똑같은 원리로
마지막 조회수보다 작은 값을 가져오게되면 같은 값의 조회수를 가진 게시글이 여러개일 경우 해당 게시글이 무시됩니다.
그래서 정렬 기준을 views, id 2개를 넣고 where조건문으로 같은 조회수를 가진 게시글이면 id값을 기준으로 게시글을 가져오도록 조건 처리를 하였습니다.
SQL코드로 구현하면 아래와 같습니다.
이런식으로 코드를 짜면 같은 조회수의 게시글이 있더라도 후 순위로 게시글 id를 기준으로 내림차순 값이 나오며 조회수가 같더라도 무시되지 않고 잘 출력됩니다.
querydsl로 구현한 코드는 아래와 같습니다. 초기에 조회수를 기준으로 값을 가져올 때는 조회수가 값으느로 null처리를 해주었습니다.
문제는 여기서 부터입니다!!! 위를 똑같이 적용해서 좋아요 개수순, 댓글 개수순으로 정렬을 구현할 때입니다. 정확하게는 모르겠는데 집계함수를 사용함으로인해 group 함수에서 문제가 발생하는 것 같습니다.
제가 구현한 코드는 아래와 같습니다.
코드를 보면 likeArticleCountEq, likeArticleCountLt에 likeArticle.count()를 사용하며 집계함수를 사용하였습니다.
정확하게 오류가 발생하는 시점을 말씀드리면, 좋아요 개수순으로 게시글을 가져올 때는, 정삭적으로 출력이 됩니다. 그런데 스크롤 이벤트를 통해 orderCond.getLikeCount() != null 일경우 값을 가져오면
java.sql.SQLException: Invalid use of group function
오류를 출력합니다.
orderCond.getLikeCount() 이 null일 경우에는 잘 작동하는데, orderCond.getLikeCount() != null 경우에만 오류가 발생하는 것을 보면 두개의 차이점은 where문 조건절이고 where문 조건절에 count() 집계함수를 사용하냐 안하냐의 차이인데.... 아직 집계함수에 대한 이해도가 부족해서 코드를 어떻게 수정해야할지 감이 안잡힙니다.ㅠㅠ
select절에 likeArticle을 사용하지 않아서 발생하는 문제인걸 수도 있을 것같기도하고....혹시 해결해주실 분 있으면 감사하겠습니다ㅠㅠㅠ 메서드 전체소스 코드는 아래와 같습니다.
//where절에서 사용하는 집계함수가 사용되는는 메서드
//console창으로 출력된 쿼리문
select
article0_.id as id1_0_0_,
restaurant2_.id as id1_9_1_,
article0_.created_date as created_2_0_0_,
article0_.modified_date as modified3_0_0_,
article0_.content as content4_0_0_,
article0_.image as image5_0_0_,
article0_.image_size as image_si6_0_0_,
article0_.member_id as member_i9_0_0_,
article0_.restaurant_id as restaur10_0_0_,
article0_.status as status7_0_0_,
article0_.views as views8_0_0_,
restaurant2_.dong as dong2_9_1_,
restaurant2_.sido as sido3_9_1_,
restaurant2_.sigoon as sigoon4_9_1_,
restaurant2_.category as category5_9_1_,
restaurant2_.address as address6_9_1_,
restaurant2_.latitude as latitude7_9_1_,
restaurant2_.longitude as longitud8_9_1_,
restaurant2_.old_address as old_addr9_9_1_,
restaurant2_.name as name10_9_1_
from
article article0_
inner join
member member1_
on article0_.member_id=member1_.id
inner join
restaurant restaurant2_
on article0_.restaurant_id=restaurant2_.id
left outer join
like_article likearticl3_
on (
article0_.id=likearticl3_.article_id
)
where
(
article0_.status=?
or article0_.status=?
)
and (
count(likearticl3_.id)<?
or count(likearticl3_.id)=?
and article0_.id<=?
)
group by
article0_.id ,
likearticl3_.article_id
order by
count(likearticl3_.id) desc,
article0_.id desc limit ?