코딩과 결혼합니다

230821 - 코드 리팩토링! 57줄이나 줄었어용 ^^ 본문

코딩과 매일매일♥/Seoulvival

230821 - 코드 리팩토링! 57줄이나 줄었어용 ^^

코딩러버 2023. 8. 21. 21:58
728x90

어마무시한 기존 코드

package com.gavoza.backend.domain.tag.service;

import com.gavoza.backend.domain.post.dto.LocationResponseDto;
import com.gavoza.backend.domain.post.dto.PostInfoResponseDto;
import com.gavoza.backend.domain.post.dto.PostResultDto;
import com.gavoza.backend.domain.post.entity.Post;
import com.gavoza.backend.domain.post.repository.PostRepository;
import com.gavoza.backend.domain.post.response.PostListResponse;
import com.gavoza.backend.domain.tag.dto.hashtagPostResponseDto;
import com.gavoza.backend.domain.user.dto.UserResponseDto;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class TagService {

    private final PostRepository postRepository;

    //인기 순위 태그 조회(전체)
    public List<String> allRankNumber(String gu, String dong) {

        Map<String, Integer> idFrequencyMap = new HashMap<>();
        List<String> hashTagResponseDtos = new ArrayList<>();

        List<Post> postList = postRepository.findAllByGuAndDong(gu,dong);
        for (Post post : postList) {
            if (Objects.isNull(post.getHashtag())) {
                continue;
            }

            String hashTag = post.getHashtag();
            String[] hashTagList = hashTag.split("#");
            for (String tagName : hashTagList) {
                if (tagName == "") {
                    continue;
                }
                idFrequencyMap.put(tagName, idFrequencyMap.getOrDefault(tagName, 0) + 1);
            }
        }
        //엔트리의 값을 내림차순으로 비교합니다. entry2의 값이 entry1의 값보다 크면 양수를 반환하고, 반대의 경우 음수를 반환하며 같으면 0을 반환합니다.
        //따라서, 이 정렬을 통해 언급 횟수가 큰 엔트리가 리스트의 앞쪽으로 오게 됩니다.
        List<Map.Entry<String, Integer>> sortedEntries = new ArrayList<>(idFrequencyMap.entrySet());
        sortedEntries.sort((entry1, entry2) -> entry2.getValue().compareTo(entry1.getValue()));

        List<String> rankedIds = new ArrayList<>();

        for (Map.Entry<String, Integer> entry : sortedEntries) {
            rankedIds.add(entry.getKey());
        }

        for (int i = 0; i < rankedIds.size(); i++) {
            if (i >= 6) {
                break;
            }
            hashTagResponseDtos.add(rankedIds.get(i));
        }
        return hashTagResponseDtos;
    }

    //카테고리별 인기 순위 태그 조회
    public List<String> categoryRankNumer(String category, String gu, String dong) {

        Map<String, Integer> idFrequencyMap = new HashMap<>();
        List<String> hashTagResponseDtos = new ArrayList<>();

        List<Post> postList = postRepository.findAllBycategoryAndGuAndDong(category, gu, dong);
        for (Post post : postList) {
            if (Objects.isNull(post.getHashtag())) {
                continue;
            }

            String hashTag = post.getHashtag();
            String[] hashTagList = hashTag.split("#");
            for (String tagName : hashTagList) {
                if (tagName == "") {
                    continue;
                }
                idFrequencyMap.put(tagName, idFrequencyMap.getOrDefault(tagName, 0) + 1);
            }
        }
        List<Map.Entry<String, Integer>> sortedEntries = new ArrayList<>(idFrequencyMap.entrySet());
        sortedEntries.sort((entry1, entry2) -> entry2.getValue().compareTo(entry1.getValue()));

        List<String> rankedIds = new ArrayList<>();

        for (Map.Entry<String, Integer> entry : sortedEntries) {
            rankedIds.add(entry.getKey());
        }

        for (int i = 0; i < rankedIds.size(); i++) {
            if (i >= 6) {
                break;
            }
            hashTagResponseDtos.add(rankedIds.get(i));
        }
        return hashTagResponseDtos;
    }


    //인기 순위 태그별 post 조회(전체)
    public PostListResponse hashtagPostResponseDtos(int size, int page, String hashtagName,String type, String gu, String dong) {
        Pageable pageable = PageRequest.of(page,size, Sort.by(Sort.Direction.DESC, "createdAt"));

        List<hashtagPostResponseDto> hashtagPostResponseDtos = new ArrayList<>();

        List<PostResultDto> postResultDtos = new ArrayList<>();

        Page<Post> postPage = type.equals("popular")
                    ?postRepository.findAllByHashtagContainingAndGuAndDongOrderByPostViewCountDesc(hashtagName,pageable,gu, dong)
                    :postRepository.findAllByHashtagContainingAndGuAndDongOrderByCreatedAtDesc(hashtagName,pageable, gu, dong);

        if (postPage == null) {
            throw new IllegalArgumentException("존재하지 않는 태그입니다.");
        }

        for (Post checkHashTagName : postPage) {
            String[] checkHashTagNames = checkHashTagName.getHashtag().split("#");
            UserResponseDto userResponseDto = new UserResponseDto(checkHashTagName.getUser());
            PostInfoResponseDto postInfoResponseDto = new PostInfoResponseDto(checkHashTagName);
            LocationResponseDto locationResponseDto = new LocationResponseDto(checkHashTagName.getGu(),checkHashTagName.getDong(),checkHashTagName.getLat(),checkHashTagName.getLng());

            postResultDtos.add(new PostResultDto(userResponseDto, postInfoResponseDto,locationResponseDto));

            //이제 확인해
            for (int i = 0; i < checkHashTagNames.length; i++) {
                if (hashtagName.equals(checkHashTagNames[i])) {
                    hashtagPostResponseDtos.add(new hashtagPostResponseDto(checkHashTagName, hashtagName));
                    break;
                }
            }
        }
        return new PostListResponse("검색 조회 성공", postPage.getTotalPages(), postPage.getTotalElements(), size , postResultDtos);
    }

    //카테고리별 인기 순위 태그 post 조회
    public PostListResponse categoryHashtagPostResponseDtos(int size, int page, String hashtagName, String category, String type, String gu, String dong) {
        // 페이지 및 사이즈 계산
        Pageable pageable = PageRequest.of(page,size, Sort.by(Sort.Direction.DESC, "createdAt"));

        List<hashtagPostResponseDto> hashtagPostResponseDtos = new ArrayList<>();

        List<PostResultDto> postResultDtos = new ArrayList<>();

        Page<Post> postPage = type.equals("popular")
                ? postRepository.findAllByCategoryAndHashtagContainingAndGuAndDongOrderByPostViewCountDesc(category,hashtagName,pageable,gu, dong)
                : postRepository.findAllByCategoryAndHashtagContainingAndGuAndDongOrderByCreatedAtDesc(category,hashtagName, pageable, gu, dong);

        if (postPage == null) {
            throw new IllegalArgumentException("존재하지 않는 태그 혹은 존재하지 않는 카테고리 입니다.");
        }

        for (Post checkhashtagName : postPage) {
            String[] checkhashTagNames = checkhashtagName.getHashtag().split("#");
            UserResponseDto userResponseDto = new UserResponseDto(checkhashtagName.getUser());
            PostInfoResponseDto postInfoResponseDto = new PostInfoResponseDto(checkhashtagName);
            LocationResponseDto locationResponseDto = new LocationResponseDto(checkhashtagName.getGu(),checkhashtagName.getDong(),checkhashtagName.getLat(),checkhashtagName.getLng());

            postResultDtos.add(new PostResultDto(userResponseDto, postInfoResponseDto,locationResponseDto));


            for (int i = 0; i < checkhashTagNames.length; i++) {
                if (hashtagName.equals(checkhashTagNames[i])) {
                    hashtagPostResponseDtos.add(new hashtagPostResponseDto(checkhashtagName, hashtagName));
                    break;
                }
            }
        }
        return new PostListResponse("검색 조회 성공", postPage.getTotalPages(), postPage.getTotalElements(), size , postResultDtos);
    }
}

 

