Querydsl로 동적쿼리 날리기(1)_Controller,Service단

YJMINT·2023년 3월 9일
1

◻️ 계기

프로젝트를 진행하며, 이번에 내가 구현해야 할 기능은 '게시글 전체 조회' 였다. 처음에는 별거 아닌 것처럼 보여서 내가 구현하겠다고 했는데, 생각보다 까다로워서 많이 고민했었다. 이번 기회에 내가 고민했던 점들과 이를 어떻게 처리했는지 정리해보고자 한다.

◻️ 고민한 흔적


나는 아이패드에 로직을 먼저 쭉 짜보고 코드를 작성하는 편이다. 같이 프로젝트를 진행하는 휼륭한 멘토님께서 알려준 방법인데, 생각 정리도 되고, 나중에 생각난 로직때문에 코드 수정을 하는 일도 줄어들어 좋은 방법인거 같다. 그리고 막히는 부분이나 긴가민가한 부분을 미리 캐치할 수 있어, 대응방법에 대한 이야기도 더 많이 나눌 수 있다는 장점도 있었다!

위에서 짜 놓은 로직을 다시 한 번 정리해보자면

<< Controller >>
1. Get요청
2. 보여줄 내용 < 유저이름, 유저프로필사진, 게시글 제목, 작성날짜, 미디어 갯수, 좋아요 갯수, 댓글갯수, 조회수, 카테고리, 글타입, 게시글번호>
-> 협업하는 분들과 회의 후 유저프로필사진을 없애고, 조회수를 넣자고 해서 변경했다!
3. 받아올 정보 < 글 타입, 카테고리 > -> @RequestParam 이용

<< Service >>
1. PostListDto를 만들어 Controller에서 받아온 데이터와 서비스 로직을 통한 데이터 담기
2. 게시글 작성 시간에 따라 '방금, 몇분 전, 몇시간 전, 몇일 전, YYYY.MM.DD' 으로 형식 변환해서 보여주기

사실 이번에는 동적쿼리를 이용해서 대부분 내가 하고 싶었던 기능을 구현할 수 있었어서 서비스단 로직이 비교적 간단했다ㅎㅎ

<< Repository >>
1. 선택한 글타입 & 선택한 카테고리에 맞춰 게시글 보여주기
2. 삭제되지 않은 게시글만 보여주기
3. 유저 권한에 따라 보여주는 게시글 달리 하기 (일반 유저는 삭제된 게시글 열람X)
4. 게시글 생성일을 기준으로 내림차순(최신순)으로 정렬
5. 페이징 (Slice)

◻️ 고민했던 점

  1. 정보들을 어떻게 받아올지 (List로 받아와도 되는지 의문)
  2. Slice는 어떻게 사용하는건지 (Page는 정보가 많은데 왜 Slice는 없는지 참..)
  3. 동적쿼리는 어떻게 사용하는건지
    • PostType으로 Q_AND_A,COMPETIOTION가 들어오면, 동적쿼리에서 (PostType == Q_AND_A and PostType == COMPETITION)에 해당하는 데이터 조회
    • 값이 들어오지 않는다면, 전체조회
    • (PostType or PostType) and (Category or Category or Category) 이런 로직으로 검색

◻️ 코드

//PostController.java
@GetMapping("/post")
    public ResponseEntity<ResponseResult<PostListWithSliceDto>> getPostList(@RequestParam(value = "postType", required = false) List<String> postType,
                                                                            @RequestParam(value = "woryOutCategory", required = false) List<String> workOutCategories,
                                                                            Pageable pageable) throws ParseException {
        return ResponseEntity.status(HttpStatus.OK).body(postService.getPostList(postType, workOutCategories, pageable));
    }
  • @RequestParam을 이용한 이유는 글타입과 카테고리를 받아오는 것이 파라미터 값과 이름을 함께 전달하는 Query String을 이용하는 것이 적절하다고 생각했기 때문이다.
  • List< String > 타입으로, 글타입과 카테고리를 받아온다. 스프링은 똑똑하기 때문에 '정보공유,Q&A,자랑' 이런식으로 콤마(,)를 통해 문자열을 나열하면 알아서 콤마를 기준으로 나눠 저장시켜준다.
  • 'required = false' 를 통해 필수로 들어올 값이 아니라고 명시한다.
  • 페이징을 하기위해 pageable을 받아온다. 스프링은 똑똑하기 때문에 page, size를 각각 적어주지 않아도 알아서 파싱해준다.
//PostService.java
public ResponseResult<PostListWithSliceDto> getPostList(List<String> postTypeList, List<String> workOutCategories, Pageable pageable) throws ParseException {
        String memberRole = SecurityUtil.getMemberRole();

        Slice<Post> postList = postRepository.postAsSearchType(memberRole, postTypeList, workOutCategories, pageable);
        List<Post> posts = postList.getContent();
        boolean hasNext = postList.hasNext();
        boolean isFirst = postList.isFirst();

        List<PostListDto> postListDtos = new ArrayList<>();

        for (Post post : posts) {

            SimpleDateFormat formatterDateTime = new SimpleDateFormat("yyyy-MM-dd HH:mm");
            String dateString = post.getDateTime().getCreatedAt();
            Date date;
            try {
                date = formatterDateTime.parse(dateString);
            } catch (ParseException e) {
                throw new ParseException(ErrorCode.DATE_FORMAT_EXCEPTION.getMessage(), e.getErrorOffset());
            }

            String calculateTime = TimeConvertUtil.calculateTime(date);

            if (calculateTime == null) {
                String[] splitString = post.getDateTime().getCreatedAt().split(" ");
                calculateTime = splitString[0];
            }

            PostListDto postListDto = PostListDto.builder()
                    .username(post.getPostWriter().getUsername())
                    .postType(post.getPostType())
                    .workOutCategory(post.getWorkOutCategory())
                    .createdAt(calculateTime)
                    .title(post.getTitle())
                    .postId(post.getId())
                    .mediaListCount(post.getMediaList().size())
                    .likeCount(post.getLikeCount().size())
                    //commentCount()
                    .views(post.getViews())
                    .build();

            postListDtos.add(postListDto);
        }

        PostListWithSliceDto postListWithSliceDto = PostListWithSliceDto.builder()
                .postListDto(postListDtos)
                .hasNext(hasNext)
                .isFirst(isFirst)
                .build();

        return new ResponseResult<>(HttpStatus.OK.value(), postListWithSliceDto);
    }
  • 아직 댓글은 구현전이라 주석처리 해놓았다ㅎㅎ
  • Slice를 위해 pageable을 받아온다. Slice< Post >에는 페이징에 필요한 정보들과 repository에서 찾아온 데이터가 함께 들어가있다. 그래서 .getContent()를 하면 repository에서 찾아온 Post타입의 데이터들이 찾아지게 되고, 페이징에 필요한 정보를 꺼내려면 바로 .hasNext()나 .isFirst()를 사용하면 된다.
  • postListDto를 사용하여 repository에서 찾아온 Post에 대한 필요한 정보들을 담고, 이를 PostListWithSliceDto에 Slice정보와 함께 넣어 반환해주었다.

📚 참고자료

https://crespo.tistory.com/m/167

profile
YJMINT's develog

0개의 댓글