TIL9. 리뷰 검색 및 정렬, QueryDSL

2025. 2. 21. 22:28·TIL

헤어스타일 은근 디테일함

 

오늘 몸 상태 최악...^^

어젯밤부터 온수매트 짱짱하게 틀었는데 손발이 거의 얼음장이었는데 오늘도 그렇다...

감기이기엔 평소에 개심한 비염이라 긴가민가했는데 이건 감기 기운이 맞다...

 

리뷰 가게명/메뉴명 검색

가게명/메뉴명으로 검색할 수 있게 하고 싶은데...

 

쿼리를... 어떻게 짜야하냐.... (세상 복잡)

어차피 queryDSL을 써야한다면 여기서도 함 써봐야할 것 같다.

 

🧶QueryDSL을 알아보자

QueryDSL이라.. 이 녀석도 처음임..ㅎ 우선 세팅을 먼저 진행해보자!Gradle 추가//QueryDSL의 버전 //plugin 보다 앞에 둘 것buildscript { ext { queryDslVersion = "5.0.0" }}dependencies { //QueryDSL JPA 라이브러리 implementat

chooobb.tistory.com

이거 참고해서 어렵게 queryDSL 설정을 넣고 어찌어찌 코드 좀 써넣었는데...

뒤늦게 팀장님이 주문 API PR하신 걸 알고 Pull 받으니 이미 해놓으셨더라.

PR 안 하신 줄 알고 테스트 어떡하지 하고 있었는데... 개발하기 전에 Pull 땡기는 습관을 가져야만...ㅜㅜ

ReviewServiceImpl

@Override
public Page<ReviewResponseDto> searchReviewsByKeyword(User user, int searchType, String keyword, int page, int size, String sortBy, boolean isAsc) {
  Sort.Direction direction = isAsc ? Sort.Direction.ASC : Sort.Direction.DESC;
  Sort sort = Sort.by(direction, sortBy);
  Pageable pageable = PageRequest.of(page, getValidPageSize(size), sort);

  Page<Review> reviews;
  if (searchType == 1) { // 가게명 검색
    reviews = reviewRepository.findReviewsByStoreName(user.getId(), keyword, pageable);
  } else if (searchType == 2) { // 메뉴명 검색
    reviews = reviewRepository.findReviewsByMenuName(user.getId(), keyword, pageable);
  } else {
    throw new IllegalArgumentException("searchType은 1(가게명) 또는 2(메뉴명)이어야 합니다.");
  }
  return reviews.map(ReviewResponseDto::new);
}

private int getValidPageSize(int size) {
  // 허용되는 페이지 크기 목록
  List<Integer> allowedSizes = Arrays.asList(10, 30, 50);
  // 페이지 크기가 허용된 값에 없으면 기본 10으로 설정
  if (!allowedSizes.contains(size)) {
    return 10;
  }
  return size;
}

ReviewRepository

ReveiwRepositoryCustom도 extends하도록 JpaRepository 뒤에 붙힘.

package com.sparta.tentenbackend.domain.review.repository;

import com.sparta.tentenbackend.domain.review.entity.Review;
import java.util.UUID;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface ReviewRepository extends JpaRepository<Review, UUID> , ReviewRepositoryCustom {
  
  Page<Review> findAllByOrder_User_IdAndIsDeletedFalse(Long id, Pageable pageable);
}

ReviewRepositoryCustom

package com.sparta.tentenbackend.domain.review.repository;

import com.sparta.tentenbackend.domain.review.entity.Review;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

public interface ReviewRepositoryCustom {
  Page<Review> findReviewsByStoreName(Long userId, String keyword, Pageable pageable);
  Page<Review> findReviewsByMenuName(Long userId, String keyword, Pageable pageable);
}

ReviewRepositoryImpl

package com.sparta.tentenbackend.domain.review.repository;

import com.querydsl.core.QueryFactory;
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import com.sparta.tentenbackend.domain.menu.entity.QMenu;
import com.sparta.tentenbackend.domain.menu.entity.QMenuOrder;
import com.sparta.tentenbackend.domain.order.entity.QOrder;
import com.sparta.tentenbackend.domain.review.entity.QReview;
import com.sparta.tentenbackend.domain.review.entity.Review;
import com.sparta.tentenbackend.domain.store.entity.QStore;
import java.util.List;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport;
import org.springframework.stereotype.Repository;

@Repository
public class ReviewRepositoryImpl extends QuerydslRepositorySupport implements ReviewRepositoryCustom {
  private final JPAQueryFactory queryFactory;

  public ReviewRepositoryImpl(JPAQueryFactory queryFactory) {
    super(Review.class);
    this.queryFactory = queryFactory;
  }

  @Override
  public Page<Review> findReviewsByStoreName(Long userId, String keyword, Pageable pageable) {
    QReview review = QReview.review;
    QOrder order = QOrder.order;
    QStore store = QStore.store;

    JPAQuery<Review> query = queryFactory.selectFrom(review)
        .join(review.order, order)
        .join(order.store, store)
        .where(store.name.containsIgnoreCase(keyword)
            .and(order.user.id.eq(userId))
            .and(review.isDeleted.isFalse()));

    List<Review> reviews = getQuerydsl().applyPagination(pageable, query).fetch();
    long total = query.fetchCount();

    return new PageImpl<>(reviews, pageable, total);
  }