기존의 코드이다. 굉장히 뭔지 읽히지도 않고 중복되는 코드들도 너무 많다. 특히 포스트에서 해시태그를 추출해내는 부분!

아래의 이부분이 계속 반복되므로 메서드를 따로 빼주었다.

 

기존 코드에서 반복되는 부분

 List<Post> postList = postRepository.findAllByGuAndDong(gu,dong);
        for (Post post : postList) {
            if (Objects.isNull(post.getHashtag())) {
                continue;
            }

            String hashTag = post.getHashtag();
            String[] hashTagList = hashTag.split("#");
            for (String tagName : hashTagList) {
                if (tagName == "") {
                    continue;
                }
                idFrequencyMap.put(tagName, idFrequencyMap.getOrDefault(tagName, 0) + 1);
            }
        }
        //엔트리의 값을 내림차순으로 비교합니다. entry2의 값이 entry1의 값보다 크면 양수를 반환하고, 반대의 경우 음수를 반환하며 같으면 0을 반환합니다.
        //따라서, 이 정렬을 통해 언급 횟수가 큰 엔트리가 리스트의 앞쪽으로 오게 됩니다.
        List<Map.Entry<String, Integer>> sortedEntries = new ArrayList<>(idFrequencyMap.entrySet());
        sortedEntries.sort((entry1, entry2) -> entry2.getValue().compareTo(entry1.getValue()));

        List<String> rankedIds = new ArrayList<>();

        for (Map.Entry<String, Integer> entry : sortedEntries) {
            rankedIds.add(entry.getKey());
        }

        for (int i = 0; i < rankedIds.size(); i++) {
            if (i >= 6) {
                break;
            }
            hashTagResponseDtos.add(rankedIds.get(i));
        }
        return hashTagResponseDtos;
    }

