본문 바로가기
Java

리펙토링 - 일급컬렉션 사용기

by bloodFinger 2021. 5. 28.

사내에서 어플에 새롭게 추가되는 기능을 처음으로 맡아서 API 설계와 기능 구현을 맡았다.

 

기능이 어느정도 완성이 된 이후 코드리뷰를 개발 팀원들에게 받았다.

 

코드 리뷰를 받을때 조금 더 깔끔하게 작성할 수 있는 방법에 대해서 리뷰를 받았고 선임 개발자(객체의 마술사) 님께서 일급 컬렉션을 사용해 리팩토링 해보는게 어떻냐고 이야기를 들었다.

 

 

일급 컬렉션?.... 처음 들어봤다... 일단 침착하자... 후

 

그래서 일급컬렉션이 무엇이고 사용하면 무엇이 좋고 어떤점에서 이점을 가지는지 에 대해서는 아주 좋은 블로그 글이 있기에 

"동욱님의 글" 을 참고 하면 좋을꺼같다.

 

 

 

그러면 리펙토링 할 코드를 살펴보자.

 

아래와 같은 stream이 난무 하는 코드를 리펙토링을 해보자.

+ 일급컬렉션은 관례상(?) 네임뒤에 s 나 복수라는 형태임을 알려주는 네이밍으로 생성하는게 좋다고 한다.

   private List<CommentDTO> findAllCommentDTOS (List<Comment> findCommentList , String userToken) {
        List<String> emails = findCommentList.stream().map(Comment::getWriter).distinct().collect(Collectors.toList());
        List<Long> commentIds = findCommentList.stream().map(Comment::getId).collect(Collectors.toList());

        // 댓글을 작성한 User Entity
        List<User> allByIdUser = userRepository.findAllByEmailIn(emails);
        //게시물 좋아요 정보 DTO
        List<CountDTO> allByCommentIdIn = commentLikedRepository.findAllByCommentIdIn(commentIds);
        //게시물에 댓글 갯수 DTO
        List<RepliesCountDTO> allRepliesCount = commentRepository.findAllRepliesCount(commentIds);
        
        return findCommentList.stream().map(comment -> CommentDTO.createCommentDTO(
                comment,
                allByIdUser.stream().filter(o -> o.userToken().equals(comment.getWriter())).findFirst().get(),
                allByCommentIdIn.stream()
                        .filter(count -> count.getCommentId() == comment.getId())
                        .map(co -> new CountDTO(co.getCommentId() , co.getLikedCount() , userToken.equals(co.getWriter()),co.getWriter()))
                        .findFirst().orElse(new CountDTO(comment.getId(),0,false,"")),
                allRepliesCount.stream().filter(o -> o.getId() == comment.getId()).findFirst()
                        .orElse(new RepliesCountDTO(comment.getId() , 0))
        )).collect(Collectors.toList());
    }

 

 

일급 컬렉션을 사용하여 리펙토링 이후

private List<CommentDTO> findAllCommentDTOS (List<Comment> findCommentList , String userToken) {
        List<String> emails = findCommentList.stream().map(Comment::getWriter).distinct().collect(Collectors.toList());
        List<Long> commentIds = findCommentList.stream().map(Comment::getId).collect(Collectors.toList());

        // 댓글을 작성한 user Entity
        Users users = new Users(userRepository.findAllByEmailIn(emails));
        //게시물 좋아요 정보 DTO
        LikedCountDTOs likedCountDTOs = new LikedCountDTOs(commentLikedRepository.findAllByCommentIdIn(commentIds));
        //게시물에 댓글 갯수
        RepliesCountDTOs repliesCountDtos = new RepliesCountDTOs(commentRepository.findAllRepliesCount(commentIds));

        return findCommentList.stream().map(comment -> CommentDTO.createCommentDTO(
                comment,
                users.getUserToken(comment.getWriter()),
                likedCountDTOs.getLikedCountDtoMatchingById(comment,userToken),
                repliesCountDtos.getDtoMatchingById(comment)
        )).collect(Collectors.toList());
    }

 

public class Users {
    private final Map<String, User> maps;

    public Users(List<User> maps){
        this.maps = maps.stream().collect(Collectors.toMap(User::userToken, each -> each));
    }          

    public User getUserToken(String userToken) {
        return this.maps.get(userToken);
    }

    public Set<String> getIds() {
        return this.maps.keySet();
    }
}

 

public class LikedCountDTOs {
    private List<LikedCountDTO> lists;

    public LikedCountDTOs(List<LikedCountDTO> lists) {
        this.lists = lists;
    }


    public LikedCountDTO getLikedCountDtoMatchingById(Comment comment , String userToken) {
        return this.lists.stream()
                        .filter(count -> count.getCommentId() == comment.getId())
                        .map(co -> new LikedCountDTO(co.getCommentId() , co.getLikedCount() , userToken.equals(co.getWriter()),co.getWriter()))
                        .findFirst().orElse(new LikedCountDTO(comment.getId(),0,false,""));
    }

}

 

public class RepliesCountDTOs {

    private List<RepliesCountDTO> lists;

    public RepliesCountDTOs(List<RepliesCountDTO> lists) {
        this.lists = lists;
    }

    public RepliesCountDTO getDtoMatchingById(Comment comment) {
        return this.lists.stream().filter(o -> o.getId() == comment.getId()).findFirst()
                .orElse(new RepliesCountDTO(comment.getId() , 0));

    }
}

 

 

 

일급 컬렉션을 사용하면서 의문점이 들었다.

 

하나의 메시지를 위해서 일회용의 일급 컬렉션을 만들어서 사용했는데 비슷한 컬렉션의 사용이 생길때 어떻게 해결해야 할지에 대한 의문이 생겼다.

 

예를 들어서 메시지중에 어떤 특정 조건의 회원 집합을 사용하는 컬렉션은 많은 부분에서 사용이 될텐데 그때마다 일회용성으로 컬렉션을 만들어서 사용해야 하는지 공통적으로 사용하는 컬렉션을 따로 모아서 사용해야 하는지...

 

고민의 끝 정답은 아닐지 몰라도 일회용성으로 사용하기로 결정했다.

이유는 메시지가 추후에 어떤 방향으로 변경 될지 모르기 때문이다. A와 B가 같은 조건의 회원 집합을 사용하다가 어떠한 문제로 인해 B의 회원에 또 다른 조건이 생긴 경우 유지 보수가 굉장히 어려워 질수 있다고 생각했다.

 

그래서 중복 아닌 중복을 선택하였다.

 

혹시 또 다른 생각이나 의견이 있다면 말씀해주시면 감사하겠습니다!  

'Java' 카테고리의 다른 글

레거시 코드 변경  (0) 2021.06.13
상속 괜찮은가? (컴포지션)  (0) 2020.08.10
제네릭의 모든것  (0) 2020.08.08
객체직렬화 ?  (0) 2020.02.23
Network - TCP 통신  (0) 2020.01.16