코딩과 결혼합니다

[Game_Crew] 리팩토링 : Post 전체조회 리팩토링 본문

코딩과 매일매일♥/Game_Crew

[Game_Crew] 리팩토링 : Post 전체조회 리팩토링

코딩러버 2023. 11. 14. 16:23
728x90

11월 13일(어제)은 우리 팀원들과 약속했던 유저 테스트 날이었으나, 상황적인 문제로 이틀 뒤로 연기되었다. 개인적으로는 MVP 기능 구현은 모두 마친 상태이지만, 내부 로직이 아직 완벽하다고 판단하지 못해 코드의 가독성, 유지보수성, 그리고 성능을 높이기 위해 리팩토링을 결정하였다.

 

이 코드는 다른 백엔드 팀원이 작성한 것으로, 서로 이해가 어려운 부분들이 있어 수정이 필요하다는 결론을 내렸다. 그리하여 나의 방식에 따라 코드를 수정하고 코드 리뷰까지 진행하였다.


📌Controller

리팩토링 전

@GetMapping("/category")
//ResponseEntity를 안쓰려면 어떻게 할 수 있나? ResponseEntity의 반환값을 다른 방식으로 하려면?? PostResponseDto를 쓰려면?? 아니면 페이지네이션용 Dto를 따로 만들어야 하나??
public ResponseEntity<Map<String, Object>> getCategoryPost(
        @RequestParam("category") String category,
        @RequestParam("page") int page,
        @RequestParam("size") int size) {

    Map<String, Object> pageNationResponseDtoList = postService.getCategoryPost(
            category,
            page - 1,
            size
    );
    return ResponseEntity.ok(pageNationResponseDtoList);
}
리팩토링

    @Operation(summary = "게시글 페이지네이션", description = "게시글 페이지네이션")
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "successful operation", content = @Content(schema = @Schema(implementation = ResponseEntity.class))),
            @ApiResponse(responseCode = "400", description = "bad request operation", content = @Content(schema = @Schema(implementation = ResponseEntity.class)))
    })
    @GetMapping("")
    public PostsResponseDto getCategoryPost(
            @RequestParam String category,
            @RequestParam int page,
            @RequestParam int size) {
        return postService.getCategoryPost(category, page - 1, size);
    }
}
  • ResponseEntity 대신 PostsResponseDto를 반환 타입으로 사용하여 코드가 더 간결해지고 가독성 향상
  • ResponseEntity를 사용해주지 않아도 Spring MVC 에서는 반환 타입에 따라 적절한 HTTP 응답 상태 코드를 자동으로 설정한다. 200(OK) 이 코드에서는 HTTP 응답 헤더를 직접 제어할 필요가 없기 때문에 Dto로 반환하였다.
  • PostsResponseDto 는 페이지네이션 정보를 포함하고 있어 별도의 페이지네이션용 Dto를 만들지 않았다.
  • 또한 매개변수의 이름이 파라미터의 이름과 동일하여 @RequestParam만 사용하여 가독성을 높였다.
  • @GetMapping("/category") 를 @GetMapping("")로 변경하여 좀 더 직관적이게 하였다. 이 메서드가 컨트롤러의 기본동작이고 다른 겹치는 메서드가 없었기 때문이다.

📌 Service

리팩토링 전

    public Map<String, Object> getCategoryPost(String category, int page, int size) {
        // 페이징 처리
        boolean isAsc = true;
        String sortBy = "postId";
        Sort.Direction direction = isAsc ? Sort.Direction.ASC : Sort.Direction.DESC;
        Sort sort = Sort.by(direction, sortBy,"title");
        Pageable pageable = PageRequest.of(page, size, sort);

        if(("all".equals(category))){
            Page<Post> postList = postRepository.findAll(pageable);

            List<PageNationResponseDto> pageNationResponseDto = postList.stream()
                    .map(PageNationResponseDto::new)
                    .collect(Collectors.toList());

            Map<String, Object> response = new HashMap<>();
            response.put("postList", pageNationResponseDto);
            response.put("totalPages", postList.getTotalPages());

            return response;
        }
        else {
            Page<Post> postListCategory = postRepository.findAllByCategory(category, pageable);

//            if (postListCategory.getContent().isEmpty()) {
//                throw new RuntimeException("존재하지 않는 카테고리입니다."); // 커스텀 익셉션이라 그냥 런타입 익셉션 씀
//            }

            Map<String, Object> response = new HashMap<>();
            List<PageNationResponseDto> pageNationResponseDto = postListCategory.stream()
                    .map(PageNationResponseDto::new)
                    .collect(Collectors.toList());

            response.put("postList", pageNationResponseDto);
            response.put("totalPages", postListCategory.getTotalPages());

            return response;
        }
    }