getHashTagsFromPosts 라는 이름으로 공통되는 코드를 뽑아 낸다음 for문으로 6개의 태그만 뽑아내는 부분을 더욱 단순화 시켰다.

 

return rankedIds.subList(0, Math.min(rankedIds.size(), 6)) 이런 식으로!

 

중복되는 코드를 메서드로 만들어줌

    //포스트에서 해시태그를 추출
    private List<String> getHashTagsFromPosts(List<Post> postList) {
        Map<String, Integer> idFrequencyMap = new HashMap<>();

        for (Post post : postList) {
            if (Objects.isNull(post.getHashtag())) {
                continue;
            }

            String hashTag = post.getHashtag();
            String[] hashTagList = hashTag.split("#");
            for (String tagName : hashTagList) {
                if (!tagName.isEmpty()) {
                    idFrequencyMap.put(tagName, idFrequencyMap.getOrDefault(tagName, 0) + 1);
                }
            }
        }

        List<Map.Entry<String, Integer>> sortedEntries = new ArrayList<>(idFrequencyMap.entrySet());
        sortedEntries.sort((entry1, entry2) -> entry2.getValue().compareTo(entry1.getValue()));

        List<String> rankedIds = new ArrayList<>();

        for (Map.Entry<String, Integer> entry : sortedEntries) {
            rankedIds.add(entry.getKey());
        }

        return rankedIds;
    }

 

다음으로는 반환하는 Dto를 다른걸로 쓰게 되면서 더이상 쓰지도 않고 저 과정을 대체 왜 거치는지 모를 로직

   ->이거   List<hashtagPostResponseDto> hashtagPostResponseDtos = new ArrayList<>();

        ...
      
        for (Post checkHashTagName : postPage) {
   ->이거         String[] checkHashTagNames = checkHashTagName.getHashtag().split("#");
            UserResponseDto userResponseDto = new UserResponseDto(checkHashTagName.getUser());
            PostInfoResponseDto postInfoResponseDto = new PostInfoResponseDto(checkHashTagName);
            LocationResponseDto locationResponseDto = new LocationResponseDto(checkHashTagName.getGu(),checkHashTagName.getDong(),checkHashTagName.getLat(),checkHashTagName.getLng());

            postResultDtos.add(new PostResultDto(userResponseDto, postInfoResponseDto,locationResponseDto));

            //이제 확인해
    ->이거      for (int i = 0; i < checkHashTagNames.length; i++) {
                if (hashtagName.equals(checkHashTagNames[i])) {
                    hashtagPostResponseDtos.add(new hashtagPostResponseDto(checkHashTagName, hashtagName));
                    break;
                }
            }
        }

인기순위 태그 뽑아내는 것도 아니고 이미 내가 찾고 싶은 태그가 포함된 post들이 잘 나오고 있는데 굳이 #을 기준으로 스플릿을 하고 같은 해시태그가 있는지 확인한다???? 이럴수가.. 굉장히 비효율적인 코드를 그대로 가지고 있었다. 심지어 쓰지도 않고 있었다. 그대로 지웠다.

 

코드 리팩토링 후

package com.gavoza.backend.domain.tag.service;