  @Override
  public Page<Review> findReviewsByMenuName(Long userId, String keyword, Pageable pageable) {
    QReview review = QReview.review;
    QOrder order = QOrder.order;
    QStore store = QStore.store;
    QMenuOrder menuOrder = QMenuOrder.menuOrder;
    QMenu menu = QMenu.menu;

    JPAQuery<Review> query = queryFactory.selectFrom(review)
        .join(review.order, order)
        .join(order.store, store)
        .join(order.menuOrderList, menuOrder)  // 메뉴 주문 목록과 연결
        .join(menuOrder.menu, menu)  // 메뉴와 연결
        .where(menu.name.containsIgnoreCase(keyword)  // 메뉴명으로 검색
            .and(order.user.id.eq(userId))  // 사용자 ID로 필터링
            .and(review.isDeleted.isFalse()));  // 삭제되지 않은 리뷰만 필터링

    List<Review> reviews = getQuerydsl().applyPagination(pageable, query).fetch();
    long total = query.fetchCount();

    return new PageImpl<>(reviews, pageable, total);
  }
}

리뷰 정렬

정렬 기준에 따라 OrderSpecifier 생성했다. 요구사항에 따르면 생성일, 수정일순으로 정렬되어야 함.

  private OrderSpecifier<?> getOrderSpecifier(String sortBy, boolean isAsc) {
    QReview review = QReview.review;
    // 정렬 기준을 받아 정렬 설정
    if ("createdAt".equalsIgnoreCase(sortBy)) {
      return isAsc ? review.createdAt.asc() : review.createdAt.desc();  // 생성일 기준
    } else if ("updatedAt".equalsIgnoreCase(sortBy)) {
      return isAsc ? review.updatedAt.asc() : review.updatedAt.desc();  // 수정일 기준
    } else {
      // 기본적으로 생성일 기준으로 정렬
      return isAsc ? review.createdAt.asc() : review.createdAt.desc();
    }
  }

 

사실 리뷰 전체 목록 조회는 굳이 queryDSL을 쓸 필요는 없을 것 같아서 그냥 ... 이렇게 했다ㅎㅎ

  // ReviewServiceImpl
  // 리뷰 목록 조회
  @Override
  public Page<ReviewResponseDto> findAllReviews(User user, int page, int size, String sortBy, boolean isAsc) {
    Sort.Direction direction = isAsc ? Sort.Direction.ASC : Sort.Direction.DESC;
    Sort sort = getSortByField(sortBy, direction);
    Pageable pageable = PageRequest.of(page, size, sort);

    // review 테이블에 저장된 order로 order.getUser().getId() == user.getId() 인 리뷰 목록/isDeleted=false만 조회하기
    Page<Review> reviews = reviewRepository.findAllByOrder_User_IdAndIsDeletedFalse(user.getId(), pageable);
    return reviews.map(ReviewResponseDto::new);
  }

  // 정렬 기준
  private Sort getSortByField(String sortBy, Sort.Direction direction) {
    if ("updatedAt".equalsIgnoreCase(sortBy)) {
      return Sort.by(direction, "updatedAt");
    } else if ("createdAt".equalsIgnoreCase(sortBy)) {
      return Sort.by(direction, "createdAt");
    } else {
      // 기본값으로 생성일 기준 정렬
      return Sort.by(direction, "createdAt");
    }
  }

 

고민

지금 사장님의 리뷰에서 주문내역에 저장된 가게의 사장님과 로그인 유저가 같을 때, 그 아이디를 불러오는 게 너무 ...

이게 맞나...? 싶어서 어케 해야할지 모르겠다.

 

Long ownerUserId = review.getOrder().getStore().getUser().getId(); < 이거...

@Override
public OwnerReviewResponseDto addOwnerReview(OwnerReviewRequestDto requestDto, User user) {
  UUID reviewId = UUID.fromString(requestDto.getReviewId());
  Review review = reviewRepository.findById(reviewId)
      .orElseThrow(() -> new NotFoundException("해당 리뷰를 찾을 수 없습니다."));
  // 유저가 OWNER고, 유저아이디가 가게 사장님 아이디와 일치할 때
  Long ownerUserId = review.getOrder().getStore().getUser().getId();
  if (user.getRole() == UserRoleEnum.OWNER && user.getId() == ownerUserId) {
  OwnerReview ownerReview = ownerReviewRepository.save(new OwnerReview(requestDto, review));
  return new OwnerReviewResponseDto(ownerReview);
  } else {
    throw new UnauthorizedException("해당 리뷰에 대한 권한이 없습니다.");
  }
}

 

잇츠 고민고민...

ㅋㅋㅋㅋㅋㅋ분위기 완전 반대

728x90

'TIL' 카테고리의 다른 글

TIL11. 테스트의 늪  (0) 2025.02.25
TIL10. 리팩토링, 카테고리별 가게 목록 조회, soft-delete  (1) 2025.02.24
TIL8. AI API  (0) 2025.02.20
TIL7. S3 Bucket 적용, DTO Validation  (0) 2025.02.19
TIL6. Paging, S3 Bucket  (0) 2025.02.18
'TIL' 카테고리의 다른 글
  • TIL11. 테스트의 늪
  • TIL10. 리팩토링, 카테고리별 가게 목록 조회, soft-delete
  • TIL8. AI API
  • TIL7. S3 Bucket 적용, DTO Validation
hnajeahi
hnajeahi
개발 스터딩
  • hnajeahi
    hnajeahi-hub
    hnajeahi
  • 전체
    오늘
    어제
    • 전체보기 (95) N
      • 개발일기 (2)
      • 코딩테스트 (64) N
      • TIL (29)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

    • 블로그 이사왔음!
  • 인기 글

  • 태그

    해시
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
hnajeahi
TIL9. 리뷰 검색 및 정렬, QueryDSL
상단으로

티스토리툴바