리팩토링

    public PostsResponseDto getCategoryPost(String category, int page, int size) {
        Pageable pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "createdAt"));

        if (category.equals("all")) {
            Page<Post> postList = postRepository.findAll(pageable);

            // Post 객체를 PostResultDto 객체로 변환
            List<PostResultDto> PostResultDtos = postList.getContent().stream()
                    .map(PostResultDto::new)
                    .collect(Collectors.toList());

            return new PostsResponseDto(Message.GET_POST_SUCCESSFUL, postList.getTotalPages(), postList.getTotalElements(), size, PostResultDtos);

        } else {
            Page<Post> postListCategory = postRepository.findAllByCategory(category, pageable);

            List<PostResultDto> PostResultDtos = postListCategory.getContent().stream()
                    .map(PostResultDto::new)
                    .collect(Collectors.toList());

            return new PostsResponseDto(Message.GET_POST_SUCCESSFUL, postListCategory.getTotalPages(), postListCategory.getTotalElements(), size, PostResultDtos);
        }
    }
  • DTO를 사용하여 응답을 보내고 있어 클라이언트 측에서 응답의 형식을 예측하고 처리하기 쉬워졌다. 리팩토링 전에는 Map 객체를 사용하여 응답을 보내기 때문에, 응답의 형식이 일관적이지 않을 수 있다.
  • if와 else 블로 내에서 동일한 코드가 중복되어 사용되었으나 리팩토링 후에는 이러한 중복을 제거하여 코드의 가독성과 유지보수성을 향상하였다.
  • 리팩토링 전에는 게시글 ID와 제목으로 오름차순 정렬을 하고 있었는데, 게시글의 생성된 시간으로 내림차순 정렬하며 사용자가 가장 최근에 작성된 게시물을 조회할 수 있도록 하였다.
  • PostsResponseDto에 메시지를 추가하여, 응답에 대한 추가적인 정보를 제공한다.

📌 DTO

@Getter
public class PageNationResponseDto {
    private Long postId;
    private String title;
    private Long totalNumber;
    private Integer currentNumber;
    private String nickname;

    private int view;

    public PageNationResponseDto(Post post){
        this.postId = post.getPostId();
        this.title = post.getTitle();
        this.totalNumber = post.getTotalNumber();
        this.currentNumber = post.getCurrentNum();
        this.nickname = post.getUser().getNickname();
//        this.images = post.getImages();
//        this.temperature = post.getUser().getTemperature(); -> 온도 어떻게하지?
        this.view = post.getView();

    }
}

 

이전에는 하나의 Dto로 상세조회와 전체조회에 대한 응답을 해주었던 것으로 보인다.

 

리팩토링 후

프론트에서 상황에 따라 필요로하는 DTO를 따로 분리하여 보냄으로 자원을 효율적으로 사용하였다

@Getter
public class PostsResponseDto {
    private String msg;
    private CustomPageable pageable;
    private List<PostResultDto> result;


    public PostsResponseDto(
            String msg,
            int totalPages,
            long totalElements,
            int size,
            List<PostResultDto> postResultDto
    ){
        this.msg = msg;
        this.pageable = new CustomPageable(totalPages, totalElements, size);
        this.result = postResultDto;
    }
}


전체 조회에 성공했을 때의 메시지와, 페이지네이션 정보 그리고 post관련 데이터를 담고 있다.

 

@Getter
public class PostResultDto {
    private Long postId;
    private String title;
    private Long totalNumber; // 전체 참가자 수
    private Long currentNumber; //참가자 수
    private String nickname;
    private String userImg;

    public PostResultDto(Post post){
        this.postId = post.getPostId();
        this.title = post.getTitle();
        this.totalNumber = post.getTotalNumber();
        this.currentNumber = post.getCurrentNumber();
        this.nickname = post.getUser().getNickname();
        this.userImg = post.getUser().getUserImg();
    }
}

결과

 

지금은 등록해놓은 post가 하나밖에 없어서...