import com.gavoza.backend.domain.post.dto.LocationResponseDto;
import com.gavoza.backend.domain.post.dto.PostInfoResponseDto;
import com.gavoza.backend.domain.post.dto.PostResultDto;
import com.gavoza.backend.domain.post.entity.Post;
import com.gavoza.backend.domain.post.repository.PostRepository;
import com.gavoza.backend.domain.post.response.PostListResponse;
import com.gavoza.backend.domain.user.dto.UserResponseDto;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class TagService {

    private final PostRepository postRepository;

    //전체 - 인기 태그 순위
    public List<String> allRankNumber(String gu, String dong) {
        List<Post> postList = postRepository.findAllByGuAndDong(gu, dong);
        List<String> rankedIds = getHashTagsFromPosts(postList);

        return rankedIds.subList(0, Math.min(rankedIds.size(), 6));
    }

    //카테고리 - 인기 태그 순위
    public List<String> categoryRankNumer(String category, String gu, String dong) {
        List<Post> postList = postRepository.findAllBycategoryAndGuAndDong(category, gu, dong);
        List<String> rankedIds = getHashTagsFromPosts(postList);

        //6위까지만 가져오기
        return rankedIds.subList(0, Math.min(rankedIds.size(), 6));
    }

    //전체 - 태그별 post
    public PostListResponse hashtagPostResponseDtos(int size, int page, String hashtagName, String type, String gu, String dong) {
        Pageable pageable = PageRequest.of(page, size);

        //정렬
        Page<Post> postPage = type.equals("popular")
                ? postRepository.findAllByHashtagContainingAndGuAndDongOrderByPostViewCountDesc(hashtagName, pageable, gu, dong)
                : postRepository.findAllByHashtagContainingAndGuAndDongOrderByCreatedAtDesc(hashtagName, pageable, gu, dong);

        if (postPage == null) {
            throw new IllegalArgumentException("존재하지 않는 태그입니다.");
        }

        List<PostResultDto> postResultDtos = new ArrayList<>();

        for (Post postList : postPage) {
            UserResponseDto userResponseDto = new UserResponseDto(postList.getUser());
            PostInfoResponseDto postInfoResponseDto = new PostInfoResponseDto(postList);
            LocationResponseDto locationResponseDto = new LocationResponseDto(postList.getGu(), postList.getDong(), postList.getLat(), postList.getLng());

            postResultDtos.add(new PostResultDto(userResponseDto, postInfoResponseDto, locationResponseDto));
        }
        return new PostListResponse("검색 조회 성공", postPage.getTotalPages(), postPage.getTotalElements(), size, postResultDtos);
    }

    //카테고리 - 태그별 post
    public PostListResponse categoryHashtagPostResponseDtos(int size, int page, String hashtagName, String category, String type, String gu, String dong) {
        Pageable pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "createdAt"));
        Page<Post> postPage = type.equals("popular")
                ? postRepository.findAllByCategoryAndHashtagContainingAndGuAndDongOrderByPostViewCountDesc(category, hashtagName, pageable, gu, dong)
                : postRepository.findAllByCategoryAndHashtagContainingAndGuAndDongOrderByCreatedAtDesc(category, hashtagName, pageable, gu, dong);

        if (postPage == null) {
            throw new IllegalArgumentException("존재하지 않는 태그 혹은 존재하지 않는 카테고리 입니다.");
        }
        
        List<PostResultDto> postResultDtos = new ArrayList<>();

        for (Post checkHashtagName : postPage) {
            UserResponseDto userResponseDto = new UserResponseDto(checkHashtagName.getUser());
            PostInfoResponseDto postInfoResponseDto = new PostInfoResponseDto(checkHashtagName);
            LocationResponseDto locationResponseDto = new LocationResponseDto(checkHashtagName.getGu(), checkHashtagName.getDong(), checkHashtagName.getLat(), checkHashtagName.getLng());

            postResultDtos.add(new PostResultDto(userResponseDto, postInfoResponseDto, locationResponseDto));
        }

        return new PostListResponse("검색 조회 성공", postPage.getTotalPages(), postPage.getTotalElements(), size, postResultDtos);
    }

    //포스트에서 해시태그를 추출
    private List<String> getHashTagsFromPosts(List<Post> postList) {
        Map<String, Integer> idFrequencyMap = new HashMap<>();

        for (Post post : postList) {
            if (Objects.isNull(post.getHashtag())) {
                continue;
            }

            String hashTag = post.getHashtag();
            String[] hashTagList = hashTag.split("#");
            for (String tagName : hashTagList) {
                if (!tagName.isEmpty()) {
                    idFrequencyMap.put(tagName, idFrequencyMap.getOrDefault(tagName, 0) + 1);
                }
            }
        }

        List<Map.Entry<String, Integer>> sortedEntries = new ArrayList<>(idFrequencyMap.entrySet());
        sortedEntries.sort((entry1, entry2) -> entry2.getValue().compareTo(entry1.getValue()));

        List<String> rankedIds = new ArrayList<>();

        for (Map.Entry<String, Integer> entry : sortedEntries) {
            rankedIds.add(entry.getKey());
        }

        return rankedIds;
    }
}

일단 가독성이 굉장히 좋아졌다. 내가 써놓고도 이게 뭐였더라 한참을 생각했어야 했는데 말이다. 물론 이보다 더 좋은 코드가 있을 수 있지만 일단 이정도로 만족! 좋은 코드들을 많이 접해봐야겠다.